Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/internal/debug/FrameworkDebugOptions.java')
-rw-r--r--bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/internal/debug/FrameworkDebugOptions.java508
1 files changed, 508 insertions, 0 deletions
diff --git a/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/internal/debug/FrameworkDebugOptions.java b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/internal/debug/FrameworkDebugOptions.java
new file mode 100644
index 000000000..d686ac843
--- /dev/null
+++ b/bundles/org.eclipse.osgi/supplement/src/org/eclipse/osgi/internal/debug/FrameworkDebugOptions.java
@@ -0,0 +1,508 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2010 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.debug;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.service.debug.*;
+import org.osgi.framework.*;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * The DebugOptions implementation class that allows accessing the list of debug options specified
+ * for the application as well as creating {@link DebugTrace} objects for the purpose of having
+ * dynamic enablement of debug tracing.
+ *
+ * @since 3.1
+ */
+public class FrameworkDebugOptions implements DebugOptions, ServiceTrackerCustomizer<DebugOptionsListener, DebugOptionsListener> {
+
+ private static final String OSGI_DEBUG = "osgi.debug"; //$NON-NLS-1$
+ private static final String OSGI_DEBUG_VERBOSE = "osgi.debug.verbose"; //$NON-NLS-1$
+ public static final String PROP_TRACEFILE = "osgi.tracefile"; //$NON-NLS-1$
+ /** monitor used to lock the options maps */
+ private final Object lock = new Object();
+ /** A current map of all the options with values set */
+ private Properties options = null;
+ /** A map of all the disabled options with values set at the time debug was disabled */
+ private Properties disabledOptions = null;
+ /** The singleton object of this class */
+ private static FrameworkDebugOptions singleton = null;
+ /** The default name of the .options file if loading when the -debug command-line argument is used */
+ private static final String OPTIONS = ".options"; //$NON-NLS-1$
+ /** A cache of all of the bundles <code>DebugTrace</code> in the format <key,value> --> <bundle name, DebugTrace> */
+ protected final static Map<String, DebugTrace> debugTraceCache = new HashMap<String, DebugTrace>();
+ /** The File object to store messages. This value may be null. */
+ protected File outFile = null;
+ /** Is verbose debugging enabled? Changing this value causes a new tracing session to start. */
+ protected boolean verboseDebug = true;
+ private volatile BundleContext context;
+ private volatile ServiceTracker<DebugOptionsListener, DebugOptionsListener> listenerTracker;
+
+ /**
+ * Internal constructor to create a <code>FrameworkDebugOptions</code> singleton object.
+ */
+ private FrameworkDebugOptions() {
+ // check if verbose debugging was set during initialization. This needs to be set even if debugging is disabled
+ this.verboseDebug = Boolean.valueOf(FrameworkProperties.getProperty(OSGI_DEBUG_VERBOSE, Boolean.TRUE.toString())).booleanValue();
+ // if no debug option was specified, don't even bother to try.
+ // Must ensure that the options slot is null as this is the signal to the
+ // platform that debugging is not enabled.
+ String debugOptionsFilename = FrameworkProperties.getProperty(OSGI_DEBUG);
+ if (debugOptionsFilename == null)
+ return;
+ options = new Properties();
+ URL optionsFile;
+ if (debugOptionsFilename.length() == 0) {
+ // default options location is user.dir (install location may be r/o so
+ // is not a good candidate for a trace options that need to be updatable by
+ // by the user)
+ String userDir = FrameworkProperties.getProperty("user.dir").replace(File.separatorChar, '/'); //$NON-NLS-1$
+ if (!userDir.endsWith("/")) //$NON-NLS-1$
+ userDir += "/"; //$NON-NLS-1$
+ debugOptionsFilename = new File(userDir, OPTIONS).toString();
+ }
+ optionsFile = buildURL(debugOptionsFilename, false);
+ if (optionsFile == null) {
+ System.out.println("Unable to construct URL for options file: " + debugOptionsFilename); //$NON-NLS-1$
+ return;
+ }
+ System.out.print("Debug options:\n " + optionsFile.toExternalForm()); //$NON-NLS-1$
+ try {
+ InputStream input = optionsFile.openStream();
+ try {
+ options.load(input);
+ System.out.println(" loaded"); //$NON-NLS-1$
+ } finally {
+ input.close();
+ }
+ } catch (FileNotFoundException e) {
+ System.out.println(" not found"); //$NON-NLS-1$
+ } catch (IOException e) {
+ System.out.println(" did not parse"); //$NON-NLS-1$
+ e.printStackTrace(System.out);
+ }
+ // trim off all the blanks since properties files don't do that.
+ for (Object key : options.keySet()) {
+ options.put(key, ((String) options.get(key)).trim());
+ }
+ }
+
+ public void start(BundleContext bc) {
+ this.context = bc;
+ listenerTracker = new ServiceTracker<DebugOptionsListener, DebugOptionsListener>(bc, DebugOptionsListener.class.getName(), this);
+ listenerTracker.open();
+ }
+
+ public void stop(BundleContext bc) {
+ listenerTracker.close();
+ listenerTracker = null;
+ this.context = null;
+ }
+
+ /**
+ * Returns the singleton instance of <code>FrameworkDebugOptions</code>.
+ * @return the instance of <code>FrameworkDebugOptions</code>
+ */
+ public static FrameworkDebugOptions getDefault() {
+
+ if (FrameworkDebugOptions.singleton == null) {
+ FrameworkDebugOptions.singleton = new FrameworkDebugOptions();
+ }
+ return FrameworkDebugOptions.singleton;
+ }
+
+ @SuppressWarnings("deprecation")
+ private static URL buildURL(String spec, boolean trailingSlash) {
+ if (spec == null)
+ return null;
+ boolean isFile = spec.startsWith("file:"); //$NON-NLS-1$
+ try {
+ if (isFile)
+ return adjustTrailingSlash(new File(spec.substring(5)).toURL(), trailingSlash);
+ return new URL(spec);
+ } catch (MalformedURLException e) {
+ // if we failed and it is a file spec, there is nothing more we can do
+ // otherwise, try to make the spec into a file URL.
+ if (isFile)
+ return null;
+ try {
+ return adjustTrailingSlash(new File(spec).toURL(), trailingSlash);
+ } catch (MalformedURLException e1) {
+ return null;
+ }
+ }
+ }
+
+ private static URL adjustTrailingSlash(URL url, boolean trailingSlash) throws MalformedURLException {
+ String file = url.getFile();
+ if (trailingSlash == (file.endsWith("/"))) //$NON-NLS-1$
+ return url;
+ file = trailingSlash ? file + "/" : file.substring(0, file.length() - 1); //$NON-NLS-1$
+ return new URL(url.getProtocol(), url.getHost(), file);
+ }
+
+ /**
+ * @see DebugOptions#getBooleanOption(String, boolean)
+ */
+ public boolean getBooleanOption(String option, boolean defaultValue) {
+ String optionValue = getOption(option);
+ return optionValue != null ? optionValue.equalsIgnoreCase("true") : defaultValue; //$NON-NLS-1$
+ }
+
+ /**
+ * @see DebugOptions#getOption(String)
+ */
+ public String getOption(String option) {
+ return getOption(option, null);
+ }
+
+ /**
+ * @see DebugOptions#getOption(String, String)
+ */
+ public String getOption(String option, String defaultValue) {
+ synchronized (lock) {
+ if (options != null) {
+ return options.getProperty(option, defaultValue);
+ }
+ }
+ return defaultValue;
+ }
+
+ /**
+ * @see DebugOptions#getIntegerOption(String, int)
+ */
+ public int getIntegerOption(String option, int defaultValue) {
+ String value = getOption(option);
+ try {
+ return value == null ? defaultValue : Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Map<String, String> getOptions() {
+ Map<String, String> snapShot = new HashMap<String, String>();
+ synchronized (lock) {
+ if (options != null)
+ snapShot.putAll((Map) options);
+ else if (disabledOptions != null)
+ snapShot.putAll((Map) disabledOptions);
+ }
+ return snapShot;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#getAllOptions()
+ */
+ String[] getAllOptions() {
+
+ String[] optionsArray = null;
+ synchronized (lock) {
+ if (options != null) {
+ optionsArray = new String[options.size()];
+ final Iterator<Map.Entry<Object, Object>> entrySetIterator = options.entrySet().iterator();
+ int i = 0;
+ while (entrySetIterator.hasNext()) {
+ Map.Entry<Object, Object> entry = entrySetIterator.next();
+ optionsArray[i] = ((String) entry.getKey()) + "=" + ((String) entry.getValue()); //$NON-NLS-1$
+ i++;
+ }
+ }
+ }
+ if (optionsArray == null) {
+ optionsArray = new String[1]; // TODO this is strange; null is the only element so we can print null in writeSession
+ }
+ return optionsArray;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#removeOption(java.lang.String)
+ */
+ public void removeOption(String option) {
+ if (option == null)
+ return;
+ String fireChangedEvent = null;
+ synchronized (lock) {
+ if (options != null && options.remove(option) != null) {
+ fireChangedEvent = getSymbolicName(option);
+ }
+ }
+ // Send the options change event outside the sync block
+ if (fireChangedEvent != null) {
+ optionsChanged(fireChangedEvent);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#setOption(java.lang.String, java.lang.String)
+ */
+ public void setOption(String option, String value) {
+
+ if (option == null || value == null) {
+ throw new IllegalArgumentException("The option and value must not be null."); //$NON-NLS-1$
+ }
+ String fireChangedEvent = null;
+ value = value != null ? value.trim() : null;
+ synchronized (lock) {
+ if (options != null) {
+ // get the current value
+ String currentValue = options.getProperty(option);
+
+ if (currentValue != null) {
+ if (!currentValue.equals(value)) {
+ fireChangedEvent = getSymbolicName(option);
+ }
+ } else {
+ if (value != null) {
+ fireChangedEvent = getSymbolicName(option);
+ }
+ }
+ if (fireChangedEvent != null) {
+ options.put(option, value);
+ }
+ }
+ }
+ // Send the options change event outside the sync block
+ if (fireChangedEvent != null) {
+ optionsChanged(fireChangedEvent);
+ }
+ }
+
+ private String getSymbolicName(String option) {
+ int firstSlashIndex = option.indexOf("/"); //$NON-NLS-1$
+ if (firstSlashIndex > 0)
+ return option.substring(0, firstSlashIndex);
+ return null;
+ }
+
+ @SuppressWarnings("cast")
+ public void setOptions(Map<String, String> ops) {
+ if (ops == null)
+ throw new IllegalArgumentException("The options must not be null."); //$NON-NLS-1$
+ Properties newOptions = new Properties();
+ for (Iterator<Map.Entry<String, String>> entries = ops.entrySet().iterator(); entries.hasNext();) {
+ Map.Entry<String, String> entry = entries.next();
+ if (!(entry.getKey() instanceof String) || !(entry.getValue() instanceof String))
+ throw new IllegalArgumentException("Option keys and values must be of type String: " + entry.getKey() + "=" + entry.getValue()); //$NON-NLS-1$ //$NON-NLS-2$
+ newOptions.put(entry.getKey(), entry.getValue().trim());
+ }
+ Set<String> fireChangesTo = null;
+
+ synchronized (lock) {
+ if (options == null) {
+ disabledOptions = newOptions;
+ // no events to fire
+ return;
+ }
+ fireChangesTo = new HashSet<String>();
+ // first check for removals
+ for (Iterator<Object> keys = options.keySet().iterator(); keys.hasNext();) {
+ String key = (String) keys.next();
+ if (!newOptions.containsKey(key)) {
+ String symbolicName = getSymbolicName(key);
+ if (symbolicName != null)
+ fireChangesTo.add(symbolicName);
+ }
+ }
+ // now check for changes to existing values
+ for (Iterator<Map.Entry<Object, Object>> newEntries = newOptions.entrySet().iterator(); newEntries.hasNext();) {
+ Map.Entry<Object, Object> entry = newEntries.next();
+ String existingValue = (String) options.get(entry.getKey());
+ if (!entry.getValue().equals(existingValue)) {
+ String symbolicName = getSymbolicName((String) entry.getKey());
+ if (symbolicName != null)
+ fireChangesTo.add(symbolicName);
+ }
+ }
+ // finally set the actual options
+ options = newOptions;
+ }
+ if (fireChangesTo != null)
+ for (Iterator<String> iChanges = fireChangesTo.iterator(); iChanges.hasNext();)
+ optionsChanged(iChanges.next());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#isDebugEnabled()
+ */
+ public boolean isDebugEnabled() {
+ synchronized (lock) {
+ return options != null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#setDebugEnabled()
+ */
+ public void setDebugEnabled(boolean enabled) {
+ boolean fireChangedEvent = false;
+ synchronized (lock) {
+ if (enabled) {
+ if (options != null)
+ return;
+ // notify the trace that a new session is started
+ EclipseDebugTrace.newSession = true;
+
+ // enable platform debugging - there is no .options file
+ FrameworkProperties.setProperty(OSGI_DEBUG, ""); //$NON-NLS-1$
+ if (disabledOptions != null) {
+ options = disabledOptions;
+ disabledOptions = null;
+ // fire changed event to indicate some options were re-enabled
+ fireChangedEvent = true;
+ } else {
+ options = new Properties();
+ }
+ } else {
+ if (options == null)
+ return;
+ // disable platform debugging.
+ FrameworkProperties.clearProperty(OSGI_DEBUG);
+ if (options.size() > 0) {
+ // Save the current options off in case debug is re-enabled
+ disabledOptions = options;
+ // fire changed event to indicate some options were disabled
+ fireChangedEvent = true;
+ }
+ options = null;
+ }
+ }
+ if (fireChangedEvent) {
+ // (Bug 300911) need to fire event to listeners that options have been disabled
+ optionsChanged("*"); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#createTrace(java.lang.String)
+ */
+ public final DebugTrace newDebugTrace(String bundleSymbolicName) {
+
+ return this.newDebugTrace(bundleSymbolicName, null);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#createTrace(java.lang.String, java.lang.Class)
+ */
+ public final DebugTrace newDebugTrace(String bundleSymbolicName, Class<?> traceEntryClass) {
+
+ DebugTrace debugTrace = null;
+ synchronized (FrameworkDebugOptions.debugTraceCache) {
+ debugTrace = FrameworkDebugOptions.debugTraceCache.get(bundleSymbolicName);
+ if (debugTrace == null) {
+ debugTrace = new EclipseDebugTrace(bundleSymbolicName, FrameworkDebugOptions.singleton, traceEntryClass);
+ FrameworkDebugOptions.debugTraceCache.put(bundleSymbolicName, debugTrace);
+ }
+ }
+ return debugTrace;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#getFile()
+ */
+ public final File getFile() {
+
+ return this.outFile;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#setFile(java.io.File)
+ */
+ public synchronized void setFile(final File traceFile) {
+
+ this.outFile = traceFile;
+ FrameworkProperties.setProperty(PROP_TRACEFILE, this.outFile.getAbsolutePath());
+ // the file changed so start a new session
+ EclipseDebugTrace.newSession = true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#getVerbose()
+ */
+ boolean isVerbose() {
+
+ return this.verboseDebug;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.osgi.service.debug.DebugOptions#setVerbose(boolean)
+ */
+ public synchronized void setVerbose(final boolean verbose) {
+
+ this.verboseDebug = verbose;
+ // the verbose flag changed so start a new session
+ EclipseDebugTrace.newSession = true;
+ }
+
+ /**
+ * Notifies the trace listener for the specified bundle that its option-path has changed.
+ * @param bundleSymbolicName The bundle of the owning trace listener to notify.
+ */
+ private void optionsChanged(String bundleSymbolicName) {
+ // use osgi services to get the listeners
+ BundleContext bc = context;
+ if (bc == null)
+ return;
+ // do not use the service tracker because that is only used to call all listeners initially when they are registered
+ // here we only want the services with the specified name.
+ ServiceReference<?>[] listenerRefs = null;
+ try {
+ listenerRefs = bc.getServiceReferences(DebugOptionsListener.class.getName(), "(" + DebugOptions.LISTENER_SYMBOLICNAME + "=" + bundleSymbolicName + ")"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ } catch (InvalidSyntaxException e) {
+ // consider logging; should not happen
+ }
+ if (listenerRefs == null)
+ return;
+ for (int i = 0; i < listenerRefs.length; i++) {
+ DebugOptionsListener service = (DebugOptionsListener) bc.getService(listenerRefs[i]);
+ if (service == null)
+ continue;
+ try {
+ service.optionsChanged(this);
+ } catch (Throwable t) {
+ // TODO consider logging
+ } finally {
+ bc.ungetService(listenerRefs[i]);
+ }
+ }
+ }
+
+ public DebugOptionsListener addingService(ServiceReference<DebugOptionsListener> reference) {
+ DebugOptionsListener listener = context.getService(reference);
+ listener.optionsChanged(this);
+ return listener;
+ }
+
+ public void modifiedService(ServiceReference<DebugOptionsListener> reference, DebugOptionsListener service) {
+ // nothing
+ }
+
+ public void removedService(ServiceReference<DebugOptionsListener> reference, DebugOptionsListener service) {
+ context.ungetService(reference);
+ }
+} \ No newline at end of file

Back to the top