Bug 477579 - Improve bundle activator start using asynchronous
initialization

- asynchronous initialization of TemplateStore
- create TemplateStoreInitializer
- asynchronous initialization of MemberOrderReferenceCache

Change-Id: Ib92289b930ad194bcf78aa28dfa893ae650e9417
Signed-off-by: Mark Hoffmann <mark.hoffmann@web.de>
diff --git a/org.eclipse.jdt.ui/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
index e171527..312bc20 100644
--- a/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.ui/META-INF/MANIFEST.MF
@@ -142,3 +142,5 @@
  com.ibm.icu;bundle-version="4.4.2",
  org.eclipse.equinox.bidi;bundle-version="[0.10.0,2.0.0)"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.osgi.util.function;version="1.1.0",
+ org.osgi.util.promise;version="1.1.0"
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPlugin.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPlugin.java
index 8d576a2..ea48d98 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPlugin.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPlugin.java
@@ -14,9 +14,11 @@
 package org.eclipse.jdt.internal.ui;
 
 import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.concurrent.Executors;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -24,6 +26,8 @@
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.util.promise.Promise;
+import org.osgi.util.promise.PromiseFactory;
 
 import org.eclipse.osgi.service.debug.DebugOptions;
 import org.eclipse.osgi.service.debug.DebugOptionsListener;
@@ -144,21 +148,13 @@
 	 * @since 3.0
 	 */
 	private static final String TEMPLATES_KEY= "org.eclipse.jdt.ui.text.custom_templates"; //$NON-NLS-1$
-	/**
-	 * The key to store customized code templates.
-	 * @since 3.0
-	 */
-	private static final String CODE_TEMPLATES_KEY= "org.eclipse.jdt.ui.text.custom_code_templates"; //$NON-NLS-1$
+
 	/**
 	 * The key to store whether the legacy templates have been migrated
 	 * @since 3.0
 	 */
 	private static final String TEMPLATES_MIGRATION_KEY= "org.eclipse.jdt.ui.text.templates_migrated"; //$NON-NLS-1$
-	/**
-	 * The key to store whether the legacy code templates have been migrated
-	 * @since 3.0
-	 */
-	private static final String CODE_TEMPLATES_MIGRATION_KEY= "org.eclipse.jdt.ui.text.code_templates_migrated"; //$NON-NLS-1$
+
 
 	public static boolean DEBUG_AST_PROVIDER;
 
@@ -209,6 +205,8 @@
 
 	private WorkingCopyManager fWorkingCopyManager;
 
+	private TemplateStoreInitializer fJavaPluginInitializer;
+
 	/**
 	 * @deprecated to avoid deprecation warning
 	 */
@@ -221,6 +219,7 @@
 	private ImageDescriptorRegistry fImageDescriptorRegistry;
 
 	private MembersOrderPreferenceCache fMembersOrderPreferenceCache;
+	private Promise<MembersOrderPreferenceCache> fMembersOrderPreferenceCachePromise;
 
 	private JavaEditorTextHoverDescriptor[] fJavaEditorTextHoverDescriptors;
 
@@ -305,11 +304,11 @@
 	}
 
 	public static Shell getActiveWorkbenchShell() {
-		 IWorkbenchWindow window= getActiveWorkbenchWindow();
-		 if (window != null) {
-		 	return window.getShell();
-		 }
-		 return null;
+		IWorkbenchWindow window= getActiveWorkbenchWindow();
+		if (window != null) {
+			return window.getShell();
+		}
+		return null;
 	}
 
 	public static String getPluginId() {
@@ -370,7 +369,7 @@
 
 	public JavaPlugin() {
 		super();
-		fgJavaPlugin = this;
+		fgJavaPlugin= this;
 	}
 
 	/* (non - Javadoc)
@@ -401,11 +400,10 @@
 		// by the MembersOrderPreferenceCache common logic
 		JavaManipulation.setPreferenceNodeId(getPluginId());
 
-		IPreferenceStore store= getPreferenceStore();
 
+		PromiseFactory promiseFactory= new PromiseFactory(Executors.newSingleThreadExecutor());
+		fMembersOrderPreferenceCachePromise= promiseFactory.submit(this::createMemberOrderPreferenceCache);
 		// must add here to guarantee that it is the first in the listener list
-		fMembersOrderPreferenceCache= new MembersOrderPreferenceCache();
-		fMembersOrderPreferenceCache.install(store);
 
 		FormatterProfileStore.checkCurrentOptionsVersion();
 
@@ -431,9 +429,7 @@
 
 			new InitializeAfterLoadJob().schedule(); // last call in start, see bug 191193
 		}
-
-		JavaManipulation.setCodeTemplateStore(getCodeTemplateStore());
-		JavaManipulation.setCodeTemplateContextRegistry(getCodeTemplateContextRegistry());
+		getTemplateStoreInitializer();
 	}
 
 	private void createOrUpdateWorkingSet(String name, String oldname, String label, final String id) {
@@ -450,7 +446,7 @@
 					workingSet.setLabel(label);
 			} else {
 				logErrorMessage("found existing workingset with name=\"" + name + "\" but id=\"" + workingSet.getId() + "\""); //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
-			}			
+			}
 		}
 		IWorkingSet oldWorkingSet= workingSetManager.getWorkingSet(oldname);
 		if (oldWorkingSet != null && id.equals(oldWorkingSet.getId())) {
@@ -570,8 +566,21 @@
 	}
 
 	/**
+	 * Creates an additional async initializer that loads the template store. Because the initialization
+	 * can be 'long running', it is not recommended to be executed in the bundle activator.
+	 *
+	 * @return the initializer instance
+	 */
+	private synchronized TemplateStoreInitializer getTemplateStoreInitializer() {
+		if (fJavaPluginInitializer == null) {
+			fJavaPluginInitializer= new TemplateStoreInitializer();
+			fJavaPluginInitializer.activate();
+		}
+		return fJavaPluginInitializer;
+	}
+
+	/**
 	 * Private deprecated method to avoid deprecation warnings
-	 * 
 	 * @return the deprecated buffer factory
 	 * @deprecated to avoid deprecation warnings
 	 */
@@ -629,7 +638,7 @@
 
 	/**
 	 * Returns the Java Core plug-in preferences.
-	 * 
+	 *
 	 * @return the Java Core plug-in preferences
 	 * @since 3.7
 	 */
@@ -651,11 +660,29 @@
 	}
 
 	public synchronized MembersOrderPreferenceCache getMemberOrderPreferenceCache() {
+		if (fMembersOrderPreferenceCache == null) {
+			try {
+				fMembersOrderPreferenceCache= fMembersOrderPreferenceCachePromise.getValue();
+			} catch (InvocationTargetException | InterruptedException e) {
+				if(e instanceof InterruptedException){
+					Thread.currentThread().interrupt();
+				}
+				log(e);
+				fMembersOrderPreferenceCache= createMemberOrderPreferenceCache();
+			}
+		}
 		// initialized on startup
 		return fMembersOrderPreferenceCache;
 	}
 
 
+	private MembersOrderPreferenceCache createMemberOrderPreferenceCache() {
+		IPreferenceStore store= getPreferenceStore();
+		MembersOrderPreferenceCache cache= new MembersOrderPreferenceCache();
+		cache.install(store);
+		return cache;
+	}
+
 	public synchronized TypeFilter getTypeFilter() {
 		if (fTypeFilter == null)
 			fTypeFilter= new TypeFilter();
@@ -818,7 +845,7 @@
 
 	/**
 	 * Private deprecated method to avoid deprecation warnings
-	 * 
+	 *
 	 * @return the deprecated template store
 	 * @deprecated to avoid deprecation warnings
 	 */
@@ -853,27 +880,17 @@
 	 */
 	public TemplateStore getCodeTemplateStore() {
 		if (fCodeTemplateStore == null) {
-			IPreferenceStore store= getPreferenceStore();
-			boolean alreadyMigrated= store.getBoolean(CODE_TEMPLATES_MIGRATION_KEY);
-			if (alreadyMigrated)
-				fCodeTemplateStore= new ContributionTemplateStore(getCodeTemplateContextRegistry(), store, CODE_TEMPLATES_KEY);
-			else {
-				fCodeTemplateStore= new CompatibilityTemplateStore(getCodeTemplateContextRegistry(), store, CODE_TEMPLATES_KEY, getOldCodeTemplateStoreInstance());
-				store.setValue(CODE_TEMPLATES_MIGRATION_KEY, true);
-			}
-
+			TemplateStoreInitializer initializer= getTemplateStoreInitializer();
+			Promise<TemplateStore> p= initializer.getCodeTemplateStore();
 			try {
-				fCodeTemplateStore.load();
-			} catch (IOException e) {
+				fCodeTemplateStore= p.getValue();
+			} catch (InvocationTargetException | InterruptedException e) {
+				if(e instanceof InterruptedException){
+					Thread.currentThread().interrupt();
+				}
+				fCodeTemplateStore= initializer.loadCodeTemplateStore();
 				log(e);
 			}
-
-			fCodeTemplateStore.startListeningForPreferenceChanges();
-
-			// compatibility / bug fixing code for duplicated templates
-			// TODO remove for 3.0
-			CompatibilityTemplateStore.pruneDuplicates(fCodeTemplateStore, true);
-
 		}
 
 		return fCodeTemplateStore;
@@ -881,12 +898,12 @@
 
 	/**
 	 * Private deprecated method to avoid deprecation warnings
-	 * 
+	 *
 	 * @return the deprecated code template store
 	 * @deprecated to avoid deprecation warnings
 	 */
 	@Deprecated
-	private org.eclipse.jdt.internal.corext.template.java.CodeTemplates getOldCodeTemplateStoreInstance() {
+	org.eclipse.jdt.internal.corext.template.java.CodeTemplates getOldCodeTemplateStoreInstance() {
 		return org.eclipse.jdt.internal.corext.template.java.CodeTemplates.getInstance();
 	}
 
@@ -913,7 +930,7 @@
 
 	/**
 	 * Flushes the instance scope of this plug-in.
-	 * 
+	 *
 	 * @since 3.7
 	 */
 	public static void flushInstanceScope() {
@@ -927,7 +944,7 @@
 	/**
 	 * Returns the registry of the extensions to the
 	 * <code>org.eclipse.jdt.ui.javaFoldingStructureProvider</code> extension point.
-	 * 
+	 *
 	 * @return the registry of contributed <code>IJavaFoldingStructureProvider</code>
 	 * @since 3.0
 	 */
@@ -1022,7 +1039,7 @@
 	 * Returns the content assist additional info focus affordance string.
 	 *
 	 * @return the affordance string which is <code>null</code> if the
-	 *			preference is disabled
+	 *						preference is disabled
 	 *
 	 * @see EditorsUI#getTooltipAffordanceString()
 	 * @since 3.4
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/TemplateStoreInitializer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/TemplateStoreInitializer.java
new file mode 100644
index 0000000..9734183
--- /dev/null
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/TemplateStoreInitializer.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * 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
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui;
+
+import java.io.IOException;
+import java.util.concurrent.Executors;
+
+import org.osgi.util.promise.Promise;
+import org.osgi.util.promise.PromiseFactory;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.jface.text.templates.persistence.TemplateStore;
+
+import org.eclipse.ui.editors.text.templates.ContributionTemplateStore;
+
+import org.eclipse.jdt.core.manipulation.JavaManipulation;
+
+/**
+ * Initializer for the template store.
+ *
+ */
+public class TemplateStoreInitializer {
+
+	/**
+	 * The key to store customized code templates.
+	 */
+	private static final String CODE_TEMPLATES_KEY= "org.eclipse.jdt.ui.text.custom_code_templates"; //$NON-NLS-1$
+
+	/**
+	 * The key to store whether the legacy code templates have been migrated
+	 */
+	private static final String CODE_TEMPLATES_MIGRATION_KEY= "org.eclipse.jdt.ui.text.code_templates_migrated"; //$NON-NLS-1$
+
+	private volatile Promise<TemplateStore> fCodeTemplateStore;
+
+	public void activate() {
+		fCodeTemplateStore = loadTemplateStoreAsync();
+	}
+
+	/**
+	 * Returns the promise for the template store
+	 * @return the promise for the template store
+	 */
+	public Promise<TemplateStore> getCodeTemplateStore() {
+		return fCodeTemplateStore;
+	}
+
+	/**
+	 * Loads the template store asynchronous. It returns a promise that is either resolved
+	 * or not, when an error occurs. In that case
+	 * @return a promise of the template store, that is either resolved or not ()
+	 */
+	private Promise<TemplateStore> loadTemplateStoreAsync() {
+		PromiseFactory pf = new PromiseFactory(Executors.newSingleThreadExecutor());
+		Promise<TemplateStore> promise = pf.submit(this::loadCodeTemplateStore);
+		// set all template store data, when promise is resolved and logs, if an exception occurs
+		return promise.onFailure(JavaPlugin::log);
+	}
+
+	/**
+	 * Sets the template store information
+	 * @param templateStore the template store instance
+	 */
+	private void setTemplateStore(TemplateStore templateStore) {
+		JavaManipulation.setCodeTemplateStore(templateStore);
+		JavaManipulation.setCodeTemplateContextRegistry(JavaPlugin.getDefault().getCodeTemplateContextRegistry());
+	}
+
+	/**
+	 * Loads the template store data
+	 * @return the template store
+	 */
+	public TemplateStore loadCodeTemplateStore() {
+		JavaPlugin javaPlugin= JavaPlugin.getDefault();
+		IPreferenceStore store= javaPlugin.getPreferenceStore();
+		boolean alreadyMigrated= store.getBoolean(CODE_TEMPLATES_MIGRATION_KEY);
+		TemplateStore templateStore;
+		if (alreadyMigrated) {
+			templateStore= new ContributionTemplateStore(javaPlugin.getCodeTemplateContextRegistry(), store, CODE_TEMPLATES_KEY);
+		} else {
+			templateStore= new CompatibilityTemplateStore(javaPlugin.getCodeTemplateContextRegistry(), store, CODE_TEMPLATES_KEY, javaPlugin.getOldCodeTemplateStoreInstance());
+			store.setValue(CODE_TEMPLATES_MIGRATION_KEY, true);
+		}
+
+		try {
+			templateStore.load();
+		} catch (IOException e) {
+			JavaPlugin.log(e);
+		}
+
+		templateStore.startListeningForPreferenceChanges();
+
+		// compatibility / bug fixing code for duplicated templates
+		// TODO remove for 3.0
+		CompatibilityTemplateStore.pruneDuplicates(templateStore, true);
+		setTemplateStore(templateStore);
+		return templateStore;
+	}
+
+}