Bug 564062 - Refactor ContentTypeManager.DEBUGGING

The ContentTypeManager DEBUGGING field is initialised as soon as the class is
loaded. This may be too eager since it may not be needed until later.  By
moving the boolean into a static inner class, we can defer its initialisation
until the first time it is used in a code path.

We can also take advantage of Equinox's new `ServiceCaller` which allows for
single-shot access of services.

Change-Id: Iea9b52894f796c8b5cc6ad1fbdf248aafa745b4f
Signed-off-by: Alex Blewitt <alex.blewitt@gmail.com>
diff --git a/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF
index 795ed5e..5180ff3 100644
--- a/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF
@@ -7,7 +7,7 @@
 Bundle-Localization: plugin
 Require-Bundle: org.eclipse.equinox.preferences;bundle-version="[3.2.0,4.0.0)",
  org.eclipse.equinox.registry;bundle-version="[3.2.0,4.0.0)",
- org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
+ org.eclipse.equinox.common;bundle-version="[3.13.0,4.0.0)"
 Export-Package: org.eclipse.core.internal.content;x-internal:=true,
  org.eclipse.core.runtime.content
 Bundle-ActivationPolicy: lazy
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java
index c3ac68e..7759cf3 100644
--- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java
+++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java
@@ -192,7 +192,7 @@
 			throw llioe.getActualException();
 		} catch (IOException ioe) {
 			// bugs 67841/ 62443  - non-low level IOException should be "ignored"
-			if (ContentTypeManager.DEBUGGING) {
+			if (ContentTypeManager.DebuggingHolder.DEBUGGING) {
 				String message = NLS.bind(ContentMessages.content_errorReadingContents, id);
 				ContentType.log(message, ioe);
 			}
@@ -630,7 +630,7 @@
 
 	void setValidation(byte validation) {
 		this.validation = validation;
-		if (ContentTypeManager.DEBUGGING)
+		if (ContentTypeManager.DebuggingHolder.DEBUGGING)
 			ContentMessages.message("Validating " + this + ": " + getValidationString(validation)); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java
index 496d569..85c237a 100644
--- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java
+++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java
@@ -19,7 +19,8 @@
 import org.eclipse.core.runtime.*;
 import org.eclipse.core.runtime.content.IContentDescription;
 import org.eclipse.core.runtime.content.IContentType;
-import org.eclipse.core.runtime.preferences.*;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.service.prefs.BackingStoreException;
 
@@ -165,7 +166,7 @@
 				String propertyKey = propertyCE.getAttribute("name"); //$NON-NLS-1$
 				QualifiedName qualifiedKey = parseQualifiedName(namespace, propertyKey);
 				if (qualifiedKey == null) {
-					if (ContentTypeManager.DEBUGGING) {
+					if (ContentTypeManager.DebuggingHolder.DEBUGGING) {
 						String message = NLS.bind(ContentMessages.content_invalidProperty, propertyKey, getUniqueId(namespace, simpleId));
 						ContentType.log(message, null);
 					}
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java
index e173b5f..37ae65e 100644
--- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java
+++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java
@@ -660,7 +660,7 @@
 			if (ensureValid(type))
 				associate(type);
 		}
-		if (ContentTypeManager.DEBUGGING)
+		if (ContentTypeManager.DebuggingHolder.DEBUGGING)
 			for (IContentType iContentType : contentTypes.values()) {
 				ContentType type = (ContentType) iContentType;
 				if (!type.isValid())
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java
index 89214da..013835e 100644
--- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java
+++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java
@@ -23,7 +23,6 @@
 import org.eclipse.core.runtime.preferences.*;
 import org.eclipse.osgi.service.debug.DebugOptions;
 import org.eclipse.osgi.util.NLS;
-import org.osgi.framework.*;
 import org.osgi.service.prefs.BackingStoreException;
 
 public class ContentTypeManager extends ContentTypeMatcher implements IContentTypeManager {
@@ -47,21 +46,24 @@
 	public static final int BLOCK_SIZE = 0x400;
 	public static final String CONTENT_TYPE_PREF_NODE = IContentConstants.RUNTIME_NAME + IPath.SEPARATOR + "content-types"; //$NON-NLS-1$
 	private static final String OPTION_DEBUG_CONTENT_TYPES = "org.eclipse.core.contenttype/debug"; //$NON-NLS-1$
-	static final boolean DEBUGGING;
 	private ContentTypeCatalog catalog;
 	private int catalogGeneration;
 
-	static {
-		boolean debugging = false;
-		Bundle bundle = FrameworkUtil.getBundle(ContentTypeManager.class);
-		BundleContext context = bundle == null ? null : bundle.getBundleContext();
-		ServiceReference<DebugOptions> reference = context.getServiceReference(DebugOptions.class);
-		DebugOptions debugOptions = reference == null ? null : context.getService(reference);
-		if (debugOptions != null) {
-			debugging = debugOptions.getBooleanOption(OPTION_DEBUG_CONTENT_TYPES, false);
-			context.ungetService(reference);
+	/**
+	 * The DebuggingHolder contains a single boolean with a
+	 * {@link DebuggingHolder#DEBUGGING} field. By providing a class that wraps it,
+	 * it will defer the lookup of this field until it is needed.
+	 */
+	static class DebuggingHolder {
+		static final boolean DEBUGGING;
+		static {
+			boolean[] debugging = { false };
+			ServiceCaller.callOnce(DebuggingHolder.class, DebugOptions.class,
+					(debugOptions) -> {
+						debugging[0] = debugOptions.getBooleanOption(OPTION_DEBUG_CONTENT_TYPES, false);
+					});
+			DEBUGGING = debugging[0];
 		}
-		DEBUGGING = debugging;
 	}
 
 	/**
@@ -196,7 +198,7 @@
 	 * Causes a new catalog to be built afresh next time an API call is made.
 	 */
 	synchronized void invalidate() {
-		if (ContentTypeManager.DEBUGGING && catalog != null)
+		if (DebuggingHolder.DEBUGGING && catalog != null)
 			ContentMessages.message("Registry discarded"); //$NON-NLS-1$
 		catalog = null;
 	}