Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 312535 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/emf/cdo/transaction/CDOCommitContext.java (+5 lines)
Lines 56-59 Link Here
56
   * Returns a map of the {@link CDORevisionDelta revision deltas} that are to be committed with this commit context.
56
   * Returns a map of the {@link CDORevisionDelta revision deltas} that are to be committed with this commit context.
57
   */
57
   */
58
  public Map<CDOID, CDORevisionDelta> getRevisionDeltas();
58
  public Map<CDOID, CDORevisionDelta> getRevisionDeltas();
59
60
  /**
61
   * @since 4.0
62
   */
63
  public boolean isPartialCommit();
59
}
64
}
(-)src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java (+16 lines)
Lines 597-600 Link Here
597
  {
597
  {
598
    delegate.setCommitComment(comment);
598
    delegate.setCommitComment(comment);
599
  }
599
  }
600
601
  /**
602
   * @since 4.0
603
   */
604
  public void setCommittables(Set<EObject> committables)
605
  {
606
    delegate.setCommittables(committables);
607
  }
608
609
  /**
610
   * @since 4.0
611
   */
612
  public Set<EObject> getCommittables()
613
  {
614
    return delegate.getCommittables();
615
  }
600
}
616
}
(-)src/org/eclipse/emf/cdo/transaction/CDOUserTransaction.java (+14 lines)
Lines 14-21 Link Here
14
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
14
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
15
import org.eclipse.emf.cdo.util.CommitException;
15
import org.eclipse.emf.cdo.util.CommitException;
16
16
17
import org.eclipse.emf.ecore.EObject;
18
17
import org.eclipse.core.runtime.IProgressMonitor;
19
import org.eclipse.core.runtime.IProgressMonitor;
18
20
21
import java.util.Set;
22
19
/**
23
/**
20
 * Only deal with transaction process.
24
 * Only deal with transaction process.
21
 * 
25
 * 
Lines 50-53 Link Here
50
   * @since 3.0
54
   * @since 3.0
51
   */
55
   */
52
  public CDOUserSavepoint getLastSavepoint();
56
  public CDOUserSavepoint getLastSavepoint();
57
58
  /**
59
   * @since 4.0
60
   */
61
  public void setCommittables(Set<EObject> committables);
62
63
  /**
64
   * @since 4.0
65
   */
66
  public Set<EObject> getCommittables();
53
}
67
}
(-)src/org/eclipse/emf/cdo/util/CommitIntegrityCheck.java (+422 lines)
Added Link Here
1
/**
2
 * Copyright (c) 2004 - 2010 Eike Stepper (Berlin, Germany) and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *    Caspar De Groot - initial API and implementation
10
 */
11
package org.eclipse.emf.cdo.util;
12
13
import org.eclipse.emf.cdo.CDOObject;
14
import org.eclipse.emf.cdo.common.id.CDOID;
15
import org.eclipse.emf.cdo.common.id.CDOID.Type;
16
import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
17
import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
18
import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
19
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
20
import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
21
import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
22
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
23
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDeltaUtil;
24
import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
25
import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
26
import org.eclipse.emf.cdo.eresource.CDOResource;
27
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
28
29
import org.eclipse.net4j.util.CheckUtil;
30
31
import org.eclipse.emf.common.util.EList;
32
import org.eclipse.emf.ecore.EObject;
33
import org.eclipse.emf.ecore.EReference;
34
import org.eclipse.emf.ecore.EStructuralFeature;
35
import org.eclipse.emf.ecore.InternalEObject.EStore;
36
import org.eclipse.emf.spi.cdo.InternalCDOObject;
37
import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
38
import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext;
39
40
import java.util.HashSet;
41
import java.util.Set;
42
43
/**
44
 * @author Caspar De Groot
45
 * @since 4.0
46
 */
47
public class CommitIntegrityCheck
48
{
49
  private InternalCDOTransaction transaction;
50
51
  private Style style;
52
53
  private Set<CDOID> newIDs, dirtyIDs, detachedIDs;
54
55
  private Set<CDOObject> missingObjects = new HashSet<CDOObject>();
56
57
  private StringBuilder exceptionMessage = new StringBuilder();
58
59
  public CommitIntegrityCheck(InternalCDOCommitContext commitContext)
60
  {
61
    this(commitContext, Style.EXCEPTION_FAST);
62
  }
63
64
  public CommitIntegrityCheck(InternalCDOCommitContext commitContext, Style style)
65
  {
66
    transaction = commitContext.getTransaction();
67
68
    CheckUtil.checkNull(style, "style should not be null");
69
    this.style = style;
70
71
    newIDs = commitContext.getNewObjects().keySet();
72
    dirtyIDs = commitContext.getDirtyObjects().keySet();
73
    detachedIDs = commitContext.getDetachedObjects().keySet();
74
  }
75
76
  public void check() throws CommitIntegrityException
77
  {
78
    // For new objects: ensure that their container is included,
79
    // as well as the targets of the new object's bidi references
80
    for (CDOID newID : newIDs)
81
    {
82
      CDOObject newObject = transaction.getObject(newID);
83
      checkContainerIncluded(newObject, "new");
84
      checkCurrentBidiRefTargetsIncluded(newObject, "new");
85
    }
86
87
    // For detached objects: ensure that their former container is included,
88
    // as well as the targets of the detached object's bidi references
89
    for (CDOID detachedID : detachedIDs)
90
    {
91
      CDOObject detachedObject = transaction.getObject(detachedID);
92
      checkFormerContainerIncluded(detachedObject);
93
      checkFormerBidiRefTargetsIncluded(detachedObject, "detached");
94
    }
95
96
    // For dirty objects: if any of the deltas for the object, affect containment (i.e. object was moved)
97
    // or a bi-di reference, ensure that for containment, both the old and new containers are included,
98
    // (or that the child is included if we are considering the dirty parent),
99
    // and that for a bi-di reference, the object holding the other end of the bi-di is included, as
100
    // well as possibly the *former* object holding the other end.
101
    //
102
    for (CDOID dirtyID : dirtyIDs)
103
    {
104
      CDOObject dirtyObject = transaction.getObject(dirtyID);
105
      analyzeRevisionDelta((InternalCDOObject)dirtyObject);
106
    }
107
108
    if (!missingObjects.isEmpty() && style == Style.EXCEPTION)
109
    {
110
      throw createException();
111
    }
112
  }
113
114
  public Set<? extends EObject> getMissingObjects()
115
  {
116
    return missingObjects;
117
  }
118
119
  private CDOID getContainerOrResourceID(InternalCDORevision revision)
120
  {
121
    CDOID containerOrResourceID = null;
122
    Object idOrObject = revision.getContainerID();
123
    if (idOrObject != null)
124
    {
125
      containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
126
    }
127
128
    if (containerOrResourceID == null || containerOrResourceID.getType() == Type.NULL)
129
    {
130
      idOrObject = revision.getResourceID();
131
      if (idOrObject != null)
132
      {
133
        containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
134
      }
135
    }
136
137
    return containerOrResourceID;
138
  }
139
140
  private void analyzeRevisionDelta(InternalCDOObject dirtyObject) throws CommitIntegrityException
141
  {
142
    // Getting the deltas from the TX is not a good idea...
143
    // We better recompute a fresh delta:
144
    //
145
    InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
146
    CheckUtil.checkNull(cleanRev, "Could not obtain clean revision for dirty object " + dirtyObject);
147
    InternalCDORevision dirtyRev = dirtyObject.cdoRevision();
148
    CDORevisionDelta rDelta = CDORevisionDeltaUtil.create(cleanRev, dirtyRev);
149
150
    for (CDOFeatureDelta featureDelta : rDelta.getFeatureDeltas())
151
    {
152
      EStructuralFeature feat = featureDelta.getFeature();
153
      if (feat == CDOContainerFeatureDelta.CONTAINER_FEATURE)
154
      {
155
        // Object is dirty with respect to its container; this means it was moved;
156
        // We must ensure that both the old and new containers are included
157
        checkContainerIncluded(dirtyObject, "moved");
158
        CDOID containerOrResourceID = getContainerOrResourceID(cleanRev);
159
        checkIncluded(containerOrResourceID, "former container (or resource) of moved", dirtyObject);
160
      }
161
      else if (feat instanceof EReference)
162
      {
163
        EReference ref = (EReference)feat;
164
        if (ref.isContainment() || ref.getEOpposite() != null)
165
        {
166
          // Object is dirty with respect to a containment feature
167
          // We must ensure that any children that were added/removed, are also
168
          // included in the commit
169
170
          if (featureDelta instanceof CDOListFeatureDelta)
171
          {
172
            for (CDOFeatureDelta innerFeatDelta : ((CDOListFeatureDelta)featureDelta).getListChanges())
173
            {
174
              checkContainmentDelta(innerFeatDelta, dirtyObject);
175
            }
176
          }
177
          else
178
          {
179
            checkContainmentDelta(featureDelta, dirtyObject);
180
          }
181
        }
182
      }
183
    }
184
  }
185
186
  private void checkContainmentDelta(CDOFeatureDelta featureDelta, CDOObject dirtyObject)
187
      throws CommitIntegrityException
188
  {
189
    if (featureDelta instanceof CDORemoveFeatureDelta)
190
    {
191
      Object idOrObject = ((CDORemoveFeatureDelta)featureDelta).getValue();
192
      CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
193
      checkIncluded(id, "removed child of", dirtyObject);
194
    }
195
    else if (featureDelta instanceof CDOAddFeatureDelta)
196
    {
197
      Object idOrObject = ((CDOAddFeatureDelta)featureDelta).getValue();
198
      CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
199
      if (id.getType() != CDOID.Type.NULL)
200
      {
201
        checkIncluded(id, "added child of", dirtyObject);
202
      }
203
    }
204
    else if (featureDelta instanceof CDOSetFeatureDelta)
205
    {
206
      Object newIDOrObject = ((CDOSetFeatureDelta)featureDelta).getValue();
207
      Object oldIDOrObject = ((CDOSetFeatureDelta)featureDelta).getOldValue();
208
      CDOID oldID = (CDOID)transaction.convertObjectToID(oldIDOrObject);
209
      if (oldIDOrObject != null)
210
      {
211
        if (newIDOrObject == null)
212
        {
213
          // Removal: old child must be included
214
          checkIncluded(oldID, "removed child of", dirtyObject);
215
        }
216
        else
217
        {
218
          // Change: both old and new child must be included
219
          checkIncluded(oldID, "former child of", dirtyObject);
220
          CDOID newID = (CDOID)transaction.convertObjectToID(newIDOrObject);
221
          checkIncluded(newID, "new child of", dirtyObject);
222
        }
223
      }
224
      else
225
      {
226
        // New child, no old child
227
        CDOID newID = (CDOID)transaction.convertObjectToID(newIDOrObject);
228
        checkIncluded(newID, "new child of", dirtyObject);
229
      }
230
    }
231
    else if (featureDelta instanceof CDOClearFeatureDelta)
232
    {
233
      EStructuralFeature feat = ((CDOClearFeatureDelta)featureDelta).getFeature();
234
      InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
235
      int n = cleanRev.size(feat);
236
      for (int i = 0; i < n; i++)
237
      {
238
        Object idOrObject = cleanRev.get(feat, i);
239
        CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
240
        checkIncluded(id, "removed child of", dirtyObject);
241
      }
242
    }
243
    else if (featureDelta instanceof CDOUnsetFeatureDelta)
244
    {
245
      EStructuralFeature feat = ((CDOUnsetFeatureDelta)featureDelta).getFeature();
246
      InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
247
      Object idOrObject = cleanRev.getValue(feat);
248
      CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
249
      checkIncluded(id, "removed child of", dirtyObject);
250
    }
251
    else
252
    {
253
      throw new RuntimeException("Unexpected delta type: " + featureDelta.getClass().getSimpleName());
254
    }
255
  }
256
257
  private void checkIncluded(CDOID id, String msg, CDOObject o) throws CommitIntegrityException
258
  {
259
    if (id.getType() == Type.NULL)
260
    {
261
      throw new IllegalArgumentException("CDOID must not be of type NULL");
262
    }
263
264
    if (!dirtyIDs.contains(id) && !detachedIDs.contains(id) && !newIDs.contains(id))
265
    {
266
      CDOObject missingObject = transaction.getObject(id);
267
      if (missingObject == null)
268
      {
269
        throw new IllegalStateException("Could not find object for CDOID " + id);
270
      }
271
      missingObjects.add(missingObject);
272
273
      if (exceptionMessage.length() > 0)
274
      {
275
        exceptionMessage.append('\n');
276
      }
277
      String m = String.format("The %s object %s needs to be included in the commit but isn't", msg, o);
278
      exceptionMessage.append(m);
279
280
      if (style == Style.EXCEPTION_FAST)
281
      {
282
        throw createException();
283
      }
284
    }
285
  }
286
287
  private CommitIntegrityException createException()
288
  {
289
    return new CommitIntegrityException(exceptionMessage.toString(), missingObjects);
290
  }
291
292
  /**
293
   * Checks whether the container of a given object is included in the commit
294
   * 
295
   * @param msgFrag
296
   * @throws CommitIntegrityException
297
   */
298
  private void checkContainerIncluded(CDOObject object, String msgFrag) throws CommitIntegrityException
299
  {
300
    EObject eContainer = object.eContainer();
301
    if (eContainer == null)
302
    {
303
      // It's a top-level object
304
      CDOResource resource = object.cdoDirectResource();
305
      checkIncluded(resource.cdoID(), "resource of " + msgFrag, object);
306
    }
307
    else
308
    {
309
      CDOObject container = CDOUtil.getCDOObject(eContainer);
310
      checkIncluded(container.cdoID(), "container of " + msgFrag, object);
311
    }
312
  }
313
314
  private void checkCurrentBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
315
  {
316
    for (EReference eRef : referencer.eClass().getEAllReferences())
317
    {
318
      if (eRef.getEOpposite() != null)
319
      {
320
        if (eRef.isMany())
321
        {
322
          EList<?> list = (EList<?>)referencer.eGet(eRef);
323
          for (Object element : list)
324
          {
325
            checkBidiRefTargetIncluded(element, referencer, msgFrag);
326
          }
327
        }
328
        else
329
        {
330
          Object refTarget = referencer.eGet(eRef);
331
          if (refTarget != null)
332
          {
333
            checkBidiRefTargetIncluded(refTarget, referencer, msgFrag);
334
          }
335
        }
336
      }
337
    }
338
  }
339
340
  private void checkFormerBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
341
  {
342
    // The referencer argument should really be a detached object, and so we know
343
    // that we can find the pre-detach revision in tx.getFormerRevisions(). However,
344
    // the object may have already been dirty prior to detachment, so we check the
345
    // clean revisions first.
346
    //
347
    InternalCDORevision cleanRev = transaction.getCleanRevisions().get(referencer);
348
    CheckUtil.checkState(cleanRev, "cleanRev");
349
350
    for (EReference eRef : referencer.eClass().getEAllReferences())
351
    {
352
      if (eRef.getEOpposite() != null)
353
      {
354
        Object value = cleanRev.get(eRef, EStore.NO_INDEX);
355
        if (value != null)
356
        {
357
          if (eRef.isMany())
358
          {
359
            EList<?> list = (EList<?>)value;
360
            for (Object element : list)
361
            {
362
              checkBidiRefTargetIncluded(element, referencer, msgFrag);
363
            }
364
          }
365
          else
366
          {
367
            checkBidiRefTargetIncluded(value, referencer, msgFrag);
368
          }
369
        }
370
      }
371
    }
372
  }
373
374
  private void checkBidiRefTargetIncluded(Object refTarget, CDOObject referencer, String msgFrag)
375
      throws CommitIntegrityException
376
  {
377
    CheckUtil.checkArg(refTarget, "refTarget");
378
    CDOID refTargetID = null;
379
    if (refTarget instanceof EObject)
380
    {
381
      refTargetID = CDOUtil.getCDOObject((EObject)refTarget).cdoID();
382
    }
383
    else if (refTarget instanceof CDOID)
384
    {
385
      refTargetID = (CDOID)refTarget;
386
    }
387
    checkIncluded(refTargetID, "reference target of " + msgFrag, referencer);
388
  }
389
390
  private void checkFormerContainerIncluded(CDOObject detachedObject) throws CommitIntegrityException
391
  {
392
    InternalCDORevision rev = transaction.getCleanRevisions().get(detachedObject);
393
    CheckUtil.checkNull(rev, "Could not obtain clean revision for detached object " + detachedObject);
394
    CDOID id = getContainerOrResourceID(rev);
395
    checkIncluded(id, "former container (or resource) of detached", detachedObject);
396
  }
397
398
  /**
399
   * Designates an exception style for a {@link CommitIntegrityCheck}
400
   * 
401
   * @author Caspar De Groot
402
   */
403
  public enum Style
404
  {
405
    /**
406
     * Throw an exception as soon as this {@link CommitIntegrityCheck} encounters the first problem
407
     */
408
    EXCEPTION_FAST,
409
410
    /**
411
     * Throw an exception when this {@link CommitIntegrityCheck} finishes performing all possible checks, in case any
412
     * problems were found
413
     */
414
    EXCEPTION,
415
416
    /**
417
     * Do not throw an exception. Caller must invoke {@link CommitIntegrityCheck#getMissingObjects()} to find out if the
418
     * check discovered any problems.
419
     */
420
    NO_EXCEPTION
421
  }
422
}
(-)src/org/eclipse/emf/cdo/util/CommitIntegrityException.java (+37 lines)
Added Link Here
1
/**
2
 * Copyright (c) 2004 - 2010 Eike Stepper (Berlin, Germany) and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 * 
8
 * Contributors:
9
 *    Caspar De Groot - initial API and implementation
10
 */
11
package org.eclipse.emf.cdo.util;
12
13
import org.eclipse.emf.ecore.EObject;
14
15
import java.util.Set;
16
17
/**
18
 * @author Caspar De Groot
19
 * @since 4.0
20
 */
21
public class CommitIntegrityException extends CommitException
22
{
23
  private static final long serialVersionUID = 3302652235940254454L;
24
25
  private Set<? extends EObject> missingObjects;
26
27
  public CommitIntegrityException(String msg, Set<? extends EObject> missingObjects)
28
  {
29
    super(msg);
30
    this.missingObjects = missingObjects;
31
  }
32
33
  public Set<? extends EObject> getMissingObjects()
34
  {
35
    return missingObjects;
36
  }
37
}
(-)src/org/eclipse/emf/internal/cdo/CDOStateMachine.java (+2 lines)
Lines 798-803 Link Here
798
    public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
798
    public void execute(InternalCDOObject object, CDOState state, CDOEvent event, Object featureDelta)
799
    {
799
    {
800
      InternalCDOTransaction transaction = object.cdoView().toTransaction();
800
      InternalCDOTransaction transaction = object.cdoView().toTransaction();
801
      InternalCDORevision cleanRevision = object.cdoRevision();
802
      transaction.getCleanRevisions().put(object, cleanRevision);
801
803
802
      // Copy revision
804
      // Copy revision
803
      InternalCDORevision revision = object.cdoRevision().copy();
805
      InternalCDORevision revision = object.cdoRevision().copy();
(-)src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java (-24 / +148 lines)
Lines 69-74 Link Here
69
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
69
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
70
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
70
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
71
import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
71
import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
72
import org.eclipse.emf.cdo.transaction.CDOCommitContext;
72
import org.eclipse.emf.cdo.transaction.CDOConflictResolver;
73
import org.eclipse.emf.cdo.transaction.CDOConflictResolver;
73
import org.eclipse.emf.cdo.transaction.CDOConflictResolver2;
74
import org.eclipse.emf.cdo.transaction.CDOConflictResolver2;
74
import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler;
75
import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler;
Lines 83-88 Link Here
83
import org.eclipse.emf.cdo.util.CDOURIUtil;
84
import org.eclipse.emf.cdo.util.CDOURIUtil;
84
import org.eclipse.emf.cdo.util.CDOUtil;
85
import org.eclipse.emf.cdo.util.CDOUtil;
85
import org.eclipse.emf.cdo.util.CommitException;
86
import org.eclipse.emf.cdo.util.CommitException;
87
import org.eclipse.emf.cdo.util.CommitIntegrityCheck;
86
import org.eclipse.emf.cdo.util.LegacyModeNotEnabledException;
88
import org.eclipse.emf.cdo.util.LegacyModeNotEnabledException;
87
import org.eclipse.emf.cdo.util.ObjectNotFoundException;
89
import org.eclipse.emf.cdo.util.ObjectNotFoundException;
88
90
Lines 191-196 Link Here
191
    }
193
    }
192
  };
194
  };
193
195
196
  private Set<EObject> committables;
197
198
  private Map<InternalCDOObject, InternalCDORevision> cleanRevisions = new HashMap<InternalCDOObject, InternalCDORevision>();
199
194
  public CDOTransactionImpl(CDOBranch branch)
200
  public CDOTransactionImpl(CDOBranch branch)
195
  {
201
  {
196
    super(branch, UNSPECIFIED_DATE);
202
    super(branch, UNSPECIFIED_DATE);
Lines 978-984 Link Here
978
  {
984
  {
979
    checkActive();
985
    checkActive();
980
    rollback(firstSavepoint);
986
    rollback(firstSavepoint);
981
    cleanUp();
987
    cleanUp(null);
982
  }
988
  }
983
989
984
  private void removeObject(CDOID id, CDOObject object)
990
  private void removeObject(CDOID id, CDOObject object)
Lines 1203-1208 Link Here
1203
        formerRevisionKeys.put(object, revKey);
1209
        formerRevisionKeys.put(object, revKey);
1204
      }
1210
      }
1205
1211
1212
      if (!cleanRevisions.containsKey(object))
1213
      {
1214
        cleanRevisions.put(object, object.cdoRevision());
1215
      }
1216
1206
      // Object may have been reattached previously, in which case it must
1217
      // Object may have been reattached previously, in which case it must
1207
      // here be removed from the collection of reattached objects
1218
      // here be removed from the collection of reattached objects
1208
      lastSavepoint.getReattachedObjects().remove(id);
1219
      lastSavepoint.getReattachedObjects().remove(id);
Lines 1551-1569 Link Here
1551
    return newPackages;
1562
    return newPackages;
1552
  }
1563
  }
1553
1564
1554
  private void cleanUp()
1565
  private void cleanUp(CDOCommitContext commitContext)
1555
  {
1566
  {
1556
    lastSavepoint = firstSavepoint;
1567
    if (commitContext == null || !commitContext.isPartialCommit())
1557
    firstSavepoint.clear();
1568
    {
1558
    firstSavepoint.setNextSavepoint(null);
1569
      lastSavepoint = firstSavepoint;
1559
    firstSavepoint.getSharedDetachedObjects().clear();
1570
      firstSavepoint.clear();
1560
1571
      firstSavepoint.setNextSavepoint(null);
1561
    // Bug 283985 (Re-attachment)
1572
      firstSavepoint.getSharedDetachedObjects().clear();
1562
    formerRevisionKeys.clear();
1573
1563
1574
      // Bug 283985 (Re-attachment)
1564
    dirty = false;
1575
      formerRevisionKeys.clear();
1565
    conflict = 0;
1576
1566
    lastTemporaryID.set(0);
1577
      cleanRevisions.clear();
1578
      dirty = false;
1579
      conflict = 0;
1580
      lastTemporaryID.set(0);
1581
    }
1582
    else
1583
    {
1584
      collapseSavepoints(commitContext);
1585
1586
      for (CDOObject object : commitContext.getDetachedObjects().values())
1587
      {
1588
        formerRevisionKeys.remove(object);
1589
      }
1590
    }
1591
1592
    // Reset partial-commit filter
1593
    committables = null;
1594
  }
1595
1596
  private void collapseSavepoints(CDOCommitContext commitContext)
1597
  {
1598
    InternalCDOSavepoint newSavepoint = createSavepoint(null);
1599
    copyUncommitted(lastSavepoint.getAllNewObjects(), commitContext.getNewObjects(), newSavepoint.getNewObjects());
1600
    copyUncommitted(lastSavepoint.getAllDirtyObjects(), commitContext.getDirtyObjects(), newSavepoint.getDirtyObjects());
1601
    copyUncommitted(lastSavepoint.getAllRevisionDeltas(), commitContext.getRevisionDeltas(),
1602
        newSavepoint.getRevisionDeltas());
1603
    copyUncommitted(lastSavepoint.getAllDetachedObjects(), commitContext.getDetachedObjects(),
1604
        newSavepoint.getDetachedObjects());
1605
    lastSavepoint = newSavepoint;
1606
    firstSavepoint = lastSavepoint;
1607
  }
1608
1609
  private <T> void copyUncommitted(Map<CDOID, T> oldSavepointMap, Map<CDOID, T> commitContextMap,
1610
      Map<CDOID, T> newSavepointMap)
1611
  {
1612
    for (Entry<CDOID, T> entry : oldSavepointMap.entrySet())
1613
    {
1614
      if (!commitContextMap.containsKey(entry.getKey()))
1615
      {
1616
        newSavepointMap.put(entry.getKey(), entry.getValue());
1617
      }
1618
    }
1567
  }
1619
  }
1568
1620
1569
  public CDOSavepoint[] exportChanges(OutputStream stream) throws IOException
1621
  public CDOSavepoint[] exportChanges(OutputStream stream) throws IOException
Lines 1925-1930 Link Here
1925
    commitComment = comment;
1977
    commitComment = comment;
1926
  }
1978
  }
1927
1979
1980
  public void setCommittables(Set<EObject> committables)
1981
  {
1982
    this.committables = committables;
1983
  }
1984
1985
  public Set<EObject> getCommittables()
1986
  {
1987
    return committables;
1988
  }
1989
1990
  public Map<InternalCDOObject, InternalCDORevision> getCleanRevisions()
1991
  {
1992
    return cleanRevisions;
1993
  }
1994
1928
  /**
1995
  /**
1929
   * @author Simon McDuff
1996
   * @author Simon McDuff
1930
   */
1997
   */
Lines 1934-1939 Link Here
1934
2001
1935
    private CDOCommitData commitData;
2002
    private CDOCommitData commitData;
1936
2003
2004
    private Map<CDOID, CDOObject> newObjects;
2005
2006
    private Map<CDOID, CDOObject> detachedObjects;
2007
2008
    private Map<CDOID, CDORevisionDelta> revisionDeltas;
2009
2010
    private Map<CDOID, CDOObject> dirtyObjects;
2011
2012
    /**
2013
     * Tracks whether this commit is *actually* partial or not. (Having tx.committables != null does not in itself mean
2014
     * that the commit will be partial, because the committables could cover all dirty/new/detached objects. But this
2015
     * boolean gets set to reflect whether the commit will really commit less than all dirty/new/detached objects.)
2016
     */
2017
    private boolean isPartialCommit;
2018
1937
    public CDOCommitContextImpl(InternalCDOTransaction transaction)
2019
    public CDOCommitContextImpl(InternalCDOTransaction transaction)
1938
    {
2020
    {
1939
      this.transaction = transaction;
2021
      this.transaction = transaction;
Lines 1943-1971 Link Here
1943
    private void calculateCommitData()
2025
    private void calculateCommitData()
1944
    {
2026
    {
1945
      List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
2027
      List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
1946
      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(getNewObjects().size());
2028
      newObjects = filterCommittables(transaction.getNewObjects());
1947
      for (CDOObject newObject : getNewObjects().values())
2029
      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(newObjects.size());
2030
      for (CDOObject newObject : newObjects.values())
1948
      {
2031
      {
1949
        revisions.add(newObject.cdoRevision());
2032
        revisions.add(newObject.cdoRevision());
1950
      }
2033
      }
1951
2034
1952
      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(getRevisionDeltas().size());
2035
      revisionDeltas = filterCommittables(transaction.getRevisionDeltas());
1953
      for (CDORevisionDelta delta : getRevisionDeltas().values())
2036
      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(revisionDeltas.size());
2037
      for (CDORevisionDelta delta : revisionDeltas.values())
1954
      {
2038
      {
1955
        deltas.add(delta);
2039
        deltas.add(delta);
1956
      }
2040
      }
1957
2041
1958
      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(getDetachedObjects().size());
2042
      detachedObjects = filterCommittables(transaction.getDetachedObjects());
1959
      for (CDOID id : getDetachedObjects().keySet())
2043
      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(detachedObjects.size());
2044
      for (CDOID id : detachedObjects.keySet())
1960
      {
2045
      {
1961
        // Add "version-less" key.
2046
        // Add "version-less" key.
1962
        // CDOSessionImpl.reviseRevisions() will call reviseLatest() accordingly.
2047
        // CDOSessionImpl.reviseRevisions() will call reviseLatest() accordingly.
1963
        detached.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
2048
        detached.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
1964
      }
2049
      }
1965
2050
2051
      dirtyObjects = filterCommittables(transaction.getDirtyObjects());
2052
1966
      commitData = new CDOCommitDataImpl(newPackageUnits, revisions, deltas, detached);
2053
      commitData = new CDOCommitDataImpl(newPackageUnits, revisions, deltas, detached);
1967
    }
2054
    }
1968
2055
2056
    private <T> Map<CDOID, T> filterCommittables(Map<CDOID, T> map)
2057
    {
2058
      if (committables == null)
2059
      {
2060
        // No partial commit filter -- nothing to do
2061
        return map;
2062
      }
2063
2064
      Map<CDOID, T> newMap = new HashMap<CDOID, T>();
2065
      for (CDOID id : map.keySet())
2066
      {
2067
        CDOObject o = getObject(id);
2068
        if (committables.contains(o))
2069
        {
2070
          newMap.put(id, map.get(id));
2071
        }
2072
        else
2073
        {
2074
          isPartialCommit = true;
2075
        }
2076
      }
2077
2078
      return newMap;
2079
    }
2080
1969
    public InternalCDOTransaction getTransaction()
2081
    public InternalCDOTransaction getTransaction()
1970
    {
2082
    {
1971
      return transaction;
2083
      return transaction;
Lines 1978-1989 Link Here
1978
2090
1979
    public Map<CDOID, CDOObject> getDirtyObjects()
2091
    public Map<CDOID, CDOObject> getDirtyObjects()
1980
    {
2092
    {
1981
      return transaction.getDirtyObjects();
2093
      return dirtyObjects;
1982
    }
2094
    }
1983
2095
1984
    public Map<CDOID, CDOObject> getNewObjects()
2096
    public Map<CDOID, CDOObject> getNewObjects()
1985
    {
2097
    {
1986
      return transaction.getNewObjects();
2098
      return newObjects;
1987
    }
2099
    }
1988
2100
1989
    public List<CDOPackageUnit> getNewPackageUnits()
2101
    public List<CDOPackageUnit> getNewPackageUnits()
Lines 1993-2004 Link Here
1993
2105
1994
    public Map<CDOID, CDOObject> getDetachedObjects()
2106
    public Map<CDOID, CDOObject> getDetachedObjects()
1995
    {
2107
    {
1996
      return transaction.getDetachedObjects();
2108
      return detachedObjects;
1997
    }
2109
    }
1998
2110
1999
    public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
2111
    public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
2000
    {
2112
    {
2001
      return transaction.getRevisionDeltas();
2113
      return revisionDeltas;
2002
    }
2114
    }
2003
2115
2004
    public void preCommit()
2116
    public void preCommit()
Lines 2046-2051 Link Here
2046
2158
2047
        try
2159
        try
2048
        {
2160
        {
2161
          // TODO (CD) It might be better to always do the checks,
2162
          // instead of only for partial commits
2163
          if (isPartialCommit)
2164
          {
2165
            new CommitIntegrityCheck(this, CommitIntegrityCheck.Style.EXCEPTION_FAST).check();
2166
          }
2167
2049
          preCommit(getNewObjects());
2168
          preCommit(getNewObjects());
2050
          preCommit(getDirtyObjects());
2169
          preCommit(getDirtyObjects());
2051
        }
2170
        }
Lines 2110-2116 Link Here
2110
          getChangeSubscriptionManager().committedTransaction(transaction, this);
2229
          getChangeSubscriptionManager().committedTransaction(transaction, this);
2111
          getAdapterManager().committedTransaction(transaction, this);
2230
          getAdapterManager().committedTransaction(transaction, this);
2112
2231
2113
          cleanUp();
2232
          cleanUp(this);
2114
          Map<CDOID, CDOID> idMappings = result.getIDMappings();
2233
          Map<CDOID, CDOID> idMappings = result.getIDMappings();
2115
          IListener[] listeners = getListeners();
2234
          IListener[] listeners = getListeners();
2116
          if (listeners != null)
2235
          if (listeners != null)
Lines 2180-2185 Link Here
2180
        }
2299
        }
2181
      }
2300
      }
2182
    }
2301
    }
2302
2303
    public boolean isPartialCommit()
2304
    {
2305
      return isPartialCommit;
2306
    }
2183
  }
2307
  }
2184
2308
2185
  /**
2309
  /**
(-)src/org/eclipse/emf/internal/cdo/transaction/CDOXACommitContextImpl.java (+5 lines)
Lines 134-139 Link Here
134
    return delegateCommitContext.getCommitData();
134
    return delegateCommitContext.getCommitData();
135
  }
135
  }
136
136
137
  public boolean isPartialCommit()
138
  {
139
    return delegateCommitContext.isPartialCommit();
140
  }
141
137
  public Object call() throws Exception
142
  public Object call() throws Exception
138
  {
143
  {
139
    state.handle(this, progressMonitor);
144
    state.handle(this, progressMonitor);
(-)src/org/eclipse/emf/internal/cdo/transaction/CDOXATransactionImpl.java (+11 lines)
Lines 34-39 Link Here
34
import org.eclipse.emf.common.notify.Adapter;
34
import org.eclipse.emf.common.notify.Adapter;
35
import org.eclipse.emf.common.notify.Notification;
35
import org.eclipse.emf.common.notify.Notification;
36
import org.eclipse.emf.common.notify.Notifier;
36
import org.eclipse.emf.common.notify.Notifier;
37
import org.eclipse.emf.ecore.EObject;
37
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
38
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
38
import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
39
import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
39
import org.eclipse.emf.spi.cdo.CDOTransactionStrategy;
40
import org.eclipse.emf.spi.cdo.CDOTransactionStrategy;
Lines 435-440 Link Here
435
    }
436
    }
436
  }
437
  }
437
438
439
  public void setCommittables(Set<EObject> committables)
440
  {
441
    throw new UnsupportedOperationException();
442
  }
443
444
  public Set<EObject> getCommittables()
445
  {
446
    throw new UnsupportedOperationException();
447
  }
448
438
  /**
449
  /**
439
   * @author Simon McDuff
450
   * @author Simon McDuff
440
   */
451
   */
(-)src/org/eclipse/emf/spi/cdo/InternalCDOTransaction.java (+6 lines)
Lines 20-25 Link Here
20
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
20
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
21
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
21
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
22
import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
22
import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
23
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
23
import org.eclipse.emf.cdo.transaction.CDOCommitContext;
24
import org.eclipse.emf.cdo.transaction.CDOCommitContext;
24
import org.eclipse.emf.cdo.transaction.CDOTransaction;
25
import org.eclipse.emf.cdo.transaction.CDOTransaction;
25
26
Lines 92-97 Link Here
92
  public Map<InternalCDOObject, CDORevisionKey> getFormerRevisionKeys();
93
  public Map<InternalCDOObject, CDORevisionKey> getFormerRevisionKeys();
93
94
94
  /**
95
  /**
96
   * @since 4.0
97
   */
98
  public Map<InternalCDOObject, InternalCDORevision> getCleanRevisions();
99
100
  /**
95
   * Provides a context for a commit operation.
101
   * Provides a context for a commit operation.
96
   * 
102
   * 
97
   * @author Simon McDuff
103
   * @author Simon McDuff
(-)src/org/eclipse/emf/cdo/common/revision/delta/CDOSetFeatureDelta.java (+10 lines)
Lines 17-23 Link Here
17
 */
17
 */
18
public interface CDOSetFeatureDelta extends CDOFeatureDelta
18
public interface CDOSetFeatureDelta extends CDOFeatureDelta
19
{
19
{
20
  /**
21
   * @since 4.0
22
   */
23
  public static final Object UNSPECIFIED = new Object();
24
20
  public int getIndex();
25
  public int getIndex();
21
26
22
  public Object getValue();
27
  public Object getValue();
28
29
  /**
30
   * @since 4.0
31
   */
32
  public Object getOldValue();
23
}
33
}
(-)src/org/eclipse/emf/cdo/internal/common/revision/delta/CDORevisionDeltaImpl.java (-1 / +1 lines)
Lines 364-370 Link Here
364
          }
364
          }
365
          else
365
          else
366
          {
366
          {
367
            addFeatureDelta(new CDOSetFeatureDeltaImpl(feature, 0, dirtyValue));
367
            addFeatureDelta(new CDOSetFeatureDeltaImpl(feature, 0, dirtyValue, originValue));
368
          }
368
          }
369
        }
369
        }
370
      }
370
      }
(-)src/org/eclipse/emf/cdo/internal/common/revision/delta/CDOSetFeatureDeltaImpl.java (-1 / +31 lines)
Lines 23-28 Link Here
23
import org.eclipse.emf.ecore.EStructuralFeature;
23
import org.eclipse.emf.ecore.EStructuralFeature;
24
24
25
import java.io.IOException;
25
import java.io.IOException;
26
import java.text.MessageFormat;
26
27
27
/**
28
/**
28
 * @author Simon McDuff
29
 * @author Simon McDuff
Lines 30-40 Link Here
30
public class CDOSetFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOSetFeatureDelta,
31
public class CDOSetFeatureDeltaImpl extends CDOSingleValueFeatureDeltaImpl implements CDOSetFeatureDelta,
31
    ListTargetAdding
32
    ListTargetAdding
32
{
33
{
34
  private Object oldValue = CDOSetFeatureDelta.UNSPECIFIED;
35
33
  public CDOSetFeatureDeltaImpl(EStructuralFeature feature, int index, Object value)
36
  public CDOSetFeatureDeltaImpl(EStructuralFeature feature, int index, Object value)
34
  {
37
  {
35
    super(feature, index, value);
38
    super(feature, index, value);
36
  }
39
  }
37
40
41
  public CDOSetFeatureDeltaImpl(EStructuralFeature feature, int index, Object value, Object oldValue)
42
  {
43
    super(feature, index, value);
44
    this.oldValue = oldValue;
45
  }
46
38
  public CDOSetFeatureDeltaImpl(CDODataInput in, EClass eClass) throws IOException
47
  public CDOSetFeatureDeltaImpl(CDODataInput in, EClass eClass) throws IOException
39
  {
48
  {
40
    super(in, eClass);
49
    super(in, eClass);
Lines 47-53 Link Here
47
56
48
  public CDOFeatureDelta copy()
57
  public CDOFeatureDelta copy()
49
  {
58
  {
50
    return new CDOSetFeatureDeltaImpl(getFeature(), getIndex(), getValue());
59
    return new CDOSetFeatureDeltaImpl(getFeature(), getIndex(), getValue(), getOldValue());
51
  }
60
  }
52
61
53
  public void apply(CDORevision revision)
62
  public void apply(CDORevision revision)
Lines 59-62 Link Here
59
  {
68
  {
60
    visitor.visit(this);
69
    visitor.visit(this);
61
  }
70
  }
71
72
  public Object getOldValue()
73
  {
74
    return oldValue;
75
  }
76
77
  @Override
78
  protected String toStringAdditional()
79
  {
80
    String oldValueForMessage;
81
    if (oldValue != CDOSetFeatureDelta.UNSPECIFIED)
82
    {
83
      oldValueForMessage = oldValue == null ? "null" : oldValue.toString();
84
    }
85
    else
86
    {
87
      oldValueForMessage = "UNSPECIFIED"; //$NON-NLS-1$
88
    }
89
90
    return super.toStringAdditional() + MessageFormat.format(", oldValue={0}", oldValueForMessage); //$NON-NLS-1$
91
  }
62
}
92
}
(-)META-INF/MANIFEST.MF (+1 lines)
Lines 15-20 Link Here
15
 org.eclipse.net4j.db.h2;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
15
 org.eclipse.net4j.db.h2;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
16
 org.eclipse.emf.ecore.xmi;bundle-version="[2.4.0,3.0.0)";visibility:=reexport,
16
 org.eclipse.emf.ecore.xmi;bundle-version="[2.4.0,3.0.0)";visibility:=reexport,
17
 org.eclipse.emf.edit;bundle-version="[2.4.0,3.0.0)",
17
 org.eclipse.emf.edit;bundle-version="[2.4.0,3.0.0)",
18
 org.eclipse.emf.transaction;bundle-version="[1.4.0,1.5.0)",
18
 org.eclipse.emf.cdo.common;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
19
 org.eclipse.emf.cdo.common;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
19
 org.eclipse.emf.cdo.common.db;bundle-version="[3.0.0,4.0.0)";visibility:=reexport,
20
 org.eclipse.emf.cdo.common.db;bundle-version="[3.0.0,4.0.0)";visibility:=reexport,
20
 org.eclipse.emf.cdo;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
21
 org.eclipse.emf.cdo;bundle-version="[4.0.0,5.0.0)";visibility:=reexport,
(-)src/org/eclipse/emf/cdo/tests/AllConfigs.java (+1 lines)
Lines 136-141 Link Here
136
    testClasses.add(ChunkingTest.class);
136
    testClasses.add(ChunkingTest.class);
137
    testClasses.add(ChunkingWithMEMTest.class);
137
    testClasses.add(ChunkingWithMEMTest.class);
138
    testClasses.add(PackageRegistryTest.class);
138
    testClasses.add(PackageRegistryTest.class);
139
    testClasses.add(PartialCommitTest.class);
139
    testClasses.add(MetaTest.class);
140
    testClasses.add(MetaTest.class);
140
    testClasses.add(RevisionDeltaTest.class);
141
    testClasses.add(RevisionDeltaTest.class);
141
    testClasses.add(RevisionHolderTest.class);
142
    testClasses.add(RevisionHolderTest.class);
(-)src/org/eclipse/emf/cdo/tests/PartialCommitTest.java (+1257 lines)
Added Link Here
1
package org.eclipse.emf.cdo.tests;
2
3
import org.eclipse.emf.cdo.CDOObject;
4
import org.eclipse.emf.cdo.common.id.CDOID;
5
import org.eclipse.emf.cdo.eresource.CDOResource;
6
import org.eclipse.emf.cdo.session.CDOSession;
7
import org.eclipse.emf.cdo.tests.legacy.model1.Model1Package;
8
import org.eclipse.emf.cdo.tests.legacy.model4.model4Package;
9
import org.eclipse.emf.cdo.tests.model1.Category;
10
import org.eclipse.emf.cdo.tests.model1.Company;
11
import org.eclipse.emf.cdo.tests.model1.Model1Factory;
12
import org.eclipse.emf.cdo.tests.model1.Product1;
13
import org.eclipse.emf.cdo.tests.model1.PurchaseOrder;
14
import org.eclipse.emf.cdo.tests.model1.Supplier;
15
import org.eclipse.emf.cdo.tests.model4.ContainedElementNoOpposite;
16
import org.eclipse.emf.cdo.tests.model4.MultiNonContainedElement;
17
import org.eclipse.emf.cdo.tests.model4.RefMultiNonContained;
18
import org.eclipse.emf.cdo.tests.model4.RefSingleContainedNPL;
19
import org.eclipse.emf.cdo.tests.model4.RefSingleNonContained;
20
import org.eclipse.emf.cdo.tests.model4.SingleNonContainedElement;
21
import org.eclipse.emf.cdo.tests.model4.model4Factory;
22
import org.eclipse.emf.cdo.util.CDOUtil;
23
import org.eclipse.emf.cdo.util.CommitException;
24
import org.eclipse.emf.cdo.util.CommitIntegrityCheck;
25
import org.eclipse.emf.cdo.util.CommitIntegrityCheck.Style;
26
import org.eclipse.emf.cdo.util.CommitIntegrityException;
27
import org.eclipse.emf.cdo.view.CDOView;
28
29
import org.eclipse.emf.ecore.EObject;
30
import org.eclipse.emf.ecore.EReference;
31
import org.eclipse.emf.ecore.util.EcoreUtil;
32
import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
33
import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext;
34
35
import java.util.HashSet;
36
import java.util.Iterator;
37
import java.util.Set;
38
39
public class PartialCommitTest extends AbstractCDOTest
40
{
41
  private static String RESOURCENAME = "/r1";
42
43
  private CDOSession session;
44
45
  private InternalCDOTransaction tx;
46
47
  private CDOResource resource1;
48
49
  /* ---- Model 1 stuff ---- */
50
51
  private Company company1, company2, company3, company99;
52
53
  private PurchaseOrder purchaseOrder;
54
55
  private Supplier supplier1;
56
57
  /* ---- Model 4 stuff ---- */
58
59
  private RefSingleContainedNPL refSingleContained1, refSingleContained2;
60
61
  private ContainedElementNoOpposite singleContainedElement1;
62
63
  private RefSingleNonContained refSingleNonContained1, refSingleNonContained2;
64
65
  private SingleNonContainedElement singleNonContainedElement1, singleNonContainedElement2;
66
67
  private RefMultiNonContained refMultiNonContained1, refMultiNonContained2;
68
69
  private MultiNonContainedElement multiNonContainedElement1, multiNonContainedElement2;
70
71
  @Override
72
  public void setUp() throws Exception
73
  {
74
    super.setUp();
75
    session = openSession();
76
    session.options().setPassiveUpdateEnabled(false);
77
    tx = (InternalCDOTransaction)session.openTransaction();
78
  }
79
80
  @Override
81
  public void tearDown() throws Exception
82
  {
83
    tx.close();
84
    session.close();
85
    super.tearDown();
86
  }
87
88
  public void testNewTopLevelResource() throws CommitException
89
  {
90
    CDOResource topResource1 = tx.createResource("/top1");
91
    tx.commit();
92
93
    topResource1.setName("top1_newname"); // Make dirty but don't include; this causes partial commit
94
    CDOResource topResource2 = tx.createResource("/top2");
95
    tx.setCommittables(createSet(topResource2, tx.getRootResource()));
96
    goodAll();
97
  }
98
99
  public void testNewTopLevelResource_rootResourceNotIncluded() throws CommitException
100
  {
101
    CDOResource topResource1 = tx.createResource("/top1");
102
    tx.commit();
103
104
    topResource1.setName("top1_newname"); // Make dirty but don't include; this causes partial commit
105
    CDOResource topResource2 = tx.createResource("/top2");
106
    tx.setCommittables(createSet(topResource2));
107
    badAll(createSet(tx.getRootResource()));
108
  }
109
110
  public void testNewNestedResource() throws CommitException
111
  {
112
    CDOResource topResource1 = tx.createResource("/top1");
113
    tx.commit();
114
115
    topResource1.setName("top1_newname"); // Make dirty but don't include; this causes partial commit
116
    CDOResource nestedResource = tx.createResource("/folder/nested");
117
    tx.setCommittables(createSet(nestedResource, nestedResource.getFolder(), tx.getRootResource()));
118
    goodAll();
119
  }
120
121
  public void testNewNestedResource_rootResourceNotIncluded() throws CommitException
122
  {
123
    CDOResource topResource1 = tx.createResource("/top1");
124
    tx.commit();
125
126
    topResource1.setName("top1_newname"); // Make dirty but don't include; this causes partial commit
127
    CDOResource nestedResource = tx.createResource("/folder/nested");
128
    tx.setCommittables(createSet(nestedResource, nestedResource.getFolder()));
129
    badAll(createSet(tx.getRootResource()));
130
  }
131
132
  public void testNewNestedResource_resourceFolderNotIncluded() throws CommitException
133
  {
134
    CDOResource topResource1 = tx.createResource("/top1");
135
    tx.commit();
136
137
    topResource1.setName("top1_newname"); // Make dirty but don't include; this causes partial commit
138
    CDOResource nestedResource = tx.createResource("/folder/nested");
139
    tx.setCommittables(createSet(nestedResource, tx.getRootResource()));
140
    badAll(createSet(nestedResource.getFolder()));
141
  }
142
143
  public void testPartialCleanUp_dirtyObjects() throws CommitException
144
  {
145
    simpleModel1Setup();
146
147
    company1.setName("Company1");
148
    company2.setName("Company2");
149
    company3.setName("Company3");
150
151
    tx.setCommittables(createSet(company1));
152
    tx.commit();
153
154
    assertClean(company1, tx);
155
    assertDirty(company2, tx);
156
    assertDirty(company3, tx);
157
    assertTrue(tx.isDirty());
158
159
    tx.setCommittables(createSet(company2));
160
    tx.commit();
161
162
    assertClean(company1, tx);
163
    assertClean(company2, tx);
164
    assertDirty(company3, tx);
165
    assertTrue(tx.isDirty());
166
167
    tx.setCommittables(createSet(company3));
168
    tx.commit();
169
170
    assertClean(company1, tx);
171
    assertClean(company2, tx);
172
    assertClean(company3, tx);
173
    assertFalse(tx.isDirty());
174
  }
175
176
  public void testPartialCleanUp_newObjects() throws CommitException
177
  {
178
    simpleModel1Setup();
179
    Category cat = Model1Factory.eINSTANCE.createCategory();
180
    resource1.getContents().add(cat);
181
    tx.commit();
182
183
    company1.setName("Zzz"); // Make dirty but don't include; so as to force partial commit
184
185
    // Make some new objects; but with different containers
186
    Company company4 = Model1Factory.eINSTANCE.createCompany();
187
    resource1.getContents().add(company4);
188
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
189
    company2.getPurchaseOrders().add(po);
190
    Product1 product = Model1Factory.eINSTANCE.createProduct1();
191
    cat.getProducts().add(product);
192
193
    tx.setCommittables(createSet(company4, resource1));
194
    tx.commit();
195
196
    assertClean(company4, tx);
197
    assertNew(po, tx);
198
    assertNew(product, tx);
199
    assertTrue(tx.isDirty());
200
201
    tx.setCommittables(createSet(po, company2));
202
    tx.commit();
203
204
    assertClean(company4, tx);
205
    assertClean(po, tx);
206
    assertNew(product, tx);
207
    assertTrue(tx.isDirty());
208
209
    tx.setCommittables(createSet(product, cat));
210
    tx.commit();
211
212
    assertClean(company4, tx);
213
    assertClean(po, tx);
214
    assertClean(product, tx);
215
    assertTrue(tx.isDirty());
216
217
    tx.setCommittables(createSet(company1));
218
    tx.commit();
219
    assertFalse(tx.isDirty());
220
  }
221
222
  public void testPartialCleanUp_detachedObjects() throws CommitException
223
  {
224
    simpleModel1Setup();
225
    Category cat = Model1Factory.eINSTANCE.createCategory();
226
    resource1.getContents().add(cat);
227
228
    // Make some new objects; but with different containers
229
    Company company4 = Model1Factory.eINSTANCE.createCompany();
230
    resource1.getContents().add(company4);
231
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
232
    company2.getPurchaseOrders().add(po);
233
    Product1 product = Model1Factory.eINSTANCE.createProduct1();
234
    cat.getProducts().add(product);
235
    tx.commit();
236
237
    company1.setName("Zzz"); // Make dirty but don't include; so as to force partial commit
238
239
    resource1.getContents().remove(company4);
240
    company2.getPurchaseOrders().remove(po);
241
    cat.getProducts().remove(product);
242
243
    assertTrue(tx.getDetachedObjects().containsValue(company4));
244
    assertTrue(tx.getFormerRevisionKeys().containsKey(company4));
245
    assertTrue(tx.getDetachedObjects().containsValue(po));
246
    assertTrue(tx.getFormerRevisionKeys().containsKey(company4));
247
    assertTrue(tx.getDetachedObjects().containsValue(product));
248
    assertTrue(tx.getFormerRevisionKeys().containsKey(company4));
249
    assertTrue(tx.isDirty());
250
251
    tx.setCommittables(createSet(company4, resource1));
252
    tx.commit();
253
254
    assertFalse(tx.getDetachedObjects().containsValue(company4));
255
    assertFalse(tx.getFormerRevisionKeys().containsKey(company4));
256
    assertTrue(tx.getDetachedObjects().containsValue(po));
257
    assertTrue(tx.getFormerRevisionKeys().containsKey(po));
258
    assertTrue(tx.getDetachedObjects().containsValue(product));
259
    assertTrue(tx.getFormerRevisionKeys().containsKey(product));
260
    assertTrue(tx.isDirty());
261
262
    tx.setCommittables(createSet(po, company2));
263
    tx.commit();
264
265
    assertFalse(tx.getDetachedObjects().containsValue(company4));
266
    assertFalse(tx.getFormerRevisionKeys().containsKey(company4));
267
    assertFalse(tx.getDetachedObjects().containsValue(po));
268
    assertFalse(tx.getFormerRevisionKeys().containsKey(po));
269
    assertTrue(tx.getDetachedObjects().containsValue(product));
270
    assertTrue(tx.getFormerRevisionKeys().containsKey(product));
271
    assertTrue(tx.isDirty());
272
273
    tx.setCommittables(createSet(product, cat));
274
    tx.commit();
275
276
    assertFalse(tx.getDetachedObjects().containsValue(company4));
277
    assertFalse(tx.getFormerRevisionKeys().containsKey(company4));
278
    assertFalse(tx.getDetachedObjects().containsValue(po));
279
    assertFalse(tx.getFormerRevisionKeys().containsKey(po));
280
    assertFalse(tx.getDetachedObjects().containsValue(product));
281
    assertFalse(tx.getFormerRevisionKeys().containsKey(product));
282
    assertTrue(tx.isDirty());
283
284
    tx.setCommittables(createSet(company1));
285
    tx.commit();
286
    assertFalse(tx.isDirty());
287
  }
288
289
  public void testDirty() throws CommitException
290
  {
291
    simpleModel1Setup();
292
    supplier1.setName("Supplier");
293
    company1.setName("Company");
294
295
    tx.setCommittables(createSet(supplier1));
296
    tx.commit();
297
298
    assertDirty(company1, tx);
299
    assertEquals(company1.getName(), "Company");
300
301
    assertClean(supplier1, tx);
302
    assertEquals(supplier1.getName(), "Supplier");
303
  }
304
305
  public void testNew() throws CommitException
306
  {
307
    simpleModel1Setup();
308
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
309
    company2.getPurchaseOrders().add(po);
310
311
    // Include both the new object and its container
312
    tx.setCommittables(createSet(company2, po));
313
    goodAll();
314
  }
315
316
  public void testNew_containerOfNewObjectNotIncluded() throws CommitException
317
  {
318
    simpleModel1Setup();
319
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
320
    company2.getPurchaseOrders().add(po);
321
322
    // Include only the new object
323
    tx.setCommittables(createSet(po));
324
    badAll(createSet(company2));
325
  }
326
327
  public void testNew_newObjectNotIncluded() throws CommitException
328
  {
329
    simpleModel1Setup();
330
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
331
    company2.getPurchaseOrders().add(po);
332
333
    // Include only the new object's container
334
    tx.setCommittables(createSet(company2));
335
    badAll(createSet(po));
336
  }
337
338
  public void testDetach() throws CommitException
339
  {
340
    simpleModel1Setup();
341
    EcoreUtil.delete(purchaseOrder);
342
343
    // Include the detached object and its old container
344
    tx.setCommittables(createSet(company1, purchaseOrder));
345
    goodAll();
346
  }
347
348
  public void testDetach_containerOfDetachedObjectNotIncluded() throws CommitException
349
  {
350
    simpleModel1Setup();
351
    EcoreUtil.delete(purchaseOrder);
352
353
    // Include only the detached object
354
    tx.setCommittables(createSet(purchaseOrder));
355
    badAll(createSet(company1));
356
  }
357
358
  public void testDetach_detachedObjectNotIncluded() throws CommitException
359
  {
360
    simpleModel1Setup();
361
    EcoreUtil.delete(purchaseOrder);
362
363
    // Include only the detached object's old container
364
    tx.setCommittables(createSet(company1));
365
    badAll(createSet(purchaseOrder));
366
  }
367
368
  public void testMove() throws CommitException
369
  {
370
    simpleModel1Setup();
371
    company2.getPurchaseOrders().add(purchaseOrder);
372
    supplier1.setName("Supplier");
373
374
    // Include the old and new containers as well as the object that was moved
375
    tx.setCommittables(createSet(purchaseOrder, company1, company2));
376
    goodAll();
377
378
    assertClean(company1, tx);
379
    assertClean(company2, tx);
380
    assertClean(purchaseOrder, tx);
381
    assertDirty(supplier1, tx);
382
383
    assertFalse(company1.getPurchaseOrders().contains(purchaseOrder));
384
    assertTrue(company2.getPurchaseOrders().contains(purchaseOrder));
385
    assertEquals("Supplier", supplier1.getName());
386
    assertSame(company2, purchaseOrder.eContainer());
387
  }
388
389
  public void testMove_newContainerNotIncluded() throws CommitException
390
  {
391
    simpleModel1Setup();
392
    company2.getPurchaseOrders().add(purchaseOrder);
393
394
    // Include only the object that was moved and its old container
395
    tx.setCommittables(createSet(purchaseOrder, company1));
396
    badAll(createSet(company2));
397
  }
398
399
  public void testMove_oldContainerNotIncluded() throws CommitException
400
  {
401
    simpleModel1Setup();
402
    company2.getPurchaseOrders().add(purchaseOrder);
403
404
    // Include only the object that was moved and its new container
405
    tx.setCommittables(createSet(purchaseOrder, company2));
406
    badAll(createSet(company1));
407
  }
408
409
  public void testMove_movedObjectNotIncluded() throws CommitException
410
  {
411
    simpleModel1Setup();
412
    company2.getPurchaseOrders().add(purchaseOrder);
413
414
    // Include only the old and new containers
415
    tx.setCommittables(createSet(company1, company2));
416
    badAll(createSet(purchaseOrder));
417
  }
418
419
  public void testMove_onlyOldContainerIncluded() throws CommitException
420
  {
421
    simpleModel1Setup();
422
    company2.getPurchaseOrders().add(purchaseOrder);
423
424
    // Include only the old container
425
    tx.setCommittables(createSet(company1));
426
    badAll(createSet(company2, purchaseOrder));
427
  }
428
429
  public void testMove_onlyNewContainerIncluded() throws CommitException
430
  {
431
    simpleModel1Setup();
432
    company2.getPurchaseOrders().add(purchaseOrder);
433
434
    // Include only the new container
435
    tx.setCommittables(createSet(company2));
436
    badAll(createSet(company1, purchaseOrder));
437
  }
438
439
  public void testMove_onlyMovedObjectIncluded() throws CommitException
440
  {
441
    simpleModel1Setup();
442
    company2.getPurchaseOrders().add(purchaseOrder);
443
444
    // Include only the moved object
445
    tx.setCommittables(createSet(purchaseOrder));
446
    badAll(createSet(company1, company2));
447
  }
448
449
  public void testDoubleMove() throws CommitException
450
  {
451
    simpleModel1Setup();
452
    company2.getPurchaseOrders().add(purchaseOrder);
453
    company3.getPurchaseOrders().add(purchaseOrder);
454
455
    // Include the old and new containers as well as the object that was moved
456
    // (The point here is that company2 does NOT have to be included.)
457
    tx.setCommittables(createSet(purchaseOrder, company1, company3));
458
    System.out.printf("---> purchaseOrder=%s company1=%s company2=%s company3=%s\n", purchaseOrder, company1, company2,
459
        company3);
460
    goodAll();
461
  }
462
463
  public void test_noCommittablesAfterCommit() throws CommitException
464
  {
465
    simpleModel1Setup();
466
    company1.setName("zzz");
467
    tx.setCommittables(createSet(company1));
468
    tx.commit();
469
470
    assertNull(tx.getCommittables());
471
  }
472
473
  public void testNewSingle() throws CommitException
474
  {
475
    simpleModel4ContainmentSetup();
476
    ContainedElementNoOpposite singleContainedElement = model4Factory.eINSTANCE.createContainedElementNoOpposite();
477
    refSingleContained2.setElement(singleContainedElement);
478
479
    // Include both the new object and its container
480
    tx.setCommittables(createSet(refSingleContained2, singleContainedElement));
481
    goodAll();
482
  }
483
484
  public void testNewSingle_containerOfNewObjectNotIncluded() throws CommitException
485
  {
486
    simpleModel4ContainmentSetup();
487
    ContainedElementNoOpposite singleContainedElement = model4Factory.eINSTANCE.createContainedElementNoOpposite();
488
    refSingleContained2.setElement(singleContainedElement);
489
490
    // Include only the new object
491
    tx.setCommittables(createSet(singleContainedElement));
492
    badAll(createSet(refSingleContained2));
493
  }
494
495
  public void testNewSingle_newObjectNotIncluded() throws CommitException
496
  {
497
    simpleModel4ContainmentSetup();
498
    ContainedElementNoOpposite singleContainedElement = model4Factory.eINSTANCE.createContainedElementNoOpposite();
499
    refSingleContained2.setElement(singleContainedElement);
500
501
    // Include only the new object's container
502
    tx.setCommittables(createSet(refSingleContained2));
503
    badAll(createSet(singleContainedElement));
504
  }
505
506
  public void testDetachSingleRef() throws CommitException
507
  {
508
    simpleModel4ContainmentSetup();
509
    refSingleContained1.setElement(null);
510
511
    // Include the detached object and its old container
512
    tx.setCommittables(createSet(refSingleContained1, singleContainedElement1));
513
    goodAll();
514
  }
515
516
  public void testDetachSingleRef_containerOfDetachedObjectNotIncluded() throws CommitException
517
  {
518
    simpleModel4ContainmentSetup();
519
    refSingleContained1.setElement(null);
520
521
    // Include only the detached object
522
    tx.setCommittables(createSet(singleContainedElement1));
523
    badAll(createSet(refSingleContained1));
524
  }
525
526
  public void testDetachSingleRef_detachedObjectNotIncluded() throws CommitException
527
  {
528
    simpleModel4ContainmentSetup();
529
    refSingleContained1.setElement(null);
530
531
    // Include only the detached object's old container
532
    tx.setCommittables(createSet(refSingleContained1));
533
    badAll(createSet(singleContainedElement1));
534
  }
535
536
  public void testMoveSingleRef() throws CommitException
537
  {
538
    simpleModel4ContainmentSetup();
539
    refSingleContained2.setElement(singleContainedElement1);
540
541
    // Include the old and new containers as well as the object that was moved
542
    tx.setCommittables(createSet(refSingleContained1, refSingleContained2, singleContainedElement1));
543
    goodAll();
544
  }
545
546
  public void testMoveSingleRef_newContainerNotIncluded() throws CommitException
547
  {
548
    simpleModel4ContainmentSetup();
549
    refSingleContained2.setElement(singleContainedElement1);
550
551
    // Include only the object that was moved and its old container
552
    tx.setCommittables(createSet(refSingleContained1, singleContainedElement1));
553
    badAll(createSet(refSingleContained2));
554
  }
555
556
  public void testMoveSingleRef_oldContainerNotIncluded() throws CommitException
557
  {
558
    simpleModel4ContainmentSetup();
559
    refSingleContained2.setElement(singleContainedElement1);
560
561
    // Include only the object that was moved and its new container
562
    tx.setCommittables(createSet(refSingleContained2, singleContainedElement1));
563
    badAll(createSet(refSingleContained1));
564
  }
565
566
  public void testMoveSingleRef_movedObjectNotIncluded() throws CommitException
567
  {
568
    simpleModel4ContainmentSetup();
569
    refSingleContained2.setElement(singleContainedElement1);
570
571
    // Include only the old and new containers
572
    tx.setCommittables(createSet(refSingleContained1, refSingleContained2));
573
    badAll(createSet(singleContainedElement1));
574
  }
575
576
  public void testMoveSingleRef_onlyOldContainerIncluded() throws CommitException
577
  {
578
    simpleModel4ContainmentSetup();
579
    refSingleContained2.setElement(singleContainedElement1);
580
581
    // Include only the old container
582
    tx.setCommittables(createSet(refSingleContained1));
583
    badAll(createSet(singleContainedElement1, refSingleContained2));
584
  }
585
586
  public void testMoveSingleRef_onlyNewContainerIncluded() throws CommitException
587
  {
588
    simpleModel4ContainmentSetup();
589
    refSingleContained2.setElement(singleContainedElement1);
590
591
    // Include only the new container
592
    tx.setCommittables(createSet(refSingleContained2));
593
    badAll(createSet(singleContainedElement1, refSingleContained1));
594
  }
595
596
  public void testMoveSingleRef_onlyMovedObjectIncluded() throws CommitException
597
  {
598
    simpleModel4ContainmentSetup();
599
    refSingleContained2.setElement(singleContainedElement1);
600
601
    // Include only the moved object
602
    tx.setCommittables(createSet(singleContainedElement1));
603
    badAll(createSet(refSingleContained1, refSingleContained2));
604
  }
605
606
  public void testNewTopLevel() throws CommitException
607
  {
608
    simpleModel1Setup();
609
    Company company = Model1Factory.eINSTANCE.createCompany();
610
    resource1.getContents().add(company);
611
612
    // Include both the resource and the new object
613
    tx.setCommittables(createSet(resource1, company));
614
    goodAll();
615
  }
616
617
  public void testNewTopLevel_newObjectNotIncluded() throws CommitException
618
  {
619
    simpleModel1Setup();
620
    Company company = Model1Factory.eINSTANCE.createCompany();
621
    resource1.getContents().add(company);
622
623
    // Include only the resource
624
    tx.setCommittables(createSet(resource1));
625
    badAll(createSet(company));
626
  }
627
628
  public void testNewTopLevel_resourceNotIncluded() throws CommitException
629
  {
630
    simpleModel1Setup();
631
    Company company = Model1Factory.eINSTANCE.createCompany();
632
    resource1.getContents().add(company);
633
634
    // Include only the new object
635
    tx.setCommittables(createSet(company));
636
    badAll(createSet(resource1));
637
  }
638
639
  public void _testNewTopLevel_resourceNotIncluded() throws CommitException
640
  {
641
    simpleModel1Setup();
642
643
    CDOID companyID = null;
644
    {
645
      Company company = Model1Factory.eINSTANCE.createCompany();
646
      resource1.getContents().add(company);
647
648
      // Include only the new object
649
      tx.setCommittables(createSet(company));
650
      tx.commit();
651
652
      companyID = CDOUtil.getCDOObject(company).cdoID();
653
    }
654
655
    System.out.println("---> companyID = " + companyID);
656
    System.out.println("---> " + CDOUtil.getCDOObject(resource1).cdoState());
657
658
    {
659
      CDOSession session2 = openSession();
660
      CDOView view = session2.openView();
661
      CDOResource resource = view.getResource(resource1.getPath());
662
663
      // We want to know if the new company that was committed, is an element
664
      // in the getContents() collection of the Resource. We cannot just call
665
      // getContents().contains(), because of the odd implementation of
666
      // CDOResourceImpl.contains: it actually asks the element, rather than
667
      // checking its own collection. So, we have to do this the hard way:
668
      //
669
      boolean found = false;
670
      Iterator<EObject> iter = resource.getContents().iterator();
671
      while (!found && iter.hasNext())
672
      {
673
        CDOObject o = CDOUtil.getCDOObject(iter.next());
674
        if (o.cdoID().equals(companyID))
675
        {
676
          found = true;
677
        }
678
      }
679
      assertTrue(found);
680
681
      view.close();
682
      session2.close();
683
    }
684
  }
685
686
  // -------- Tests concerning bi-di references ----------
687
  //
688
  // Cases to test:
689
  // Bi-di refs are analogous to containment, the only difference being that
690
  // bi-di refs are symmetrical, whereas containment/container is not.
691
  //
692
  // So:
693
  //
694
  // For DIRTY objects, the cases are:
695
  // 1. Setting a bidi ref to null where it was previously non-null
696
  // Must check that object owning opposite feature is included
697
  // 2. Setting a bidi ref to non-null where it was previously null
698
  // Must check that object owning opposite feature is included
699
  // 3. Changing a bidi ref from one non-null value to another
700
  // Must check that both the object owning the NEW opposite feature,
701
  // as well as the OLD one, are included
702
  //
703
  // For NEW objects, the
704
  // If the detached object had any non-null bidi refs, we must check
705
  // whether the bidi target is included.
706
  //
707
  // For DETACHED objects:
708
  // If the detached object had any non-null bidi refs, we must check
709
  // whether the bidi target is included.
710
711
  public void testDirtySingleBidiNew() throws CommitException
712
  {
713
    simpleModel4SingleBidiSetup();
714
    singleNonContainedElement2.setParent(refSingleNonContained2);
715
716
    tx.setCommittables(createSet(singleNonContainedElement2, refSingleNonContained2));
717
    goodAll();
718
  }
719
720
  public void testDirtySingleBidiNew_newtargetNotIncluded() throws CommitException
721
  {
722
    simpleModel4SingleBidiSetup();
723
    singleNonContainedElement2.setParent(refSingleNonContained2);
724
725
    tx.setCommittables(createSet(singleNonContainedElement2));
726
    badAll(createSet(refSingleNonContained2));
727
  }
728
729
  public void testDirtySingleBidiChanged() throws CommitException
730
  {
731
    simpleModel4SingleBidiSetup();
732
    // We "reparent" the singleNonContainedElement1
733
    singleNonContainedElement1.setParent(refSingleNonContained2);
734
735
    tx.setCommittables(createSet(singleNonContainedElement1, refSingleNonContained1, refSingleNonContained2));
736
    goodAll();
737
  }
738
739
  public void testDirtySingleBidiChanged_newTargetNotIncluded() throws CommitException
740
  {
741
    simpleModel4SingleBidiSetup();
742
    // We "reparent" the singleNonContainedElement1
743
    singleNonContainedElement1.setParent(refSingleNonContained2);
744
745
    tx.setCommittables(createSet(singleNonContainedElement1, refSingleNonContained1));
746
    badAll(createSet(refSingleNonContained2));
747
  }
748
749
  public void testDirtySingleBidiChanged_oldTargetNotIncluded() throws CommitException
750
  {
751
    simpleModel4SingleBidiSetup();
752
    // We "reparent" the singleNonContainedElement1
753
    singleNonContainedElement1.setParent(refSingleNonContained2);
754
755
    tx.setCommittables(createSet(singleNonContainedElement1, refSingleNonContained2));
756
    badAll(createSet(refSingleNonContained1));
757
  }
758
759
  public void testDirtySingleBidiRemoved() throws CommitException
760
  {
761
    simpleModel4SingleBidiSetup();
762
    singleNonContainedElement1.setParent(null);
763
764
    tx.setCommittables(createSet(singleNonContainedElement1, refSingleNonContained1));
765
    goodAll();
766
  }
767
768
  public void testDirtySingleBidiRemoved_oldTargetNotIncluded() throws CommitException
769
  {
770
    simpleModel4SingleBidiSetup();
771
    singleNonContainedElement1.setParent(null);
772
773
    tx.setCommittables(createSet(singleNonContainedElement1));
774
    badAll(createSet(refSingleNonContained1));
775
  }
776
777
  public void testSingleBidiOnNewObject() throws CommitException
778
  {
779
    simpleModel4SingleBidiSetup();
780
    SingleNonContainedElement newNonContainedElement = model4Factory.eINSTANCE.createSingleNonContainedElement();
781
    resource1.getContents().add(newNonContainedElement);
782
    newNonContainedElement.setParent(refSingleNonContained2);
783
784
    tx.setCommittables(createSet(newNonContainedElement, resource1, refSingleNonContained2));
785
    goodAll();
786
  }
787
788
  public void testSingleBidiOnNewObject_targetNotIncluded() throws CommitException
789
  {
790
    simpleModel4SingleBidiSetup();
791
    SingleNonContainedElement newNonContainedElement = model4Factory.eINSTANCE.createSingleNonContainedElement();
792
    resource1.getContents().add(newNonContainedElement);
793
    newNonContainedElement.setParent(refSingleNonContained2);
794
795
    tx.setCommittables(createSet(newNonContainedElement, resource1));
796
    badAll(createSet(refSingleNonContained2));
797
  }
798
799
  public void testSingleBidiOnRemovedObject() throws CommitException
800
  {
801
    simpleModel4SingleBidiSetup();
802
    EcoreUtil.delete(singleNonContainedElement1);
803
804
    tx.setCommittables(createSet(singleNonContainedElement1, resource1, refSingleNonContained1));
805
    goodAll();
806
  }
807
808
  public void testSingleBidiOnRemovedObject_targetNotIncluded() throws CommitException
809
  {
810
    simpleModel4SingleBidiSetup();
811
    EcoreUtil.delete(singleNonContainedElement1);
812
813
    tx.setCommittables(createSet(singleNonContainedElement1, resource1));
814
    badAll(createSet(refSingleNonContained1));
815
  }
816
817
  public void testDirtyMultiBidiNew() throws CommitException
818
  {
819
    simpleModel4MultiBidiSetup();
820
    multiNonContainedElement2.setParent(refMultiNonContained2);
821
822
    tx.setCommittables(createSet(multiNonContainedElement2, refMultiNonContained2));
823
    goodAll();
824
  }
825
826
  public void testDirtyMultiBidiNew_newtargetNotIncluded() throws CommitException
827
  {
828
    simpleModel4MultiBidiSetup();
829
    multiNonContainedElement2.setParent(refMultiNonContained2);
830
831
    tx.setCommittables(createSet(multiNonContainedElement2));
832
    badAll(createSet(refMultiNonContained2));
833
  }
834
835
  public void testDirtyMultiBidiChanged() throws CommitException
836
  {
837
    simpleModel4MultiBidiSetup();
838
    // We "reparent" the multiNonContainedElement1
839
    multiNonContainedElement1.setParent(refMultiNonContained2);
840
841
    tx.setCommittables(createSet(multiNonContainedElement1, refMultiNonContained1, refMultiNonContained2));
842
    goodAll();
843
  }
844
845
  public void testDirtyMultiBidiChanged_newTargetNotIncluded() throws CommitException
846
  {
847
    simpleModel4MultiBidiSetup();
848
    // We "reparent" the multiNonContainedElement1
849
    multiNonContainedElement1.setParent(refMultiNonContained2);
850
851
    tx.setCommittables(createSet(multiNonContainedElement1, refMultiNonContained1));
852
    badAll(createSet(refMultiNonContained2));
853
  }
854
855
  public void testDirtyMultiBidiChanged_oldTargetNotIncluded() throws CommitException
856
  {
857
    simpleModel4MultiBidiSetup();
858
    // We "reparent" the multiNonContainedElement1
859
    multiNonContainedElement1.setParent(refMultiNonContained2);
860
861
    tx.setCommittables(createSet(multiNonContainedElement1, refMultiNonContained2));
862
    badAll(createSet(refMultiNonContained1));
863
  }
864
865
  public void testDirtyMultiBidiRemoved() throws CommitException
866
  {
867
    simpleModel4MultiBidiSetup();
868
    multiNonContainedElement1.setParent(null);
869
870
    tx.setCommittables(createSet(multiNonContainedElement1, refMultiNonContained1));
871
    goodAll();
872
  }
873
874
  public void testDirtyMultiBidiRemoved_oldTargetNotIncluded() throws CommitException
875
  {
876
    simpleModel4MultiBidiSetup();
877
    multiNonContainedElement1.setParent(null);
878
879
    tx.setCommittables(createSet(multiNonContainedElement1));
880
    badAll(createSet(refMultiNonContained1));
881
  }
882
883
  public void testMultiBidiOnNewObject() throws CommitException
884
  {
885
    simpleModel4MultiBidiSetup();
886
    MultiNonContainedElement newNonContainedElement = model4Factory.eINSTANCE.createMultiNonContainedElement();
887
    resource1.getContents().add(newNonContainedElement);
888
    newNonContainedElement.setParent(refMultiNonContained2);
889
890
    tx.setCommittables(createSet(newNonContainedElement, resource1, refMultiNonContained2));
891
    goodAll();
892
  }
893
894
  public void testMultiBidiOnNewObject_targetNotIncluded() throws CommitException
895
  {
896
    simpleModel4MultiBidiSetup();
897
    MultiNonContainedElement newNonContainedElement = model4Factory.eINSTANCE.createMultiNonContainedElement();
898
    resource1.getContents().add(newNonContainedElement);
899
    newNonContainedElement.setParent(refMultiNonContained2);
900
901
    tx.setCommittables(createSet(newNonContainedElement, resource1));
902
    badAll(createSet(refMultiNonContained2));
903
  }
904
905
  public void testMultiBidiOnRemovedObject() throws CommitException
906
  {
907
    simpleModel4MultiBidiSetup();
908
    EcoreUtil.delete(multiNonContainedElement1);
909
910
    tx.setCommittables(createSet(multiNonContainedElement1, resource1, refMultiNonContained1));
911
    goodAll();
912
  }
913
914
  public void testMultiBidiOnRemovedObject_targetNotIncluded() throws CommitException
915
  {
916
    simpleModel4MultiBidiSetup();
917
    EcoreUtil.delete(multiNonContainedElement1);
918
919
    tx.setCommittables(createSet(multiNonContainedElement1, resource1));
920
    badAll(createSet(refMultiNonContained1));
921
  }
922
923
  public void testCheckWithoutCommit_exceptionFast() throws CommitException
924
  {
925
    simpleModel1Setup();
926
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
927
    company2.getPurchaseOrders().add(po);
928
929
    // Include only the new object
930
    tx.setCommittables(createSet(po));
931
    InternalCDOCommitContext ctx = tx.createCommitContext();
932
    try
933
    {
934
      new CommitIntegrityCheck(ctx, CommitIntegrityCheck.Style.EXCEPTION_FAST).check();
935
    }
936
    catch (CommitIntegrityException e)
937
    {
938
      // Good
939
    }
940
  }
941
942
  public void testCheckWithoutCommit_exception() throws CommitException
943
  {
944
    simpleModel1Setup();
945
    PurchaseOrder po = Model1Factory.eINSTANCE.createPurchaseOrder();
946
    company2.getPurchaseOrders().add(po);
947
948
    // Include only the new object
949
    tx.setCommittables(createSet(po));
950
    InternalCDOCommitContext ctx = tx.createCommitContext();
951
    try
952
    {
953
      new CommitIntegrityCheck(ctx, CommitIntegrityCheck.Style.EXCEPTION_FAST).check();
954
    }
955
    catch (CommitIntegrityException e)
956
    {
957
      // Good
958
    }
959
  }
960
961
  public void testCommittablesContainUncommittableObjects()
962
  {
963
    // Idea here is to include some objects in the committables, that exist, but
964
    // are neither dirty nor detached nor new.
965
    //
966
    // Actually, one could wonder what the desirable behavior is in this case.
967
    // Should there be a failure? Or should the "committables" be considered more like
968
    // a filter; i.e. it's ok for the filter to cover more than what can actually be committed.
969
    // Hmm... *ponder* *ponder*.
970
    //
971
  }
972
973
  /**
974
   * Test the commit integrity, assuming that it is good, using all possible checking styles.
975
   */
976
  private void goodAll() throws CommitException
977
  {
978
    good(Style.NO_EXCEPTION);
979
    good(Style.EXCEPTION_FAST);
980
    good(Style.EXCEPTION);
981
    good(null);
982
  }
983
984
  /**
985
   * Test the commit integrity, assuming that it is good.
986
   * 
987
   * @param style
988
   *          - the checking style to be used; if null, just commit. In that case, the commit logic will choose the
989
   *          checking style.
990
   */
991
  private void good(Style style) throws CommitException
992
  {
993
    if (style != null)
994
    {
995
      InternalCDOCommitContext ctx = tx.createCommitContext();
996
      CommitIntegrityCheck check = new CommitIntegrityCheck(ctx, style);
997
998
      try
999
      {
1000
        check.check();
1001
        assertTrue("check.getMissingObjects() should have been empty", check.getMissingObjects().isEmpty());
1002
      }
1003
      catch (CommitIntegrityException e)
1004
      {
1005
        fail("Should not have thrown " + CommitIntegrityException.class.getName());
1006
      }
1007
    }
1008
    else
1009
    {
1010
      try
1011
      {
1012
        // We always make company99 dirty if it's present
1013
        // (This is just a control object to verify that some stuff does NOT get
1014
        // committed.)
1015
        if (company99 != null)
1016
        {
1017
          company99.setName("000");
1018
        }
1019
1020
        tx.commit();
1021
1022
        // And we verify that it didn't get included in the commit
1023
        if (company99 != null)
1024
        {
1025
          assertDirty(company99, tx);
1026
          assertTrue("Transaction should still have been dirty", tx.isDirty());
1027
        }
1028
      }
1029
      catch (CommitException e)
1030
      {
1031
        Throwable cause = e.getCause().getCause();
1032
        if (cause instanceof CommitIntegrityException)
1033
        {
1034
          fail("---> Should not have failed with: " + e.getCause().getMessage());
1035
        }
1036
        else
1037
        {
1038
          throw e;
1039
        }
1040
      }
1041
    }
1042
  }
1043
1044
  /**
1045
   * Test the commit integrity, assuming that it is bad, using all possible checking styles.
1046
   */
1047
  private void badAll(Set<EObject> expectedMissingObjects) throws CommitException
1048
  {
1049
    bad(Style.NO_EXCEPTION, expectedMissingObjects);
1050
    bad(Style.EXCEPTION_FAST, expectedMissingObjects);
1051
    bad(Style.EXCEPTION, expectedMissingObjects);
1052
    bad(null, expectedMissingObjects);
1053
  }
1054
1055
  /**
1056
   * Test the commit integrity, assuming that it is bad.
1057
   * 
1058
   * @param style
1059
   *          - the checking style to be used; if null, just commit. In that case, the commit logic will choose the
1060
   *          checking style.
1061
   */
1062
  private void bad(Style style, Set<EObject> expectedMissingObjects) throws CommitException
1063
  {
1064
    CommitIntegrityException commitIntegrityEx = null;
1065
    Set<? extends EObject> missingObjects = null;
1066
1067
    CommitIntegrityCheck check = null;
1068
    if (style != null)
1069
    {
1070
      InternalCDOCommitContext ctx = tx.createCommitContext();
1071
      check = new CommitIntegrityCheck(ctx, style);
1072
    }
1073
1074
    if (style == Style.NO_EXCEPTION)
1075
    {
1076
      try
1077
      {
1078
        check.check();
1079
      }
1080
      catch (CommitIntegrityException e)
1081
      {
1082
        fail("Should not have thrown " + CommitIntegrityException.class.getName());
1083
      }
1084
    }
1085
    else if (style == CommitIntegrityCheck.Style.EXCEPTION || style == CommitIntegrityCheck.Style.EXCEPTION_FAST)
1086
    {
1087
      try
1088
      {
1089
        check.check();
1090
        fail("Should have thrown " + CommitIntegrityException.class.getName());
1091
      }
1092
      catch (CommitIntegrityException e)
1093
      {
1094
        commitIntegrityEx = e;
1095
      }
1096
    }
1097
    else if (style == null)
1098
    {
1099
      try
1100
      {
1101
        tx.commit();
1102
        fail("Should have thrown " + CommitException.class.getName());
1103
      }
1104
      catch (CommitException e)
1105
      {
1106
        Throwable cause = e.getCause().getCause();
1107
        if (cause instanceof CommitIntegrityException)
1108
        {
1109
          // Good
1110
          commitIntegrityEx = (CommitIntegrityException)cause;
1111
          System.out.println("---> Failed properly: " + e.getCause().getMessage());
1112
        }
1113
        else
1114
        {
1115
          throw e;
1116
        }
1117
      }
1118
    }
1119
    else
1120
    {
1121
      fail("Unknown style");
1122
    }
1123
1124
    if (commitIntegrityEx != null)
1125
    {
1126
      missingObjects = commitIntegrityEx.getMissingObjects();
1127
    }
1128
    else
1129
    {
1130
      missingObjects = check.getMissingObjects();
1131
    }
1132
1133
    if (style == Style.EXCEPTION_FAST)
1134
    {
1135
      assertEquals(1, missingObjects.size());
1136
    }
1137
    else
1138
    {
1139
      // We cannot use == here, because it isn't (always) possible for the logic to
1140
      // find all missing objects
1141
      assertTrue(missingObjects.size() <= expectedMissingObjects.size());
1142
    }
1143
1144
    for (EObject missingObject : missingObjects)
1145
    {
1146
      assertTrue(expectedMissingObjects.contains(missingObject));
1147
    }
1148
  }
1149
1150
  private void simpleModel1Setup() throws CommitException
1151
  {
1152
    EReference ref = Model1Package.eINSTANCE.getCompany_PurchaseOrders();
1153
    boolean preconditions = ref.isContainment() && ref.getEOpposite() == null && ref.isMany();
1154
    if (!preconditions)
1155
    {
1156
      throw new RuntimeException("Model1 does not meet prerequirements for this test");
1157
    }
1158
1159
    resource1 = tx.createResource(RESOURCENAME);
1160
    company1 = Model1Factory.eINSTANCE.createCompany();
1161
    company2 = Model1Factory.eINSTANCE.createCompany();
1162
    company3 = Model1Factory.eINSTANCE.createCompany();
1163
    company99 = Model1Factory.eINSTANCE.createCompany();
1164
    supplier1 = Model1Factory.eINSTANCE.createSupplier();
1165
    purchaseOrder = Model1Factory.eINSTANCE.createPurchaseOrder();
1166
    company1.getPurchaseOrders().add(purchaseOrder);
1167
    resource1.getContents().add(company1);
1168
    resource1.getContents().add(company2);
1169
    resource1.getContents().add(company3);
1170
    resource1.getContents().add(company99);
1171
    resource1.getContents().add(supplier1);
1172
    tx.commit();
1173
  }
1174
1175
  private void simpleModel4ContainmentSetup() throws CommitException
1176
  {
1177
    EReference ref = model4Package.eINSTANCE.getRefSingleContainedNPL_Element();
1178
    boolean preconditions = ref.isContainment() && ref.getEOpposite() == null && !ref.isMany();
1179
    if (!preconditions)
1180
    {
1181
      throw new RuntimeException("Model4 does not meet prerequirements for this test");
1182
    }
1183
1184
    resource1 = tx.createResource(RESOURCENAME);
1185
1186
    refSingleContained1 = model4Factory.eINSTANCE.createRefSingleContainedNPL();
1187
    refSingleContained2 = model4Factory.eINSTANCE.createRefSingleContainedNPL();
1188
    singleContainedElement1 = model4Factory.eINSTANCE.createContainedElementNoOpposite();
1189
    refSingleContained1.setElement(singleContainedElement1);
1190
    resource1.getContents().add(refSingleContained1);
1191
    resource1.getContents().add(refSingleContained2);
1192
1193
    tx.commit();
1194
  }
1195
1196
  private void simpleModel4SingleBidiSetup() throws CommitException
1197
  {
1198
    EReference ref = model4Package.eINSTANCE.getRefSingleNonContained_Element();
1199
    boolean preconditions = !ref.isContainment() && ref.getEOpposite() != null && !ref.isMany();
1200
    if (!preconditions)
1201
    {
1202
      throw new RuntimeException("Model4 does not meet prerequirements for this test");
1203
    }
1204
1205
    resource1 = tx.createResource(RESOURCENAME);
1206
1207
    refSingleNonContained1 = model4Factory.eINSTANCE.createRefSingleNonContained();
1208
    refSingleNonContained2 = model4Factory.eINSTANCE.createRefSingleNonContained();
1209
    singleNonContainedElement1 = model4Factory.eINSTANCE.createSingleNonContainedElement();
1210
    singleNonContainedElement2 = model4Factory.eINSTANCE.createSingleNonContainedElement();
1211
    refSingleNonContained1.setElement(singleNonContainedElement1);
1212
    resource1.getContents().add(refSingleNonContained1);
1213
    resource1.getContents().add(refSingleNonContained2);
1214
    resource1.getContents().add(singleNonContainedElement1);
1215
    resource1.getContents().add(singleNonContainedElement2);
1216
1217
    tx.commit();
1218
  }
1219
1220
  private void simpleModel4MultiBidiSetup() throws CommitException
1221
  {
1222
    EReference ref = model4Package.eINSTANCE.getRefMultiNonContained_Elements();
1223
    boolean preconditions = !ref.isContainment() && ref.getEOpposite() != null && ref.isMany();
1224
    if (!preconditions)
1225
    {
1226
      throw new RuntimeException("Model4 does not meet prerequirements for this test");
1227
    }
1228
1229
    resource1 = tx.createResource(RESOURCENAME);
1230
1231
    refMultiNonContained1 = model4Factory.eINSTANCE.createRefMultiNonContained();
1232
    refMultiNonContained2 = model4Factory.eINSTANCE.createRefMultiNonContained();
1233
    multiNonContainedElement1 = model4Factory.eINSTANCE.createMultiNonContainedElement();
1234
    multiNonContainedElement2 = model4Factory.eINSTANCE.createMultiNonContainedElement();
1235
    refMultiNonContained1.getElements().add(multiNonContainedElement1);
1236
    resource1.getContents().add(refMultiNonContained1);
1237
    resource1.getContents().add(refMultiNonContained2);
1238
    resource1.getContents().add(multiNonContainedElement1);
1239
    resource1.getContents().add(multiNonContainedElement2);
1240
1241
    tx.commit();
1242
  }
1243
1244
  private Set<EObject> createSet(EObject... objects)
1245
  {
1246
    Set<EObject> committables = new HashSet<EObject>();
1247
    for (EObject o : objects)
1248
    {
1249
      if (o == null)
1250
      {
1251
        throw new NullPointerException();
1252
      }
1253
      committables.add(o);
1254
    }
1255
    return committables;
1256
  }
1257
}

Return to bug 312535