Bug 566942 - [CDO] Performance issues when loading models stored on a
truly remote CDO repository

Implement a non-lazy CacheAdapter that has an efficient
InverseCrossReferencer.

Use of this
org.eclipse.papyrus.cdo.core.resource.NonLazyPapyrusCDOCacheAdapter to
replace the CacheAdapter when System property
ORG_ECLIPSE_PAPYRUS_LAZY_CDO_RESOURCE_LOADING is not true.

In org.eclipse.emf.cdo.dawn.gmf.editors.impl.DawnGMFEditorSupport.handleRemoteLockChanges(Map<Object,
DawnState>), process the RemoteLockState asynchronously.


Change-Id: Ia052fabda59f16a9799a2b546396da3239c0b580
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/NonLazyPapyrusCDOCacheAdapter.java b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/NonLazyPapyrusCDOCacheAdapter.java
new file mode 100644
index 0000000..894639c
--- /dev/null
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/NonLazyPapyrusCDOCacheAdapter.java
@@ -0,0 +1,179 @@
+/*****************************************************************************
+ * Copyright (c) 2020 CEA LIST and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *   CEA LIST - Initial API and implementation
+ *   
+ *****************************************************************************/
+
+package org.eclipse.papyrus.cdo.core.resource;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.emf.cdo.util.CDOCrossReferenceAdapter;
+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.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.uml2.common.util.CacheAdapter;
+
+/** Class based on @see org.eclipse.emf.cdo.util.CDOCrossReferenceAdapter */
+public class NonLazyPapyrusCDOCacheAdapter extends CacheAdapter {
+
+	public static void register(CacheAdapter cacheAdapter) {
+		if (THREAD_LOCAL == null) {
+			try {
+				Field field = CacheAdapter.class.getDeclaredField("INSTANCE");
+				field.setAccessible(true);
+				field.set(null, cacheAdapter);
+			} catch (Throwable throwable) {
+				// ignore
+			}
+		} else {
+			THREAD_LOCAL.set(cacheAdapter);
+		}
+	}
+
+	@Override
+	public void setTarget(Notifier target) {
+		if (target instanceof Resource) {
+			super.setTarget((Resource) target);
+		} else if (target instanceof EObject) {
+			super.setTarget((EObject) target);
+		} else if (target instanceof ResourceSet) {
+			super.setTarget((ResourceSet) target);
+		}
+	}
+
+	@Override
+	public void unsetTarget(Notifier target) {
+		if (target instanceof Resource) {
+			super.unsetTarget((Resource) target);
+		} else if (target instanceof EObject) {
+			super.unsetTarget((EObject) target);
+		} else if (target instanceof ResourceSet) {
+			super.unsetTarget((ResourceSet) target);
+		}
+	}
+
+	@Override
+	protected void selfAdapt(Notification notification) {
+		Object notifier = notification.getNotifier();
+		if (notifier instanceof Resource) {
+			switch (notification.getFeatureID(Resource.class)) {
+			case Resource.RESOURCE__CONTENTS: {
+				if (!unloadedResources.contains(notifier)) {
+					switch (notification.getEventType()) {
+					case Notification.REMOVE: {
+						Resource resource = (Resource) notifier;
+						if (!resource.isLoaded()) {
+							EObject eObject = (EObject) notification.getOldValue();
+							unloadedEObjects.put(eObject, resource);
+							for (Iterator<EObject> i = EcoreUtil.getAllProperContents(eObject, false); i.hasNext();) {
+								unloadedEObjects.put(i.next(), resource);
+							}
+						}
+						break;
+					}
+					case Notification.REMOVE_MANY: {
+						Resource resource = (Resource) notifier;
+						if (!resource.isLoaded()) {
+							@SuppressWarnings("unchecked")
+							List<EObject> eObjects = (List<EObject>) notification.getOldValue();
+							for (Iterator<EObject> i = EcoreUtil.getAllProperContents(eObjects, false); i.hasNext();) {
+								unloadedEObjects.put(i.next(), resource);
+							}
+						}
+						break;
+					}
+					default: {
+						handleContainment(notification);
+						break;
+					}
+					}
+				}
+				break;
+			}
+			case Resource.RESOURCE__IS_LOADED: {
+				if (notification.getNewBooleanValue()) {
+					unloadedResources.remove(notifier);
+					for (Notifier child : ((Resource) notifier).getContents()) {
+						addAdapter(child);
+					}
+				} else {
+					unloadedResources.add((Resource) notifier);
+					for (Iterator<Map.Entry<EObject, Resource>> i = unloadedEObjects.entrySet().iterator(); i.hasNext();) {
+						Map.Entry<EObject, Resource> entry = i.next();
+						if (entry.getValue() == notifier) {
+							i.remove();
+							if (!resolve()) {
+								EObject eObject = entry.getKey();
+								Collection<EStructuralFeature.Setting> settings = inverseCrossReferencer.get(eObject);
+								if (settings != null) {
+									for (EStructuralFeature.Setting setting : settings) {
+										getInverseCrossReferencer().addProxy(eObject, setting.getEObject());
+									}
+								}
+							}
+						}
+					}
+				}
+				break;
+			}
+			}
+		} else if (notifier instanceof EObject) {
+			Object feature = notification.getFeature();
+			if (feature instanceof EReference) {
+				EReference reference = (EReference) feature;
+				if (reference.isContainment()) {
+					handleContainment(notification);
+				} else if (isIncluded(reference)) {
+					handleCrossReference(reference, notification);
+				}
+			}
+		} else if (notifier instanceof ResourceSet) {
+			if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES) {
+				handleContainment(notification);
+			}
+		}
+	}
+
+	@Override
+	protected CDOInverseCrossReferencer createInverseCrossReferencer() {
+		return new CDOInverseCrossReferencer();
+	}
+
+	protected CDOInverseCrossReferencer getInverseCrossReferencer() {
+		return (CDOInverseCrossReferencer) inverseCrossReferencer;
+	}
+
+	/**
+	 * An {@link org.eclipse.emf.ecore.util.ECrossReferenceAdapter.InverseCrossReferencer InverseCrossReferencer} with an
+	 * {@link #addProxy(EObject, EObject)} method that is visible to {@link CDOCrossReferenceAdapter}.
+	 *
+	 * @author Eike Stepper
+	 */
+	protected class CDOInverseCrossReferencer extends InverseCrossReferencer {
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		protected void addProxy(EObject proxy, EObject context) {
+			super.addProxy(proxy, context);
+		}
+	}
+}
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/PapyrusCDOResourceImpl.java b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/PapyrusCDOResourceImpl.java
index 443947a..d77915c 100644
--- a/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/PapyrusCDOResourceImpl.java
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.core/src/org/eclipse/papyrus/cdo/core/resource/PapyrusCDOResourceImpl.java
@@ -23,6 +23,9 @@
 import org.eclipse.emf.cdo.common.revision.CDORevision;
 import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.internal.cdo.view.CDOViewImpl.OptionsImpl;
+import org.eclipse.emf.spi.cdo.InternalCDOView;
+import org.eclipse.uml2.common.util.CacheAdapter;
 
 public class PapyrusCDOResourceImpl extends CDOResourceImpl {
 
@@ -39,10 +42,24 @@
 	 */
 	@Override
 	public void load(Map<?, ?> options) throws IOException {
-		super.load(options);
-		Boolean lazy = Boolean.getBoolean(ORG_ECLIPSE_PAPYRUS_LAZY_CDO_RESOURCE_LOADING);
-		if (!lazy) {
-			cdoPrefetch(CDORevision.DEPTH_INFINITE);
+		if (!isLoaded()) {
+			Boolean lazy = Boolean.getBoolean(ORG_ECLIPSE_PAPYRUS_LAZY_CDO_RESOURCE_LOADING);
+			if (!lazy) {
+				cdoPrefetch(CDORevision.DEPTH_INFINITE);
+
+				InternalCDOView cdoView = this.cdoView();
+
+				((OptionsImpl) cdoView.options()).setLockStatePrefetchEnabled(true);
+
+				CacheAdapter cacheAdapter = CacheAdapter.getInstance();
+				if (cacheAdapter.getClass() != NonLazyPapyrusCDOCacheAdapter.class) {
+					NonLazyPapyrusCDOCacheAdapter.register(new NonLazyPapyrusCDOCacheAdapter());
+				}
+
+			}
+
+			super.load(options);
+
 		}
 	}
 
diff --git a/cdo/bundles/org.eclipse.papyrus.cdo.ui/src/org/eclipse/papyrus/cdo/internal/ui/editors/DawnGraphicalEditorSupport.java b/cdo/bundles/org.eclipse.papyrus.cdo.ui/src/org/eclipse/papyrus/cdo/internal/ui/editors/DawnGraphicalEditorSupport.java
index 9689462..39c6da9 100755
--- a/cdo/bundles/org.eclipse.papyrus.cdo.ui/src/org/eclipse/papyrus/cdo/internal/ui/editors/DawnGraphicalEditorSupport.java
+++ b/cdo/bundles/org.eclipse.papyrus.cdo.ui/src/org/eclipse/papyrus/cdo/internal/ui/editors/DawnGraphicalEditorSupport.java
@@ -109,15 +109,17 @@
 
 		dawnSupport.registerListeners();
 
-		// find and process objects locked remotely
-		final Map<Object, DawnState> remoteLocks = getRemoteLocks(diagramEditor);
-		if (!remoteLocks.isEmpty()) {
-			// post for later because the editor isn't yet connected to its
-			// editor site in the workbench
-			UIUtil.later(new Runnable() {
 
-				@Override
-				public void run() {
+
+		// post for later because the editor isn't yet connected to its
+		// editor site in the workbench
+		UIUtil.later(new Runnable() {
+
+			@Override
+			public void run() {
+				// find and process objects locked remotely
+				final Map<Object, DawnState> remoteLocks = getRemoteLocks(diagramEditor);
+				if (!remoteLocks.isEmpty()) {
 					dawnSupport.handleRemoteLockChanges(remoteLocks);
 
 					for (Object next : remoteLocks.keySet()) {
@@ -127,8 +129,9 @@
 						CDOStateAdapter.setState(view, remoteLocks.get(next));
 					}
 				}
-			});
-		}
+			}
+		});
+
 	}
 
 	CDOView getCDOView() {
@@ -154,12 +157,12 @@
 		if (diagram != null) {
 			Iterator<EObject> iter = EcoreUtil.getAllProperContents(Collections.singleton(diagram), false);
 
-			while (iter.hasNext()) {
-				CDOObject next = CDOUtils.getCDOObject(iter.next());
-				if ((next != null) && CDOUtils.isLocked(next, true)) {
-					result.put(next, DawnState.LOCKED_REMOTELY);
-				}
-			}
+			// while (iter.hasNext()) {
+			// CDOObject next = CDOUtils.getCDOObject(iter.next());
+			// if ((next != null) && CDOUtils.isLocked(next, true)) {
+			// result.put(next, DawnState.LOCKED_REMOTELY);
+			// }
+			// }
 		}
 
 		return result;