diff options
author | Christian W. Damus | 2014-11-13 20:57:30 +0000 |
---|---|---|
committer | Christian W. Damus | 2014-11-13 21:50:17 +0000 |
commit | 14be55490adc7e7b1325fd033600ed912917aad6 (patch) | |
tree | 8d5a54fc610d9506cc085f05d5a5912bddc78dcb /plugins | |
parent | 2921506e21105cbf1ac389525fd0bdfed7839bdc (diff) | |
download | org.eclipse.papyrus-14be55490adc7e7b1325fd033600ed912917aad6.tar.gz org.eclipse.papyrus-14be55490adc7e7b1325fd033600ed912917aad6.tar.xz org.eclipse.papyrus-14be55490adc7e7b1325fd033600ed912917aad6.zip |
451338: [RSA Model Import] Performance issues when importing larger models to Papyrus
https://bugs.eclipse.org/bugs/show_bug.cgi?id=451338
Fix severe performance problems in stereotype repair, including:
* expensive search for a profile matching an unknown-schema EPackage for every instance of an EClass in the package, despite that a match can never be found
* expensive validation of the transaction that does stereotype repair, despite that there are no semantic changes worth validating
* let SemanticUMLContentProvider refresh its roots only once at the end of a transaction that changes resource-set roots, instead of repeatedly throughout the transaction (and posting async viewer refreshes along the way)
Conflicts:
plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java
Diffstat (limited to 'plugins')
4 files changed, 250 insertions, 59 deletions
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/adapters/ResourceSetRootsAdapter.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/adapters/ResourceSetRootsAdapter.java index 4f8a718fe48..c6811ed723b 100644 --- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/adapters/ResourceSetRootsAdapter.java +++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/adapters/ResourceSetRootsAdapter.java @@ -1,5 +1,5 @@ /*****************************************************************************
- * Copyright (c) 2014 CEA LIST.
+ * Copyright (c) 2014 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -8,18 +8,27 @@ *
* Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus - bug 451338
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.emf.adapters;
+import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EContentAdapter;
+import org.eclipse.emf.transaction.NotificationFilter;
+import org.eclipse.emf.transaction.ResourceSetChangeEvent;
+import org.eclipse.emf.transaction.ResourceSetListener;
+import org.eclipse.emf.transaction.RollbackException;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
/**
- * An EMF Adapter which listens on Resource Set root elements
+ * An EMF Adapter which listens on Resource Set root elements. For resource sets managed by {@link TransactionalEditingDomain}s,
+ * consider using the {@link Transactional} subclass.
*
* @author Camille Letavernier
*
@@ -104,4 +113,117 @@ public abstract class ResourceSetRootsAdapter extends EContentAdapter { protected abstract void doNotify(Notification msg);
+ //
+ // Nested types
+ //
+
+ /**
+ * A variant of the {@link ResourceSetRootsAdapter} that is attached to a {@link TransactionalEditingDomain} to process batched notifications.
+ */
+ public static abstract class Transactional extends ResourceSetRootsAdapter implements ResourceSetListener {
+ private final boolean isPrecommit;
+
+ private final NotificationFilter filter = NotificationFilter.NOT_TOUCH.and(createFilter());
+
+ /**
+ * Initializes me as a post-commit resource-set roots notification handler.
+ */
+ public Transactional() {
+ this(false);
+ }
+
+ /**
+ * Initializes me as a pre- or post-commit resource-set roots notification handler.
+ *
+ * @param isPrecommit
+ * {@code true} to react to pre-commit notifications; {@code false} to react to post-commit notifications
+ */
+ public Transactional(boolean isPrecommit) {
+ this.isPrecommit = isPrecommit;
+ }
+
+ public boolean isAggregatePrecommitListener() {
+ return false;
+ }
+
+ public boolean isPrecommitOnly() {
+ return isPrecommit;
+ }
+
+ public boolean isPostcommitOnly() {
+ return !isPrecommit;
+ }
+
+ /**
+ * Subclasses may override/extend this method to create custom filters, perhaps based on the default filter create by the superclass.
+ * <b>Note</b> that this method is invoked by the superclass constructor, so subclasses must not attempt to access their own state.
+ *
+ * @return my notification filter
+ */
+ protected NotificationFilter createFilter() {
+ return NotificationFilter.createFeatureFilter(ResourceSet.class, ResourceSet.RESOURCE_SET__RESOURCES).or(
+ NotificationFilter.createFeatureFilter(Resource.class, Resource.RESOURCE__CONTENTS));
+ }
+
+ public NotificationFilter getFilter() {
+ return filter;
+ }
+
+ public void resourceSetChanged(ResourceSetChangeEvent event) {
+ handleResourceSetChangeEvent(event);
+ }
+
+ public Command transactionAboutToCommit(ResourceSetChangeEvent event) throws RollbackException {
+ handleResourceSetChangeEvent(event);
+ return null;
+ }
+
+ /**
+ * Subclasses may override this to handle notifications as a group.
+ *
+ * @param event
+ * the resource-set changed event carrying notifications to process
+ *
+ * @see #doNotify(Notification)
+ */
+ protected void handleResourceSetChangeEvent(ResourceSetChangeEvent event) {
+ for (Notification next : event.getNotifications()) {
+ doNotify(next);
+ }
+ }
+
+ /**
+ * Subclasses may override this to handle notifications individually.
+ *
+ * @param msg
+ * a notification from the group sent by the commit of a transaction
+ *
+ * @see #handleResourceSetChangeEvent(ResourceSetChangeEvent)
+ */
+ @Override
+ protected void doNotify(Notification msg) {
+ // Pass
+ }
+
+ @Override
+ public final void setTarget(Notifier newTarget) {
+ // Don't attach me to anything. I am fed directly by the editing domain
+ }
+
+ @Override
+ public final void unsetTarget(Notifier oldTarget) {
+ // Pass
+ }
+
+ @Override
+ protected final void addAdapter(Notifier notifier) {
+ // Don't attach me to anything. I am fed directly by the editing domain
+ }
+
+ @Override
+ protected final void removeAdapter(Notifier notifier) {
+ // Pass
+ }
+ }
+
}
diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java index 73199c701f3..2611395b217 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 CEA and others. + * Copyright (c) 2014 CEA, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ * * Contributors: * Christian W. Damus (CEA) - Initial API and implementation + * Christian W. Damus - bug 451338 * */ package org.eclipse.papyrus.uml.modelrepair.internal.stereotypes; @@ -40,6 +41,7 @@ import org.eclipse.uml2.uml.Extension; import org.eclipse.uml2.uml.Package; import org.eclipse.uml2.uml.Profile; import org.eclipse.uml2.uml.ProfileApplication; +import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.util.UMLUtil; import com.google.common.base.Function; @@ -63,6 +65,8 @@ public class ZombieStereotypesDescriptor { private static final Pattern AUTO_NSURI_PATTERN = Pattern.compile("^http://.*/([^/]+)/\\d+$"); + private final Profile nullProfile = UMLFactory.eINSTANCE.createProfile(); + private final Resource resource; private final Package root; @@ -79,6 +83,8 @@ public class ZombieStereotypesDescriptor { private Map<EPackage, Map<IRepairAction.Kind, IRepairAction>> repairActions = Maps.newHashMap(); + private Map<EPackage, Profile> definitionToProfileMap = Maps.newHashMap(); + public ZombieStereotypesDescriptor(Resource resource, Package root, Set<EPackage> appliedProfileDefinitions, Function<? super EPackage, Profile> dynamicProfileSupplier, LabelProviderService labelProviderService) { this.resource = resource; this.root = root; @@ -269,7 +275,16 @@ public class ZombieStereotypesDescriptor { } protected Profile findProfile(EPackage definition) { - return UMLUtil.getProfile(definition, root); + Profile result = definitionToProfileMap.get(definition); + if (result == null) { + result = UMLUtil.getProfile(definition, root); + if (result == null) { + result = nullProfile; + } + definitionToProfileMap.put(definition, result); + } + + return (result == nullProfile) ? /* cache miss */null : /* cache hit */result; } protected ProfileContext getProfileContext(EObject stereotypeApplication, EPackage schema) { diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/ui/ZombieStereotypesDialog.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/ui/ZombieStereotypesDialog.java index 20a68564bb4..609cfbb0df2 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/ui/ZombieStereotypesDialog.java +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/ui/ZombieStereotypesDialog.java @@ -9,6 +9,7 @@ * Contributors: * CEA - Initial API and implementation * Christian W. Damus (CEA) - bug 431953 (adapted from SwitchProfileDialog) + * Christian W. Damus - bug 451338 * */ package org.eclipse.papyrus.uml.modelrepair.ui; @@ -16,6 +17,7 @@ package org.eclipse.papyrus.uml.modelrepair.ui; import java.lang.reflect.InvocationTargetException; import java.util.AbstractCollection; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -29,6 +31,9 @@ import org.eclipse.emf.common.util.DiagnosticChain; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.RollbackException; +import org.eclipse.emf.transaction.Transaction; +import org.eclipse.emf.transaction.TransactionalCommandStack; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; @@ -227,55 +232,64 @@ public class ZombieStereotypesDialog extends TrayDialog { } final List<MissingSchema> repairActions = Lists.newArrayList(actionsToApply); - editingDomain.getCommandStack().execute(new RecordingCommand(editingDomain, "Repair stereotypes") { + try { + ((TransactionalCommandStack) editingDomain.getCommandStack()).execute(new RecordingCommand(editingDomain, "Repair stereotypes") { - @Override - protected void doExecute() { + @Override + protected void doExecute() { - final BasicDiagnostic diagnostics = new BasicDiagnostic(Activator.PLUGIN_ID, 0, "Problems in repairing stereotypes", null); + final BasicDiagnostic diagnostics = new BasicDiagnostic(Activator.PLUGIN_ID, 0, "Problems in repairing stereotypes", null); - IRunnableWithProgress runnable = TransactionHelper.createPrivilegedRunnableWithProgress(editingDomain, new IRunnableWithProgress() { + IRunnableWithProgress runnable = TransactionHelper.createPrivilegedRunnableWithProgress(editingDomain, new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) { - SubMonitor subMonitor = SubMonitor.convert(monitor, actionsToApply.size()); + public void run(IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor, actionsToApply.size()); - for (Iterator<MissingSchema> iter = repairActions.iterator(); iter.hasNext();) { - if (!iter.next().apply(diagnostics, subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE))) { - // Leave this one to try it again - iter.remove(); + for (Iterator<MissingSchema> iter = repairActions.iterator(); iter.hasNext();) { + if (!iter.next().apply(diagnostics, subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE))) { + // Leave this one to try it again + iter.remove(); + } } - } - subMonitor.done(); + subMonitor.done(); + } + }); + + Cursor waitCursor = new Cursor(getShell().getDisplay(), SWT.CURSOR_WAIT); + try { + getShell().setCursor(waitCursor); + progress.setVisible(true); + ModalContext.run(runnable, true, progress, getShell().getDisplay()); + } catch (Exception e) { + getShell().setCursor(null); + Throwable t = e; + if (e instanceof InvocationTargetException) { + t = ((InvocationTargetException) e).getTargetException(); + } + StatusManager.getManager().handle(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to repair stereotypes.", t), StatusManager.BLOCK | StatusManager.LOG); + } finally { + getShell().setCursor(null); + waitCursor.dispose(); + progress.setVisible(false); } - }); - Cursor waitCursor = new Cursor(getShell().getDisplay(), SWT.CURSOR_WAIT); - try { - getShell().setCursor(waitCursor); - progress.setVisible(true); - ModalContext.run(runnable, true, progress, getShell().getDisplay()); - } catch (Exception e) { - getShell().setCursor(null); - Throwable t = e; - if (e instanceof InvocationTargetException) { - t = ((InvocationTargetException) e).getTargetException(); + if (diagnostics.getSeverity() > Diagnostic.OK) { + DiagnosticDialog dialog = new DiagnosticDialog(getShell(), "Problems in Repairing Stereotypes", + "Some repair actions could not be completed normally. Please review the specific details and take any corrective action that may be required.", + diagnostics, Diagnostic.ERROR | Diagnostic.WARNING); + dialog.setBlockOnOpen(true); + dialog.open(); } - StatusManager.getManager().handle(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to repair stereotypes.", t), StatusManager.BLOCK | StatusManager.LOG); - } finally { - getShell().setCursor(null); - waitCursor.dispose(); - progress.setVisible(false); } - - if (diagnostics.getSeverity() > Diagnostic.OK) { - DiagnosticDialog dialog = new DiagnosticDialog(getShell(), "Problems in Repairing Stereotypes", "Some repair actions could not be completed normally. Please review the specific details and take any corrective action that may be required.", - diagnostics, Diagnostic.ERROR | Diagnostic.WARNING); - dialog.setBlockOnOpen(true); - dialog.open(); - } - } - }); + }, Collections.singletonMap(Transaction.OPTION_NO_VALIDATION, true)); + } catch (RollbackException e) { + // Shouldn't happen without validation! + Activator.log.error(e); + } catch (InterruptedException e) { + IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Repair operation was cancelled.", e); + StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG); + } getMissingSchemas().removeAll(repairActions); updateControls(); diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/providers/SemanticUMLContentProvider.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/providers/SemanticUMLContentProvider.java index 125f1b5408b..2e1ebb1543a 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/providers/SemanticUMLContentProvider.java +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/providers/SemanticUMLContentProvider.java @@ -1,5 +1,5 @@ /*****************************************************************************
- * Copyright (c) 2012, 2014 CEA LIST and others.
+ * Copyright (c) 2012, 2014 CEA LIST, Christian W. Damus, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -9,6 +9,7 @@ * Contributors:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
* Christian W. Damus (CEA) - bug 410346
+ * Christian W. Damus - bug 451338
*
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.providers;
@@ -24,6 +25,10 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.transaction.ResourceSetChangeEvent;
+import org.eclipse.emf.transaction.ResourceSetListener;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.papyrus.infra.core.resource.ModelSet;
import org.eclipse.papyrus.infra.core.resource.NotFoundException;
@@ -253,7 +258,7 @@ public class SemanticUMLContentProvider extends SemanticEMFContentProvider { }
if (newInput == null) {
- resourceSetListener.unsetTarget(root);
+ rootsAdapter.detach(root);
} else {
listenOnResourceSet(resourceSet);
}
@@ -265,13 +270,13 @@ public class SemanticUMLContentProvider extends SemanticEMFContentProvider { protected void listenOnResourceSet(ResourceSet resourceSet) {
if (root != null) {
- resourceSetListener.unsetTarget(root);
+ rootsAdapter.detach(root);
root = null;
roots = null;
}
if (resourceSet != null) {
- resourceSetListener.setTarget(resourceSet);
+ rootsAdapter.attach(resourceSet);
this.root = resourceSet;
this.roots = getRoots(root);
}
@@ -280,7 +285,7 @@ public class SemanticUMLContentProvider extends SemanticEMFContentProvider { @Override
public void dispose() {
if (root != null) {
- resourceSetListener.unsetTarget(root);
+ rootsAdapter.detach(root);
}
root = null;
roots = null;
@@ -292,22 +297,55 @@ public class SemanticUMLContentProvider extends SemanticEMFContentProvider { private Viewer viewer;
- private ResourceSetRootsAdapter resourceSetListener = new ResourceSetRootsAdapter() {
+ private class RootsAdapter {
private boolean needsRefresh = false;
- @Override
- protected void doNotify(Notification msg) {
- if (root == null || msg.isTouch()) {
- return;
+ private Object listener;
+
+ void attach(ResourceSet resourceSet) {
+ TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(resourceSet);
+ if (domain != null) {
+ ResourceSetListener rsl = new ResourceSetRootsAdapter.Transactional() {
+ @Override
+ protected void handleResourceSetChangeEvent(ResourceSetChangeEvent event) {
+ triggerRefresh();
+ }
+ };
+ domain.addResourceSetListener(rsl);
+ listener = rsl;
+ } else {
+ ResourceSetRootsAdapter adapter = new ResourceSetRootsAdapter() {
+ @Override
+ protected void doNotify(Notification msg) {
+ if (root == null || msg.isTouch()) {
+ return;
+ }
+
+ switch (msg.getEventType()) {
+ case Notification.ADD:
+ case Notification.ADD_MANY:
+ case Notification.REMOVE:
+ case Notification.REMOVE_MANY:
+ triggerRefresh();
+ }
+ }
+ };
+ adapter.setTarget(resourceSet);
+ listener = adapter;
}
+ }
- switch (msg.getEventType()) {
- case Notification.ADD:
- case Notification.ADD_MANY:
- case Notification.REMOVE:
- case Notification.REMOVE_MANY:
- triggerRefresh();
+ void detach(ResourceSet resourceSet) {
+ if (listener instanceof ResourceSetRootsAdapter.Transactional) {
+ TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(resourceSet);
+ if (domain != null) {
+ domain.removeResourceSetListener((ResourceSetListener) listener);
+ listener = null;
+ }
+ } else if (listener instanceof ResourceSetRootsAdapter) {
+ ((ResourceSetRootsAdapter) listener).unsetTarget(resourceSet);
+ listener = null;
}
}
@@ -329,6 +367,8 @@ public class SemanticUMLContentProvider extends SemanticEMFContentProvider { });
}
}
- };
+ }
+
+ private RootsAdapter rootsAdapter = new RootsAdapter();
}
|