((CDOTransaction)object1.cdoView()).merge(null, DefaultCDOMerger.PerFeature.ManyValued());
Am 30.01.2013 23:29, schrieb Andrew Whelan:
> Hello,
>
> I'm having a problem trying to figure out the easiest way to solve the following dirty transaction problem.
>
> Lets say we have a CDOObject that a user (user1) has retrieved from the database. We will call this object1.
> Before any changes are being made to object1 a different user (user2) retrieves the same object/record from the
> database (this will be object2, same CDOID and version).
> User1 updates object1 and commits.
>
> User2 updates object2 and tries to commit but we get a CommitException because of the old
> java.util.ConcurrentModificationException.
Yes, that's a classical commit conflict. User2 should have been able to fail early, i.e., before even attempting to
commit, if he had registered a listener with the transaction and reacted to CDOTransactionConflictEvents. These methods
may also help with conflict detection:
org.eclipse.emf.cdo.transaction.CDOTransaction.hasConflict()
org.eclipse.emf.cdo.transaction.CDOTransaction.getConflicts()
There's also infrastructure for resolving conflicts automatically:
org.eclipse.emf.cdo.transaction.CDOTransaction.Options.getConflictResolvers()
org.eclipse.emf.cdo.transaction.CDOTransaction.Options.setConflictResolvers(CDOConflictResolver[])
org.eclipse.emf.cdo.transaction.CDOTransaction.Options.addConflictResolver(CDOConflictResolver)
org.eclipse.emf.cdo.transaction.CDOTransaction.Options.removeConflictResolver(CDOConflictResolver)
In CDO 4.2 I've just fixed a severe bug in CDOMergingConflictResolver and removed its deprecation. These diagrams
explain how it works:
https://bugs.eclipse.org/bugs/attachment.cgi?id=226361
> If I want object2 (user2's object2 data) to overwrite the original commit from user1(object1), what is the easiest way
> to do this for user2?
The easiest way is to wrap the modifying and committing code in a try{} block, a rollback() call in the catch{} block
and loop until no exceptions occur during commit() anymore. Note that the correct form also involves synchronizing the
contents of the try{} block on the local transaction to prevent the background invalidation runner from modifying the
model in the middle of related local changes.
> Mind you these users are working in different sessions
That should be of no relevance.
It doesn't look like I mentioned that the objects are also in different client applications. Two remote clients.
I am using the server configuration with org.eclipse.emf.cdo.server.product.tcp_h2. I have two remote client applications accessing the server.
> and user2 might not easily be able to obtain the transaction from object1.
That should always be possible with (CDOTransaction)(object1.cdoView()) for native models or
(CDOTransaction)(CDOUtil.getCDOObject(object1).cdoView()) for legacy models.
The trouble with this is that object1 and object2 are running in different clients. They are editing the same record and accessing the same server.
I have tried transaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL);
and I created a listener on the transaction, using the example in org.eclipse.emf.cdo.examples.client.offline.nodes.NodeType.Client.createTransaction.
I also tried using an Adaptor like you did in ChangeSubscriptionTest. It works fine if its all is happening in the same client application but I can't seem to get a second client application to recognize that a record has been changed in a different client.
I've tried CDOTransaction.hasConflict(), conflict resolvers don't seem to be any help. Is there anything you can think of that would be different with two separate client applications that are using the same server??
It looks like you are successfully doing this in the Offline example from the webcast. I'm not sure what I could possibly be missing.
I still end up with the ConcurrentModificationException in the second client because I can't detect that it has been changed (or has conflices) until the server throws back the ConcurrentModificationException.
I would even settle with being able to overwrite the one from the first client with the second (it would just be another revision), but the transaction is hosed because the CDOObject represents a historical revision at that point. I tried looping until it let me as you suggested above but it never gets cleared up because its a historical revision.
I'm happy to hear any suggestions that would take me out of this pickle.
Thanks for listing!
> Thanks for your repeated patience. If its any consolation, it looks like we are going to use CDO for this project,
> long term.
Great! It would be awesome if you could take the time to write up a nice review at Ohloh:
https://www.ohloh.net/p/cdo/reviews/new
Cheers
/Eike
----
http://www.esc-net.de
http://thegordian.blogspot.com
http://twitter.com/eikestepper
>
> Thanks
> -Andrew
> PS:
> ((CDOTransaction)object1.cdoView()).merge(null, DefaultCDOMerger.PerFeature.ManyValued());
> because merging of dirty transactions are not yet supported. It was something to try.
See https://bugs.eclipse.org/bugs/show_bug.cgi?id=396804#c2 on why merging is different from conflict resolution.
IConnector connector = Net4jUtil.getConnector(IPluginContainer.INSTANCE, "tcp", server); CDONet4jSessionConfiguration config = CDONet4jUtil.createNet4jSessionConfiguration(); config.setPassiveUpdateEnabled(true); config.setConnector(connector); config.setRepositoryName(repository); CDONet4jSession session = config.openNet4jSession();
sessionTransaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); TransactionListener tl = new TransactionListener(); sessionTransaction.addListener(tl);
public void notifyEvent(IEvent event) { if (event instanceof CDOViewTargetChangedEvent) { System.out.println("transaction changed"); hasChanged = true; } }