diff options
| author | Esteban Dugueperoux | 2015-02-20 11:17:29 +0000 |
|---|---|---|
| committer | Esteban DUGUEPEROUX | 2015-02-26 11:02:20 +0000 |
| commit | 1c7f627141c23d0b531aed77250be33321802ca0 (patch) | |
| tree | 4199cc169acd5883237fbcb71b2ccc22892fec6f | |
| parent | 051561733efbb093b4dd606d727041b0170ecbd3 (diff) | |
| download | org.eclipse.sirius-1c7f627141c23d0b531aed77250be33321802ca0.tar.gz org.eclipse.sirius-1c7f627141c23d0b531aed77250be33321802ca0.tar.xz org.eclipse.sirius-1c7f627141c23d0b531aed77250be33321802ca0.zip | |
[452962] Have Saver registration done in a single place and correctly
- Have Saver registration/unregistration for both
ResourceSetListener and Lifecycle in constructor/dispose().
- Add SaverTest to check that in case of rollback a save in postcommit
is
disarmed to avoid a save for the next executed command.
- SaverTest is minimalist and can be run in standalone, it's for that
SessionResourcesSynchronizer/DAnalysisSelectorService/DAnalysisSessionImpl
and some others classes are changed.
- To avoid "IllegalStateException: Cannot activate read/write
transaction in read-only transaction context" when calling the
SavingPolicy in postcommit and this SavingPolicy execute an EMF Command,
have Saver call the SavingPolicy in
TransactionalEditingDomainListener.transactionClosed().
Have WorkspaceBackEnd cleaned correctly on session close.
Bug: 452962
Change-Id: Ibca4ebdb0a85eaf1ab9c0583b49a6f8630174cc4
Signed-off-by: Esteban Dugueperoux <esteban.dugueperoux@obeo.fr>
6 files changed, 262 insertions, 84 deletions
diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/resource/AbstractResourceSyncBackend.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/resource/AbstractResourceSyncBackend.java index b58beb0333..f48950d645 100644 --- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/resource/AbstractResourceSyncBackend.java +++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/api/resource/AbstractResourceSyncBackend.java @@ -43,16 +43,19 @@ public abstract class AbstractResourceSyncBackend { this.client = client; } - /** * initialize and prepare the backend. */ - public abstract void install(); + public void install() { + + } /** * de-initialize the backend. */ - public abstract void uninstall(); + public void uninstall() { + observedSet = null; + } /** * Specify which {@link ResourceSet} the backend should notify about. diff --git a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/resource/WorkspaceBackend.java b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/resource/WorkspaceBackend.java index 1a1d5f2ad3..1777318436 100644 --- a/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/resource/WorkspaceBackend.java +++ b/plugins/org.eclipse.sirius.common/src/org/eclipse/sirius/common/tools/internal/resource/WorkspaceBackend.java @@ -57,6 +57,7 @@ public class WorkspaceBackend extends AbstractResourceSyncBackend { workspace.removeResourceChangeListener(listener); listener = null; } + super.uninstall(); } public ResourceSet getObservedSet() { diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java index 8f194d8f0d..f1a4be8e31 100644 --- a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/suite/common/AllCommonPluginTests.java @@ -59,6 +59,7 @@ import org.eclipse.sirius.tests.unit.common.OperationCanceledExceptionSessionTes import org.eclipse.sirius.tests.unit.common.PreferencesTests; import org.eclipse.sirius.tests.unit.common.RefreshEditorsPrecommitListenerTests; import org.eclipse.sirius.tests.unit.common.RestoreSessionFromEditorInputTests; +import org.eclipse.sirius.tests.unit.common.SaverTest; import org.eclipse.sirius.tests.unit.common.SiriusCrossReferenceAdapterTests; import org.eclipse.sirius.tests.unit.common.TransientSessionTests; import org.eclipse.sirius.tests.unit.common.WorkspaceResourceSyncTestCase; @@ -265,6 +266,7 @@ public class AllCommonPluginTests extends TestCase { suite.addTestSuite(TransientSessionTests.class); suite.addTestSuite(RestoreSessionFromEditorInputTests.class); suite.addTestSuite(SiriusCrossReferenceAdapterTests.class); + suite.addTestSuite(SaverTest.class); } /** @@ -280,7 +282,8 @@ public class AllCommonPluginTests extends TestCase { suite.addTest(new JUnit4TestAdapter(DiagramMigrationTestCampaign10.class)); // This one takes too long (12 minutes) to be part of the Gerrit suite. suite.addTestSuite(AcceleoMTInterpreterOnPackageImportTests.class); - // The ones below are "blacklisted" for now because they caused at least one false-negative Gerrit Verification job + // The ones below are "blacklisted" for now because they caused at least + // one false-negative Gerrit Verification job suite.addTestSuite(SessionManagerListener2Tests.class); } diff --git a/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/SaverTest.java b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/SaverTest.java new file mode 100644 index 0000000000..8545dfd3cf --- /dev/null +++ b/plugins/org.eclipse.sirius.tests.junit/src/org/eclipse/sirius/tests/unit/common/SaverTest.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2015 Obeo. + * All rights reserved. This program + * and the accompanying materials are made available under the terms of the + * Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.tests.unit.common; + +import java.io.File; +import java.util.Collection; +import java.util.Map; + +import junit.framework.TestCase; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.ResourceSetListener; +import org.eclipse.emf.transaction.ResourceSetListenerImpl; +import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.sirius.business.api.helper.SiriusUtil; +import org.eclipse.sirius.business.api.session.SavingPolicy; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionStatus; +import org.eclipse.sirius.business.api.session.factory.SessionFactory; +import org.eclipse.sirius.business.internal.resource.parser.AirDResourceFactory; +import org.eclipse.sirius.business.internal.session.danalysis.DAnalysisSessionImpl; +import org.eclipse.sirius.viewpoint.DAnalysis; + +/** + * Test for Bugzilla 445603. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class SaverTest extends TestCase { + + private File tempFile; + + private Session session; + + @Override + protected void setUp() throws Exception { + super.setUp(); + Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(SiriusUtil.SESSION_RESOURCE_EXTENSION, new AirDResourceFactory()); + tempFile = File.createTempFile("test", "." + SiriusUtil.SESSION_RESOURCE_EXTENSION); + tempFile.delete(); + URI sessionResourceURI = URI.createFileURI(tempFile.getCanonicalPath()); + session = SessionFactory.INSTANCE.createSession(sessionResourceURI, new NullProgressMonitor()); + session.open(new NullProgressMonitor()); + } + + /** + * Test that a session saving in middle of a EMF Transaction is done only + * after transaction closing. + */ + public void testSaveInMiddleOfTransaction() { + // Make the session dirty + TransactionalEditingDomain domain = session.getTransactionalEditingDomain(); + DAnalysis dAnalysis = (DAnalysis) session.getSessionResource().getContents().get(0); + Command updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.DIRTY, session.getStatus()); + + // Test that session is saved only after transaction closing + ResourceSetListener saverInMiddleOfTx = new SaverInMiddleOfTx(session, false); + domain.addResourceSetListener(saverInMiddleOfTx); + updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.SYNC, session.getStatus()); + domain.removeResourceSetListener(saverInMiddleOfTx); + + // Test also with a rollback + saverInMiddleOfTx = new SaverInMiddleOfTx(session, true); + domain.addResourceSetListener(saverInMiddleOfTx); + + updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.SYNC, session.getStatus()); + domain.removeResourceSetListener(saverInMiddleOfTx); + + updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.DIRTY, session.getStatus()); + } + + /** + * Test that a session saving in middle of a EMF Transaction is done only + * after transaction closing and with a {@link SavingPolicy} executing a EMF + * Command does not throw exception "IllegalStateException: Cannot activate + * read/write transaction in read-only transaction context". + */ + public void testSaveInMiddleOfTransactionWithSavingPolicyExecutingAEMFCommand() { + ((DAnalysisSessionImpl) session).setSaveInExclusiveTransaction(false); + final SavingPolicy savingPolicy = session.getSavingPolicy(); + session.setSavingPolicy(new SavingPolicy() { + + @Override + public Collection<Resource> save(Iterable<Resource> resourcesToSave, Map<?, ?> options, IProgressMonitor monitor) { + TransactionalEditingDomain domain = session.getTransactionalEditingDomain(); + DAnalysis dAnalysis = (DAnalysis) session.getSessionResource().getContents().get(0); + Command updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + return savingPolicy.save(resourcesToSave, options, monitor); + } + }); + // Make the session dirty + TransactionalEditingDomain domain = session.getTransactionalEditingDomain(); + DAnalysis dAnalysis = (DAnalysis) session.getSessionResource().getContents().get(0); + Command updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.DIRTY, session.getStatus()); + + // Test that session is saved only after transaction closing + ResourceSetListener saverInMiddleOfTx = new SaverInMiddleOfTx(session, false); + domain.addResourceSetListener(saverInMiddleOfTx); + updateDAnalysisCmd = new ChangeDAnalysisCmd(domain, dAnalysis); + domain.getCommandStack().execute(updateDAnalysisCmd); + assertEquals(SessionStatus.SYNC, session.getStatus()); + domain.removeResourceSetListener(saverInMiddleOfTx); + } + + private static class ChangeDAnalysisCmd extends RecordingCommand { + + private DAnalysis dAnalysis; + + public ChangeDAnalysisCmd(TransactionalEditingDomain domain, DAnalysis dAnalysis) { + super(domain); + this.dAnalysis = dAnalysis; + } + + @Override + protected void doExecute() { + dAnalysis.setVersion(dAnalysis.getVersion() + "Changed"); + } + } + + private static class SaverInMiddleOfTx extends ResourceSetListenerImpl { + + private Session session; + + private boolean rollback; + + public SaverInMiddleOfTx(Session session, boolean rollback) { + this.session = session; + this.rollback = rollback; + } + + @Override + public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException { + session.save(new NullProgressMonitor()); + if (rollback) { + throw new RollbackException(Status.CANCEL_STATUS); + } else { + assertEquals(SessionStatus.DIRTY, session.getStatus()); + } + return super.transactionAboutToCommit(event); + } + } + + @Override + protected void tearDown() throws Exception { + session.close(new NullProgressMonitor()); + session = null; + tempFile.delete(); + tempFile = null; + super.tearDown(); + } +} diff --git a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/DAnalysisSessionImpl.java b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/DAnalysisSessionImpl.java index 71c5ff361e..8dc8288cde 100644 --- a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/DAnalysisSessionImpl.java +++ b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/DAnalysisSessionImpl.java @@ -13,7 +13,6 @@ package org.eclipse.sirius.business.internal.session.danalysis; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -142,7 +141,7 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements // Session's configuration - private final Saver saver = new Saver(this); + private final Saver saver; private ReloadingPolicy reloadingPolicy; @@ -192,7 +191,7 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements Preconditions.checkNotNull(this.sessionResource, "A session must be inside a resource."); this.transactionalEditingDomain = Preconditions.checkNotNull(TransactionUtil.getEditingDomain(mainDAnalysis), "A session must be associated to an EditingDomain"); this.mainDAnalysis = mainDAnalysis; - + this.saver = new Saver(this); this.interpreter = new ODesignGenericInterpreter(); this.representationsChangeAdapter = new RepresentationsChangeAdapter(this); this.controlledResourcesDetector = new ControlledResourcesDetector(this); @@ -303,8 +302,7 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements // resourceSet // code to remove when AirDCrossReferenceAdapter is deleted List<Adapter> adaptersToRemove = new ArrayList<Adapter>(); - for (Iterator<Adapter> iterator = resourceSet.eAdapters().iterator(); iterator.hasNext(); ) { - Adapter next = iterator.next(); + for (Adapter next : resourceSet.eAdapters()) { if (next instanceof SiriusCrossReferenceAdapter) { ((SiriusCrossReferenceAdapter) next).disableResolveProxy(); adaptersToRemove.add(next); @@ -984,6 +982,14 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements return this.saver.deferSaveToPostCommit; } + /** + * Set to true to do saving in a read-only EMF Transaction, false otherwise. + * Note that if the {@link SavingPolicy} execute some EMF Command, this must + * be at false. + * + * @param saveInExclusiveTransaction + * specify if the saving is done in a read-only transaction + */ public void setSaveInExclusiveTransaction(boolean saveInExclusiveTransaction) { this.saver.saveInExclusiveTransaction = saveInExclusiveTransaction; } @@ -1212,7 +1218,6 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements monitor.worked(1); this.representationNameListener = new RepresentationNameListener(this); monitor.worked(1); - saver.initialize(); final Collection<DAnalysis> allAnalyses = allAnalyses(); if (allAnalyses.isEmpty()) { @@ -1277,8 +1282,6 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements monitor.worked(1); DViewOperations.on(this).updateSelectedViewpointsData(new SubProgressMonitor(monitor, 10)); initLocalTriggers(); - - getTransactionalEditingDomain().addResourceSetListener(saver); } catch (OperationCanceledException e) { close(new SubProgressMonitor(monitor, 10)); throw e; @@ -1306,9 +1309,6 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements if (!isOpen()) { return; } - if (saver != null && getTransactionalEditingDomain() != null) { - getTransactionalEditingDomain().removeResourceSetListener(saver); - } ViewpointRegistry.getInstance().removeListener(this.vsmUpdater); this.vsmUpdater = null; notifyListeners(SessionListener.CLOSING); @@ -1400,10 +1400,9 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements // Disable resolveProxy for SiriusCrossreferencerAdapter. // SiriusCrossreferencerAdapter on EObject are also on resource, // consequently we manage only the resource itself. - for (Iterator<Adapter> iterator = resource.eAdapters().iterator(); iterator.hasNext(); ) { - Adapter next = iterator.next(); - if (next instanceof SiriusCrossReferenceAdapter) { - ((SiriusCrossReferenceAdapter) next).disableResolveProxy(); + for (Adapter adapter : resource.eAdapters()) { + if (adapter instanceof SiriusCrossReferenceAdapter) { + ((SiriusCrossReferenceAdapter) adapter).disableResolveProxy(); } } } @@ -1419,10 +1418,9 @@ public class DAnalysisSessionImpl extends DAnalysisSessionEObjectImpl implements // Enable resolveProxy for SiriusCrossreferencerAdapter. // SiriusCrossreferencerAdapter on EObject are also on resource, // consequently we manage only the resource itself. - for (Iterator<Adapter> iterator = resource.eAdapters().iterator(); iterator.hasNext(); ) { - Adapter next = iterator.next(); - if (next instanceof SiriusCrossReferenceAdapter) { - ((SiriusCrossReferenceAdapter) next).enableResolveProxy(); + for (Adapter adapter : resource.eAdapters()) { + if (adapter instanceof SiriusCrossReferenceAdapter) { + ((SiriusCrossReferenceAdapter) adapter).enableResolveProxy(); } } } diff --git a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/Saver.java b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/Saver.java index 9ec3b6366c..1e27288a9e 100644 --- a/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/Saver.java +++ b/plugins/org.eclipse.sirius/src/org/eclipse/sirius/business/internal/session/danalysis/Saver.java @@ -14,32 +14,33 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.emf.transaction.ResourceSetChangeEvent; -import org.eclipse.emf.transaction.ResourceSetListenerImpl; import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.TransactionalEditingDomain.Lifecycle; import org.eclipse.emf.transaction.TransactionalEditingDomainEvent; -import org.eclipse.emf.transaction.TransactionalEditingDomainListener; import org.eclipse.emf.transaction.TransactionalEditingDomainListenerImpl; import org.eclipse.emf.transaction.impl.InternalTransaction; import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; /** - * Encapsulates the decision of *when* to actually save the session's state - * when Session.save() is called. If Session.save() is called while a - * transaction is in progress and deferSaveToPostCommit is true, the actual - * saving will be performed after the current transaction has been - * successfully commited. Otherwise it is performed immediatly. + * Encapsulates the decision of *when* to actually save the session's state when + * Session.save() is called. If Session.save() is called while a transaction is + * in progress and deferSaveToPostCommit is true, the actual saving will be + * performed after the current transaction has been successfully commited. + * Otherwise it is performed immediatly. * * @author pcdavid */ -final class Saver extends ResourceSetListenerImpl { +final class Saver extends TransactionalEditingDomainListenerImpl { - boolean deferSaveToPostCommit; - + boolean saveInExclusiveTransaction; - + AtomicBoolean domainDisposed = new AtomicBoolean(false); + + private TransactionalEditingDomain domain; + private final DAnalysisSessionImpl session; private AtomicBoolean saveOnPostCommit = new AtomicBoolean(false); @@ -48,22 +49,7 @@ final class Saver extends ResourceSetListenerImpl { private IProgressMonitor savedMonitor; - /** - * Make sure the Saver's state is reset after the transaction is - * finished, even in case of rollback (in which case #resourceSetChanged - * will not have need called). - */ - private TransactionalEditingDomainListener domainListener = new TransactionalEditingDomainListenerImpl() { - @Override - public void transactionClosed(TransactionalEditingDomainEvent event) { - disarm(); - } - - @Override - public void editingDomainDisposing(TransactionalEditingDomainEvent event) { - domainDisposed.set(true); - } - }; + private AtomicBoolean isSaving = new AtomicBoolean(); /** * Create a new Saver for the specified session. @@ -73,51 +59,40 @@ final class Saver extends ResourceSetListenerImpl { */ public Saver(DAnalysisSessionImpl session) { this.session = session; - } - - public void initialize() { - TransactionalEditingDomain ted = session.getTransactionalEditingDomain(); - if (ted instanceof TransactionalEditingDomain.Lifecycle) { - TransactionalEditingDomain.Lifecycle lc = (TransactionalEditingDomain.Lifecycle) ted; - lc.addTransactionalEditingDomainListener(domainListener); - } - } - - public void dispose() { - TransactionalEditingDomain ted = session.getTransactionalEditingDomain(); - if (ted instanceof TransactionalEditingDomain.Lifecycle) { - TransactionalEditingDomain.Lifecycle lc = (TransactionalEditingDomain.Lifecycle) ted; - lc.removeTransactionalEditingDomainListener(domainListener); + domain = session.getTransactionalEditingDomain(); + Lifecycle lifecycle = TransactionUtil.getAdapter(domain, Lifecycle.class); + if (lifecycle != null) { + lifecycle.addTransactionalEditingDomainListener(this); } - disarm(); - } - - @Override - public boolean isPostcommitOnly() { - return true; } + /** + * Do saving after transaction closing in case the SavingPolicy trigger + * another transaction by executing a EMF Command. + */ @Override - public void resourceSetChanged(ResourceSetChangeEvent event) { - if (saveOnPostCommit.get()) { - saveNow(this.savedOptions, this.savedMonitor, true); + public void transactionClosed(TransactionalEditingDomainEvent event) { + if (!event.getTransaction().isReadOnly()) { + if (saveOnPostCommit.get()) { + saveNow(this.savedOptions, this.savedMonitor, saveInExclusiveTransaction); + } } } public void save(Map<?, ?> options, IProgressMonitor monitor) { boolean tip = transactionInProgress(); if (tip && deferSaveToPostCommit) { - saveOnPostCommit(options, monitor); + saveAfterTransactionClosing(options, monitor); } else { saveNow(options, monitor, saveInExclusiveTransaction && !tip && !domainDisposed.get()); } } /** - * Arm the trigger so that the saving is performed on the next - * post-commit. + * Arm the trigger so that the saving is performed after transaction + * closing. */ - private void saveOnPostCommit(Map<?, ?> options, IProgressMonitor monitor) { + private void saveAfterTransactionClosing(Map<?, ?> options, IProgressMonitor monitor) { this.savedOptions = options; this.savedMonitor = monitor; this.saveOnPostCommit.set(true); @@ -127,10 +102,23 @@ final class Saver extends ResourceSetListenerImpl { * Save immediately and disarm the trigger. */ private void saveNow(Map<?, ?> options, IProgressMonitor monitor, boolean runExclusive) { - try { - session.doSave(options, monitor, runExclusive); - } finally { - disarm(); + // This allows to have session saving thread safe, i.e. only one thread + // can do a save at a time + synchronized (isSaving) { + // In addition if the session saving or more specifically its + // SavingPolicy execute a EMF Command, and we have saveOnPostCommit + // at true, we risk a StackOverflow then to avoid that we check if + // we are already in a session saving call + if (!isSaving.get()) { + try { + isSaving.set(true); + session.doSave(options, monitor, runExclusive); + } finally { + disarm(); + isSaving.set(false); + + } + } } } @@ -148,4 +136,12 @@ final class Saver extends ResourceSetListenerImpl { return false; } -}
\ No newline at end of file + public void dispose() { + Lifecycle lifecycle = TransactionUtil.getAdapter(domain, Lifecycle.class); + if (lifecycle != null) { + lifecycle.removeTransactionalEditingDomainListener(this); + } + disarm(); + } + +} |
