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 (+426 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.id.CDOIDAndVersion;
17
import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
18
import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
19
import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
20
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
21
import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
22
import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
23
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
24
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDeltaUtil;
25
import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
26
import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
27
import org.eclipse.emf.cdo.eresource.CDOResource;
28
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
29
30
import org.eclipse.net4j.util.CheckUtil;
31
32
import org.eclipse.emf.common.util.EList;
33
import org.eclipse.emf.ecore.EObject;
34
import org.eclipse.emf.ecore.EReference;
35
import org.eclipse.emf.ecore.EStructuralFeature;
36
import org.eclipse.emf.ecore.InternalEObject.EStore;
37
import org.eclipse.emf.spi.cdo.InternalCDOObject;
38
import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
39
import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext;
40
41
import java.util.Collection;
42
import java.util.HashSet;
43
import java.util.Set;
44
45
/**
46
 * @author Caspar De Groot
47
 * @since 4.0
48
 */
49
public class CommitIntegrityCheck
50
{
51
  /**
52
   * Designates an exception style for a {@link CommitIntegrityCheck}
53
   * 
54
   * @author Caspar De Groot
55
   */
56
  public enum Style
57
  {
58
    /**
59
     * Throw an exception as soon as this {@link CommitIntegrityCheck} encounters the first problem
60
     */
61
    EXCEPTION_FAST,
62
63
    /**
64
     * Throw an exception when this {@link CommitIntegrityCheck} finishes performing all possible checks, in case any
65
     * problems were found
66
     */
67
    EXCEPTION,
68
69
    /**
70
     * Do not throw an exception. Caller must invoke {@link CommitIntegrityCheck#getMissingObjects()} to find out if the
71
     * check discovered any problems.
72
     */
73
    NO_EXCEPTION
74
  }
75
76
  private InternalCDOTransaction tx;
77
78
  private Set<CDOID> newIDs, dirtyIDs, detachedIDs;
79
80
  private Style style;
81
82
  private Set<CDOObject> missingObjects = new HashSet<CDOObject>();
83
84
  private StringBuilder exceptionMessage = new StringBuilder();
85
86
  public CommitIntegrityCheck(InternalCDOCommitContext commitContext, Style style)
87
  {
88
    tx = commitContext.getTransaction();
89
90
    newIDs = commitContext.getNewObjects().keySet();
91
    dirtyIDs = commitContext.getDirtyObjects().keySet();
92
    detachedIDs = commitContext.getDetachedObjects().keySet();
93
94
    CheckUtil.checkNull(style, "style should not be null");
95
    this.style = style;
96
  }
97
98
  private Set<CDOID> collectIDs(Collection<? extends CDOIDAndVersion> cdoIDandVersions)
99
  {
100
    Set<CDOID> cdoIDs = new HashSet<CDOID>();
101
    for (CDOIDAndVersion idAndV : cdoIDandVersions)
102
    {
103
      cdoIDs.add(idAndV.getID());
104
    }
105
    return cdoIDs;
106
  }
107
108
  public void check() throws CommitIntegrityException
109
  {
110
    // For new objects: ensure that their container is included,
111
    // as well as the targets of the new object's bidi references
112
    for (CDOID newID : newIDs)
113
    {
114
      CDOObject newObject = tx.getObject(newID);
115
      checkContainerIncluded(newObject, "new");
116
      checkCurrentBidiRefTargetsIncluded(newObject, "new");
117
    }
118
119
    // For detached objects: ensure that their former container is included,
120
    // as well as the targets of the detached object's bidi references
121
    for (CDOID detachedID : detachedIDs)
122
    {
123
      CDOObject detachedObject = tx.getObject(detachedID);
124
      checkFormerContainerIncluded(detachedObject);
125
      checkFormerBidiRefTargetsIncluded(detachedObject, "detached");
126
    }
127
128
    // For dirty objects: if any of the deltas for the object, affect containment (i.e. object was moved)
129
    // or a bi-di reference, ensure that for containment, both the old and new containers are included,
130
    // (or that the child is included if we are considering the dirty parent),
131
    // and that for a bi-di reference, the object holding the other end of the bi-di is included, as
132
    // well as possibly the *former* object holding the other end.
133
    //
134
    for (CDOID dirtyID : dirtyIDs)
135
    {
136
      CDOObject dirtyObject = tx.getObject(dirtyID);
137
      analyzeRevisionDelta((InternalCDOObject)dirtyObject);
138
    }
139
140
    if (!missingObjects.isEmpty() && style == Style.EXCEPTION)
141
    {
142
      throw createException();
143
    }
144
  }
145
146
  private CDOID getContainerOrResourceID(InternalCDORevision revision)
147
  {
148
    CDOID containerOrResourceID = null;
149
    Object idOrObject = revision.getContainerID();
150
    if (idOrObject != null)
151
    {
152
      containerOrResourceID = (CDOID)tx.convertObjectToID(idOrObject);
153
    }
154
    if (containerOrResourceID == null || containerOrResourceID.getType() == Type.NULL)
155
    {
156
      idOrObject = revision.getResourceID();
157
      if (idOrObject != null)
158
      {
159
        containerOrResourceID = (CDOID)tx.convertObjectToID(idOrObject);
160
      }
161
    }
162
    return containerOrResourceID;
163
  }
164
165
  private void analyzeRevisionDelta(InternalCDOObject dirtyObject) throws CommitIntegrityException
166
  {
167
    // Getting the deltas from the TX is not a good idea...
168
    // We better recompute a fresh delta:
169
    //
170
    InternalCDORevision cleanRev = tx.getCleanRevisions().get(dirtyObject);
171
    CheckUtil.checkNull(cleanRev, "Could not obtain clean revision for dirty object " + dirtyObject);
172
    InternalCDORevision dirtyRev = dirtyObject.cdoRevision();
173
    CDORevisionDelta rDelta = CDORevisionDeltaUtil.create(cleanRev, dirtyRev);
174
175
    for (CDOFeatureDelta fDelta : rDelta.getFeatureDeltas())
176
    {
177
      EStructuralFeature feat = fDelta.getFeature();
178
      if (feat == CDOContainerFeatureDelta.CONTAINER_FEATURE)
179
      {
180
        // Object is dirty with respect to its container; this means it was moved;
181
        // We must ensure that both the old and new containers are included
182
        checkContainerIncluded(dirtyObject, "moved");
183
        CDOID containerOrResourceID = getContainerOrResourceID(cleanRev);
184
        checkIncluded(containerOrResourceID, "former container (or resource) of moved", dirtyObject);
185
      }
186
      else if (feat instanceof EReference)
187
      {
188
        EReference ref = (EReference)feat;
189
        if (ref.isContainment() || ref.getEOpposite() != null)
190
        {
191
          // Object is dirty with respect to a containment feature
192
          // We must ensure that any children that were added/removed, are also
193
          // included in the commit
194
195
          if (fDelta instanceof CDOListFeatureDelta)
196
          {
197
            for (CDOFeatureDelta innerFeatDelta : ((CDOListFeatureDelta)fDelta).getListChanges())
198
            {
199
              checkContainmentDelta(innerFeatDelta, dirtyObject);
200
            }
201
          }
202
          else
203
          {
204
            checkContainmentDelta(fDelta, dirtyObject);
205
          }
206
        }
207
      }
208
    }
209
  }
210
211
  private void checkContainmentDelta(CDOFeatureDelta fDelta, CDOObject dirtyObject) throws CommitIntegrityException
212
  {
213
    if (fDelta instanceof CDORemoveFeatureDelta)
214
    {
215
      Object idOrObject = ((CDORemoveFeatureDelta)fDelta).getValue();
216
      CDOID id = (CDOID)tx.convertObjectToID(idOrObject);
217
      checkIncluded(id, "removed child of", dirtyObject);
218
    }
219
    else if (fDelta instanceof CDOAddFeatureDelta)
220
    {
221
      Object idOrObject = ((CDOAddFeatureDelta)fDelta).getValue();
222
      CDOID id = (CDOID)tx.convertObjectToID(idOrObject);
223
      if (id.getType() != CDOID.Type.NULL)
224
      {
225
        checkIncluded(id, "added child of", dirtyObject);
226
      }
227
    }
228
    else if (fDelta instanceof CDOSetFeatureDelta)
229
    {
230
      Object newIdOrObject = ((CDOSetFeatureDelta)fDelta).getValue();
231
      Object oldIdOrObject = ((CDOSetFeatureDelta)fDelta).getOldValue();
232
      CDOID oldId = (CDOID)tx.convertObjectToID(oldIdOrObject);
233
      if (oldIdOrObject != null)
234
      {
235
        if (newIdOrObject == null)
236
        {
237
          // Removal: old child must be included
238
          checkIncluded(oldId, "removed child of", dirtyObject);
239
        }
240
        else
241
        {
242
          // Change: both old and new child must be included
243
          checkIncluded(oldId, "former child of", dirtyObject);
244
          CDOID newId = (CDOID)tx.convertObjectToID(newIdOrObject);
245
          checkIncluded(newId, "new child of", dirtyObject);
246
        }
247
      }
248
      else
249
      {
250
        // New child, no old child
251
        CDOID newId = (CDOID)tx.convertObjectToID(newIdOrObject);
252
        checkIncluded(newId, "new child of", dirtyObject);
253
      }
254
    }
255
    else if (fDelta instanceof CDOClearFeatureDelta)
256
    {
257
      EStructuralFeature feat = ((CDOClearFeatureDelta)fDelta).getFeature();
258
      InternalCDORevision cleanRev = tx.getCleanRevisions().get(dirtyObject);
259
      int n = cleanRev.size(feat);
260
      for (int i = 0; i < n; i++)
261
      {
262
        Object idOrObject = cleanRev.get(feat, i);
263
        CDOID id = (CDOID)tx.convertObjectToID(idOrObject);
264
        checkIncluded(id, "removed child of", dirtyObject);
265
      }
266
    }
267
    else if (fDelta instanceof CDOUnsetFeatureDelta)
268
    {
269
      EStructuralFeature feat = ((CDOUnsetFeatureDelta)fDelta).getFeature();
270
      InternalCDORevision cleanRev = tx.getCleanRevisions().get(dirtyObject);
271
      Object idOrObject = cleanRev.getValue(feat);
272
      CDOID id = (CDOID)tx.convertObjectToID(idOrObject);
273
      checkIncluded(id, "removed child of", dirtyObject);
274
    }
275
    else
276
    {
277
      throw new RuntimeException("Unexpected delta type: " + fDelta.getClass().getSimpleName());
278
    }
279
  }
280
281
  private void checkIncluded(CDOID id, String msg, CDOObject o) throws CommitIntegrityException
282
  {
283
    if (id.getType() == Type.NULL)
284
    {
285
      throw new IllegalArgumentException("CDOID must not be of type NULL");
286
    }
287
288
    if (!dirtyIDs.contains(id) && !detachedIDs.contains(id) && !newIDs.contains(id))
289
    {
290
      CDOObject missingObject = tx.getObject(id);
291
      if (missingObject == null)
292
      {
293
        throw new IllegalStateException("Could not find object for CDOID " + id);
294
      }
295
      missingObjects.add(missingObject);
296
297
      if (exceptionMessage.length() > 0)
298
      {
299
        exceptionMessage.append('\n');
300
      }
301
      String m = String.format("The %s object %s needs to be included in the commit but isn't", msg, o);
302
      exceptionMessage.append(m);
303
304
      if (style == Style.EXCEPTION_FAST)
305
      {
306
        throw createException();
307
      }
308
    }
309
  }
310
311
  private CommitIntegrityException createException()
312
  {
313
    return new CommitIntegrityException(exceptionMessage.toString(), missingObjects);
314
  }
315
316
  /**
317
   * Checks whether the container of a given object is included in the commit
318
   * 
319
   * @param msgFrag
320
   * @throws CommitIntegrityException
321
   */
322
  private void checkContainerIncluded(CDOObject object, String msgFrag) throws CommitIntegrityException
323
  {
324
    EObject eContainer = object.eContainer();
325
    if (eContainer == null)
326
    {
327
      // It's a top-level object
328
      CDOResource resource = object.cdoDirectResource();
329
      checkIncluded(resource.cdoID(), "resource of " + msgFrag, object);
330
    }
331
    else
332
    {
333
      CDOObject container = CDOUtil.getCDOObject(eContainer);
334
      checkIncluded(container.cdoID(), "container of " + msgFrag, object);
335
    }
336
  }
337
338
  private void checkCurrentBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
339
  {
340
    for (EReference eRef : referencer.eClass().getEAllReferences())
341
    {
342
      if (eRef.getEOpposite() != null)
343
      {
344
        if (eRef.isMany())
345
        {
346
          EList<?> list = (EList<?>)referencer.eGet(eRef);
347
          for (Object element : list)
348
          {
349
            checkBidiRefTargetIncluded(element, referencer, msgFrag);
350
          }
351
        }
352
        else
353
        {
354
          Object refTarget = referencer.eGet(eRef);
355
          if (refTarget != null)
356
          {
357
            checkBidiRefTargetIncluded(refTarget, referencer, msgFrag);
358
          }
359
        }
360
      }
361
    }
362
  }
363
364
  private void checkFormerBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
365
  {
366
    // The referencer argument should really be a detached object, and so we know
367
    // that we can find the pre-detach revision in tx.getFormerRevisions(). However,
368
    // the object may have already been dirty prior to detachment, so we check the
369
    // clean revisions first.
370
    //
371
    InternalCDORevision cleanRev = tx.getCleanRevisions().get(referencer);
372
    CheckUtil.checkState(cleanRev, "cleanRev");
373
374
    for (EReference eRef : referencer.eClass().getEAllReferences())
375
    {
376
      if (eRef.getEOpposite() != null)
377
      {
378
        Object value = cleanRev.get(eRef, EStore.NO_INDEX);
379
        if (value != null)
380
        {
381
          if (eRef.isMany())
382
          {
383
            EList<?> list = (EList<?>)value;
384
            for (Object element : list)
385
            {
386
              checkBidiRefTargetIncluded(element, referencer, msgFrag);
387
            }
388
          }
389
          else
390
          {
391
            checkBidiRefTargetIncluded(value, referencer, msgFrag);
392
          }
393
        }
394
      }
395
    }
396
  }
397
398
  private void checkBidiRefTargetIncluded(Object refTarget, CDOObject referencer, String msgFrag)
399
      throws CommitIntegrityException
400
  {
401
    CheckUtil.checkArg(refTarget, "refTarget");
402
    CDOID refTargetID = null;
403
    if (refTarget instanceof EObject)
404
    {
405
      refTargetID = CDOUtil.getCDOObject((EObject)refTarget).cdoID();
406
    }
407
    else if (refTarget instanceof CDOID)
408
    {
409
      refTargetID = (CDOID)refTarget;
410
    }
411
    checkIncluded(refTargetID, "reference target of " + msgFrag, referencer);
412
  }
413
414
  private void checkFormerContainerIncluded(CDOObject detachedObject) throws CommitIntegrityException
415
  {
416
    InternalCDORevision rev = tx.getCleanRevisions().get(detachedObject);
417
    CheckUtil.checkNull(rev, "Could not obtain clean revision for detached object " + detachedObject);
418
    CDOID id = getContainerOrResourceID(rev);
419
    checkIncluded(id, "former container (or resource) of detached", detachedObject);
420
  }
421
422
  public Set<? extends EObject> getMissingObjects()
423
  {
424
    return missingObjects;
425
  }
426
}
(-)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 / +146 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 1202-1207 Link Here
1202
        CDORevisionKey revKey = CDORevisionUtil.createRevisionKey(object.cdoRevision());
1208
        CDORevisionKey revKey = CDORevisionUtil.createRevisionKey(object.cdoRevision());
1203
        formerRevisionKeys.put(object, revKey);
1209
        formerRevisionKeys.put(object, revKey);
1204
      }
1210
      }
1211
      if (!cleanRevisions.containsKey(object))
1212
      {
1213
        cleanRevisions.put(object, object.cdoRevision());
1214
      }
1205
1215
1206
      // Object may have been reattached previously, in which case it must
1216
      // Object may have been reattached previously, in which case it must
1207
      // here be removed from the collection of reattached objects
1217
      // here be removed from the collection of reattached objects
Lines 1551-1569 Link Here
1551
    return newPackages;
1561
    return newPackages;
1552
  }
1562
  }
1553
1563
1554
  private void cleanUp()
1564
  private void cleanUp(CDOCommitContext commitContext)
1565
  {
1566
    if (commitContext == null || !commitContext.isPartialCommit())
1567
    {
1568
      lastSavepoint = firstSavepoint;
1569
      firstSavepoint.clear();
1570
      firstSavepoint.setNextSavepoint(null);
1571
      firstSavepoint.getSharedDetachedObjects().clear();
1572
1573
      // Bug 283985 (Re-attachment)
1574
      formerRevisionKeys.clear();
1575
1576
      cleanRevisions.clear();
1577
      dirty = false;
1578
      conflict = 0;
1579
      lastTemporaryID.set(0);
1580
    }
1581
    else
1582
    {
1583
      collapseSavepoints(commitContext);
1584
1585
      for (CDOObject object : commitContext.getDetachedObjects().values())
1586
      {
1587
        formerRevisionKeys.remove(object);
1588
      }
1589
    }
1590
1591
    // Reset partial-commit filter
1592
    committables = null;
1593
  }
1594
1595
  private void collapseSavepoints(CDOCommitContext commitContext)
1596
  {
1597
    InternalCDOSavepoint newSavepoint = createSavepoint(null);
1598
    copyUncommitted(lastSavepoint.getAllNewObjects(), commitContext.getNewObjects(), newSavepoint.getNewObjects());
1599
    copyUncommitted(lastSavepoint.getAllDirtyObjects(), commitContext.getDirtyObjects(), newSavepoint.getDirtyObjects());
1600
    copyUncommitted(lastSavepoint.getAllRevisionDeltas(), commitContext.getRevisionDeltas(),
1601
        newSavepoint.getRevisionDeltas());
1602
    copyUncommitted(lastSavepoint.getAllDetachedObjects(), commitContext.getDetachedObjects(),
1603
        newSavepoint.getDetachedObjects());
1604
    lastSavepoint = newSavepoint;
1605
    firstSavepoint = lastSavepoint;
1606
  }
1607
1608
  private <T> void copyUncommitted(Map<CDOID, T> oldSavepointMap, Map<CDOID, T> commitCtxMap,
1609
      Map<CDOID, T> newSavepointMap)
1555
  {
1610
  {
1556
    lastSavepoint = firstSavepoint;
1611
    for (Entry<CDOID, T> entry : oldSavepointMap.entrySet())
1557
    firstSavepoint.clear();
1612
    {
1558
    firstSavepoint.setNextSavepoint(null);
1613
      if (!commitCtxMap.containsKey(entry.getKey()))
1559
    firstSavepoint.getSharedDetachedObjects().clear();
1614
      {
1560
1615
        newSavepointMap.put(entry.getKey(), entry.getValue());
1561
    // Bug 283985 (Re-attachment)
1616
      }
1562
    formerRevisionKeys.clear();
1617
    }
1563
1564
    dirty = false;
1565
    conflict = 0;
1566
    lastTemporaryID.set(0);
1567
  }
1618
  }
1568
1619
1569
  public CDOSavepoint[] exportChanges(OutputStream stream) throws IOException
1620
  public CDOSavepoint[] exportChanges(OutputStream stream) throws IOException
Lines 1925-1930 Link Here
1925
    commitComment = comment;
1976
    commitComment = comment;
1926
  }
1977
  }
1927
1978
1979
  public void setCommittables(Set<EObject> committables)
1980
  {
1981
    this.committables = committables;
1982
  }
1983
1984
  public Set<EObject> getCommittables()
1985
  {
1986
    return committables;
1987
  }
1988
1989
  public Map<InternalCDOObject, InternalCDORevision> getCleanRevisions()
1990
  {
1991
    return cleanRevisions;
1992
  }
1993
1928
  /**
1994
  /**
1929
   * @author Simon McDuff
1995
   * @author Simon McDuff
1930
   */
1996
   */
Lines 1934-1939 Link Here
1934
2000
1935
    private CDOCommitData commitData;
2001
    private CDOCommitData commitData;
1936
2002
2003
    private Map<CDOID, CDOObject> newObjects;
2004
2005
    private Map<CDOID, CDOObject> detachedObjects;
2006
2007
    private Map<CDOID, CDORevisionDelta> revisionDeltas;
2008
2009
    private Map<CDOID, CDOObject> dirtyObjects;
2010
2011
    /**
2012
     * Tracks whether this commit is *actually* partial or not. (Having tx.committables != null does not in itself mean
2013
     * that the commit will be partial, because the committables could cover all dirty/new/detached objects. But this
2014
     * boolean gets set to reflect whether the commit will really commit less than all dirty/new/detached objects.)
2015
     */
2016
    private boolean isPartialCommit;
2017
1937
    public CDOCommitContextImpl(InternalCDOTransaction transaction)
2018
    public CDOCommitContextImpl(InternalCDOTransaction transaction)
1938
    {
2019
    {
1939
      this.transaction = transaction;
2020
      this.transaction = transaction;
Lines 1943-1971 Link Here
1943
    private void calculateCommitData()
2024
    private void calculateCommitData()
1944
    {
2025
    {
1945
      List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
2026
      List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
1946
      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(getNewObjects().size());
2027
      newObjects = filterCommittables(transaction.getNewObjects());
1947
      for (CDOObject newObject : getNewObjects().values())
2028
      List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(newObjects.size());
2029
      for (CDOObject newObject : newObjects.values())
1948
      {
2030
      {
1949
        revisions.add(newObject.cdoRevision());
2031
        revisions.add(newObject.cdoRevision());
1950
      }
2032
      }
1951
2033
1952
      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(getRevisionDeltas().size());
2034
      revisionDeltas = filterCommittables(transaction.getRevisionDeltas());
1953
      for (CDORevisionDelta delta : getRevisionDeltas().values())
2035
      List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(revisionDeltas.size());
2036
      for (CDORevisionDelta delta : revisionDeltas.values())
1954
      {
2037
      {
1955
        deltas.add(delta);
2038
        deltas.add(delta);
1956
      }
2039
      }
1957
2040
1958
      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(getDetachedObjects().size());
2041
      detachedObjects = filterCommittables(transaction.getDetachedObjects());
1959
      for (CDOID id : getDetachedObjects().keySet())
2042
      List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(detachedObjects.size());
2043
      for (CDOID id : detachedObjects.keySet())
1960
      {
2044
      {
1961
        // Add "version-less" key.
2045
        // Add "version-less" key.
1962
        // CDOSessionImpl.reviseRevisions() will call reviseLatest() accordingly.
2046
        // CDOSessionImpl.reviseRevisions() will call reviseLatest() accordingly.
1963
        detached.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
2047
        detached.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
1964
      }
2048
      }
1965
2049
2050
      dirtyObjects = filterCommittables(transaction.getDirtyObjects());
2051
1966
      commitData = new CDOCommitDataImpl(newPackageUnits, revisions, deltas, detached);
2052
      commitData = new CDOCommitDataImpl(newPackageUnits, revisions, deltas, detached);
1967
    }
2053
    }
1968
2054
2055
    private <T> Map<CDOID, T> filterCommittables(Map<CDOID, T> map)
2056
    {
2057
      if (committables == null)
2058
      {
2059
        // No partial commit filter -- nothing to do
2060
        return map;
2061
      }
2062
2063
      Map<CDOID, T> newMap = new HashMap<CDOID, T>();
2064
      for (CDOID id : map.keySet())
2065
      {
2066
        CDOObject o = getObject(id);
2067
        if (committables.contains(o))
2068
        {
2069
          newMap.put(id, map.get(id));
2070
        }
2071
        else
2072
        {
2073
          isPartialCommit = true;
2074
        }
2075
      }
2076
      return newMap;
2077
    }
2078
1969
    public InternalCDOTransaction getTransaction()
2079
    public InternalCDOTransaction getTransaction()
1970
    {
2080
    {
1971
      return transaction;
2081
      return transaction;
Lines 1978-1989 Link Here
1978
2088
1979
    public Map<CDOID, CDOObject> getDirtyObjects()
2089
    public Map<CDOID, CDOObject> getDirtyObjects()
1980
    {
2090
    {
1981
      return transaction.getDirtyObjects();
2091
      return dirtyObjects;
1982
    }
2092
    }
1983
2093
1984
    public Map<CDOID, CDOObject> getNewObjects()
2094
    public Map<CDOID, CDOObject> getNewObjects()
1985
    {
2095
    {
1986
      return transaction.getNewObjects();
2096
      return newObjects;
1987
    }
2097
    }
1988
2098
1989
    public List<CDOPackageUnit> getNewPackageUnits()
2099
    public List<CDOPackageUnit> getNewPackageUnits()
Lines 1993-2004 Link Here
1993
2103
1994
    public Map<CDOID, CDOObject> getDetachedObjects()
2104
    public Map<CDOID, CDOObject> getDetachedObjects()
1995
    {
2105
    {
1996
      return transaction.getDetachedObjects();
2106
      return detachedObjects;
1997
    }
2107
    }
1998
2108
1999
    public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
2109
    public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
2000
    {
2110
    {
2001
      return transaction.getRevisionDeltas();
2111
      return revisionDeltas;
2002
    }
2112
    }
2003
2113
2004
    public void preCommit()
2114
    public void preCommit()
Lines 2046-2051 Link Here
2046
2156
2047
        try
2157
        try
2048
        {
2158
        {
2159
          // TODO (CD) It might be better to always do the checks,
2160
          // instead of only for partial commits
2161
          if (isPartialCommit)
2162
          {
2163
            new CommitIntegrityCheck(this, CommitIntegrityCheck.Style.EXCEPTION_FAST).check();
2164
          }
2165
2049
          preCommit(getNewObjects());
2166
          preCommit(getNewObjects());
2050
          preCommit(getDirtyObjects());
2167
          preCommit(getDirtyObjects());
2051
        }
2168
        }
Lines 2110-2116 Link Here
2110
          getChangeSubscriptionManager().committedTransaction(transaction, this);
2227
          getChangeSubscriptionManager().committedTransaction(transaction, this);
2111
          getAdapterManager().committedTransaction(transaction, this);
2228
          getAdapterManager().committedTransaction(transaction, this);
2112
2229
2113
          cleanUp();
2230
          cleanUp(this);
2114
          Map<CDOID, CDOID> idMappings = result.getIDMappings();
2231
          Map<CDOID, CDOID> idMappings = result.getIDMappings();
2115
          IListener[] listeners = getListeners();
2232
          IListener[] listeners = getListeners();
2116
          if (listeners != null)
2233
          if (listeners != null)
Lines 2180-2185 Link Here
2180
        }
2297
        }
2181
      }
2298
      }
2182
    }
2299
    }
2300
2301
    public boolean isPartialCommit()
2302
    {
2303
      return isPartialCommit;
2304
    }
2183
  }
2305
  }
2184
2306
2185
  /**
2307
  /**
(-)src/org/eclipse/emf/internal/cdo/transaction/CDOXACommitContextImpl.java (+5 lines)
Lines 199-202 Link Here
199
  {
199
  {
200
    return MessageFormat.format("CDOXACommitContext[{0}, {1}]", transactionManager, state);
200
    return MessageFormat.format("CDOXACommitContext[{0}, {1}]", transactionManager, state);
201
  }
201
  }
202
203
  public boolean isPartialCommit()
204
  {
205
    return delegateCommitContext.isPartialCommit();
206
  }
202
}
207
}
(-)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 RuntimeException("Not yet implemented");
442
  }
443
444
  public Set<EObject> getCommittables()
445
  {
446
    throw new RuntimeException("Not yet implemented");
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 / +30 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
    return super.toStringAdditional() + MessageFormat.format(", oldValue={0}", oldValueForMessage); //$NON-NLS-1$
90
  }
62
}
91
}
(-)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