diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src')
11 files changed, 1009 insertions, 0 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/activator/CoreBundleActivator.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/activator/CoreBundleActivator.java new file mode 100644 index 000000000..ec97a63bf --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/activator/CoreBundleActivator.java @@ -0,0 +1,73 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.log.core.activator;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.tcf.te.runtime.tracing.TraceHandler;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class CoreBundleActivator extends Plugin {
+ // The shared instance
+ private static CoreBundleActivator plugin;
+ // The trace handler instance
+ private static TraceHandler traceHandler;
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static CoreBundleActivator getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Convenience method which returns the unique identifier of this plugin.
+ */
+ public static String getUniqueIdentifier() {
+ if (getDefault() != null && getDefault().getBundle() != null) {
+ return getDefault().getBundle().getSymbolicName();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the bundles trace handler.
+ *
+ * @return The bundles trace handler.
+ */
+ public static TraceHandler getTraceHandler() {
+ if (traceHandler == null) {
+ traceHandler = new TraceHandler(getUniqueIdentifier());
+ }
+ return traceHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/IPreferenceKeys.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/IPreferenceKeys.java new file mode 100644 index 000000000..226a31052 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/IPreferenceKeys.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.interfaces; + + +/** + * TCF logging bundle preference key identifiers. + */ +public interface IPreferenceKeys { + /** + * Common prefix for all core preference keys + */ + public final String PREFIX = "tcf.log.core."; //$NON-NLS-1$ + + /** + * If set to <code>true</code>, back-end communication is logged. + */ + public final String PREF_LOGGING_ENABLED = PREFIX + "enabled"; //$NON-NLS-1$ + + /** + * If set to <code>true</code>, locator heart beat events are logged. + */ + public final String PREF_SHOW_HEARTBEATS = PREFIX + "show.heartbeats"; //$NON-NLS-1$ + + /** + * The maximum number of bytes the log files are allowed to grow to, in bytes. + * Defaults to 5MB. + */ + public final String PREF_MAX_FILE_SIZE = PREFIX + "limits.fileSize"; //$NON-NLS-1$ + + /** + * The maximum number of files kept in the cycle. + * Defaults to 5. + */ + public final String PREF_MAX_FILES_IN_CYCLE = PREFIX + "limits.inCycle"; //$NON-NLS-1$ +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/ITracing.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/ITracing.java new file mode 100644 index 000000000..afef953b7 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/interfaces/ITracing.java @@ -0,0 +1,21 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.log.core.interfaces;
+
+/**
+ * TCF logging tracing identifiers.
+ */
+public interface ITracing {
+
+ /**
+ * If enabled, prints information about the logging channel trace listener method invocations.
+ */
+ public static String ID_TRACE_CHANNEL_TRACE_LISTENER = "trace/channelTraceListener"; //$NON-NLS-1$
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/LogManager.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/LogManager.java new file mode 100644 index 000000000..810da446e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/LogManager.java @@ -0,0 +1,385 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Wind River Systems, Inc. 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.tcf.log.core.internal;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.protocol.IChannel;
+import org.eclipse.tcf.protocol.IPeer;
+import org.eclipse.tcf.protocol.Protocol;
+import org.eclipse.tcf.te.tcf.log.core.activator.CoreBundleActivator;
+import org.eclipse.tcf.te.tcf.log.core.interfaces.IPreferenceKeys;
+import org.eclipse.tcf.te.tcf.log.core.internal.listener.ChannelStateChangeListener;
+import org.eclipse.tcf.te.tcf.log.core.internal.listener.ChannelTraceListener;
+import org.eclipse.tcf.te.tcf.log.core.internal.nls.Messages;
+import org.eclipse.tcf.te.tcf.core.Tcf;
+import org.eclipse.tcf.te.tcf.core.interfaces.listeners.IChannelStateChangeListener;
+import org.eclipse.tcf.te.tcf.core.interfaces.listeners.IProtocolStateChangeListener;
+
+
+/**
+ * TCF logging log manager implementation.
+ */
+public final class LogManager implements IProtocolStateChangeListener {
+ // Reference to the channel state change listener
+ private IChannelStateChangeListener channelStateChangeListener;
+
+ // Maps file writer per log file base name
+ private final Map<String, FileWriter> fileWriterMap = new HashMap<String, FileWriter>();
+
+ // Maximum log file size in bytes
+ private long maxFileSize;
+ // Maximum number of files in cycle
+ private int maxInCycle;
+
+ /*
+ * Thread save singleton instance creation.
+ */
+ private static class LazyInstance {
+ public static LogManager instance = new LogManager();
+ }
+
+ /**
+ * Constructor.
+ */
+ /* default */ LogManager() {
+ super();
+
+ // initialize from preferences
+ initializeFromPreferences();
+ }
+
+ /**
+ * Returns the singleton instance.
+ */
+ public static LogManager getInstance() {
+ return LazyInstance.instance;
+ }
+
+ /**
+ * Dispose the log manager instance.
+ */
+ public void dispose() {
+ String message = NLS.bind(Messages.ChannelTraceListener_logManagerDispose_message,
+ ChannelTraceListener.DATE_FORMAT.format(new Date(System.currentTimeMillis())));
+ for (FileWriter writer : fileWriterMap.values()) {
+ try {
+ writer.write(message);
+ writer.write("\n"); //$NON-NLS-1$
+ } catch (IOException e) {
+ /* ignored on purpose */
+ } finally {
+ try {
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ /* ignored on purpose */
+ }
+ }
+ }
+ fileWriterMap.clear();
+ }
+
+ /**
+ * Initialize the log manager based on the current
+ * preference settings
+ */
+ private void initializeFromPreferences() {
+ String fileSize = Platform.getPreferencesService().getString(CoreBundleActivator.getUniqueIdentifier(),
+ IPreferenceKeys.PREF_MAX_FILE_SIZE, "5M", null); //$NON-NLS-1$
+
+ try {
+ // If the last character is either K, M or G -> convert to bytes
+ char lastChar = fileSize.toUpperCase().charAt(fileSize.length() - 1);
+ if ('K' == lastChar || 'M' == lastChar || 'G' == lastChar) {
+ maxFileSize = Long.parseLong(fileSize.substring(0, fileSize.length() - 1));
+ switch (lastChar) {
+ case 'K':
+ maxFileSize = maxFileSize * 1024;
+ break;
+ case 'M':
+ maxFileSize = maxFileSize * 1024 * 1024;
+ break;
+ case 'G':
+ maxFileSize = maxFileSize * 1024 * 1024 * 1024;
+ break;
+ }
+ } else {
+ maxFileSize = Long.parseLong(fileSize);
+ }
+ } catch (NumberFormatException e) {
+ maxFileSize = 5242880L;
+ }
+
+ maxInCycle = Platform.getPreferencesService().getInt(CoreBundleActivator.getUniqueIdentifier(),
+ IPreferenceKeys.PREF_MAX_FILES_IN_CYCLE, 5, null);
+ }
+
+ /**
+ * Create, register and initialize the listeners.
+ * <p>
+ * <b>Note:</b> This method is supposed to be called from {@link Startup} only!
+ */
+ /* default */ final void initListeners() {
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // If the channel state change listener instance has been created
+ // already, there is nothing left to do here
+ if (channelStateChangeListener != null) return;
+
+ // Register ourself as protocol change listener
+ Tcf.addProtocolStateChangeListener(this);
+
+ // Create and register the channel state change listener
+ channelStateChangeListener = new ChannelStateChangeListener();
+ Tcf.addChannelStateChangeListener(channelStateChangeListener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.tcf.te.tcf.core.interfaces.listeners.IProtocolStateChangeListener#stateChanged(boolean)
+ */
+ @Override
+ public void stateChanged(boolean state) {
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // On shutdown, get the listener removed and disposed
+ if (!state) {
+ Tcf.removeChannelStateChangeListener(channelStateChangeListener);
+ channelStateChangeListener = null;
+
+ Tcf.removeProtocolStateChangeListener(this);
+ }
+ }
+
+ /**
+ * Returns the file writer instance to use for the given channel.
+ *
+ * @param channel The channel. Must not be <code>null</code>.
+ * @return The file writer instance or <code>null</code>.
+ */
+ public FileWriter getWriter(IChannel channel) {
+ Assert.isNotNull(channel);
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // Before looking up the writer, check the file limits
+ checkLimits(channel);
+
+ String logName = getLogName(channel);
+ FileWriter writer = logName != null ? fileWriterMap.get(logName) : null;
+ if (writer == null && logName != null) {
+ // Create the writer
+ IPath path = getLogDir();
+ if (path != null) {
+ path = path.append(logName + ".log"); //$NON-NLS-1$
+ try {
+ writer = new FileWriter(path.toFile(), true);
+ fileWriterMap.put(logName, writer);
+ } catch (IOException e) {
+ /* ignored on purpose */
+ }
+ }
+ }
+
+ return writer;
+ }
+
+ /**
+ * Close the writer instance used for the given channel.
+ *
+ * @param channel The channel. Must not be <code>null</code>.
+ * @param message The last message to write or <code>null</code>.
+ */
+ public void closeWriter(IChannel channel, String message) {
+ Assert.isNotNull(channel);
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ // Remove the writer from the map
+ String logName = getLogName(channel);
+ FileWriter writer = logName != null ? fileWriterMap.remove(logName) : null;
+ if (writer != null) {
+ try {
+ // If specified, write the last message.
+ if (message != null) {
+ writer.write(message);
+ writer.write("\n"); //$NON-NLS-1$
+ }
+ } catch (IOException e) {
+ /* ignored on purpose */
+ } finally {
+ try {
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ /* ignored on purpose */
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the log file base name for the given peer id.
+ *
+ * @param channel The channel. Must not be <code>null</code>.
+ * @return The log file base name.
+ */
+ public String getLogName(IChannel channel) {
+ Assert.isNotNull(channel);
+ Assert.isTrue(Protocol.isDispatchThread());
+
+ String logName = null;
+
+ IPeer peer = channel.getRemotePeer();
+ if (peer != null) {
+ // Get the peer name
+ logName = peer.getName();
+
+ // Get the peer host IP address
+ String ip = peer.getAttributes().get(IPeer.ATTR_IP_HOST);
+ // Fallback: The peer id
+ if (ip == null || "".equals(ip.trim())) { //$NON-NLS-1$
+ ip = peer.getID();
+ }
+
+ // Append the peer host IP address
+ if (ip != null && !"".equals(ip.trim())) { //$NON-NLS-1$
+ logName += " " + ip.trim(); //$NON-NLS-1$
+ }
+
+ // Unify name and replace all undesired characters with '_'
+ logName = makeValid(logName);
+ }
+
+ return logName;
+ }
+
+ /**
+ * Replaces a set of predefined patterns with underscore to
+ * make a valid name.
+ *
+ * @param name The name. Must not be <code>null</code>.
+ * @return The modified name.
+ */
+ private String makeValid(String name) {
+ Assert.isNotNull(name);
+
+ String result = name.replaceAll("\\s", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ result = result.replaceAll("[:/\\;,]", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ return result;
+ }
+
+ /**
+ * Returns the log directory.
+ *
+ * @return The log directory.
+ */
+ public IPath getLogDir() {
+ IPath logDir = null;
+
+ try {
+ File file = CoreBundleActivator.getDefault().getStateLocation().append(".logs").toFile(); //$NON-NLS-1$
+ if (!file.exists()) file.mkdirs();
+ if (file.canRead() && file.isDirectory()) {
+ logDir = new Path(file.toString());
+ }
+ } catch (IllegalStateException e) {
+ // Ignored: Workspace less environment (-data @none)
+ }
+
+ if (logDir == null) {
+ // First fallback: ${HOME}/.tcf/.logs
+ File file = new Path(System.getProperty("user.home")).append(".tcf/.logs").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ if (!file.exists()) file.mkdirs();
+ if (file.canRead() && file.isDirectory()) {
+ logDir = new Path(file.toString());
+ }
+ }
+
+ if (logDir == null) {
+ // Second fallback: ${TEMP}/.tcf/.logs
+ File file = new Path(System.getProperty("java.io.tmpdir")).append(".tcf/.logs").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ if (!file.exists()) file.mkdirs();
+ if (file.canRead() && file.isDirectory()) {
+ logDir = new Path(file.toString());
+ }
+ }
+
+ return logDir;
+ }
+
+ /**
+ * Checks the limits set by the preferences.
+ *
+ * @param channel The channel. Must not be <code>null</code>.
+ * @return The checked file writer instance.
+ */
+ private void checkLimits(IChannel channel) {
+ Assert.isNotNull(channel);
+
+ String logName = getLogName(channel);
+ if (logName != null && !"".equals(logName.trim())) { //$NON-NLS-1$
+ IPath path = getLogDir();
+ if (path != null) {
+ IPath fullPath = path.append(logName + ".log"); //$NON-NLS-1$
+ File file = fullPath.toFile();
+ if (file.exists()) {
+ long size = file.length();
+ if (size >= maxFileSize) {
+ // Max log file size reached -> cycle files
+
+ // If there is an active writer, flush and close the writer
+ closeWriter(channel, null);
+
+ // Determine if the maximum number of files in the cycle has been reached
+ File maxFileInCycle = path.append(logName + "_" + maxInCycle + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ if (maxFileInCycle.exists()) {
+ // We have to rotate the full cycle, first in cycle to be removed.
+ int no = 1;
+ File fileInCycle = path.append(logName + "_" + no + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ fileInCycle.delete();
+
+ while (no <= maxInCycle) {
+ no++;
+ fileInCycle = path.append(logName + "_" + no + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ File renameTo = path.append(logName + "_" + (no - 1) + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ fileInCycle.renameTo(renameTo);
+ }
+
+ // Rename the log file
+ file.renameTo(maxFileInCycle);
+ } else {
+ // Not at the limit, find the next file name in the cycle
+ int no = 1;
+ File fileInCycle = path.append(logName + "_" + no + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ while (fileInCycle.exists()) {
+ no++;
+ fileInCycle = path.append(logName + "_" + no + ".log").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ Assert.isTrue(no <= maxInCycle);
+
+ // Rename the log file
+ file.renameTo(fileInCycle);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/PreferencesInitializer.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/PreferencesInitializer.java new file mode 100644 index 000000000..5207de915 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/PreferencesInitializer.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.tcf.te.tcf.log.core.activator.CoreBundleActivator; +import org.eclipse.tcf.te.tcf.log.core.interfaces.IPreferenceKeys; + + +/** + * TCF logging bundle preference initializer. + */ +public class PreferencesInitializer extends AbstractPreferenceInitializer { + + /** + * Constructor. + */ + public PreferencesInitializer() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences() + */ + @Override + public void initializeDefaultPreferences() { + // Get the bundles preferences manager + IEclipsePreferences prefs = DefaultScope.INSTANCE.getNode(CoreBundleActivator.getUniqueIdentifier()); + if (prefs != null) { + // [Hidden] Enable back-end communication logging: default on + prefs.putBoolean(IPreferenceKeys.PREF_LOGGING_ENABLED, true); + // [Hidden] Heat beat events: default off + prefs.putBoolean(IPreferenceKeys.PREF_SHOW_HEARTBEATS, false); + // [Hidden] Maximum log file size in bytes: default 5M + prefs.put(IPreferenceKeys.PREF_MAX_FILE_SIZE, "5M"); //$NON-NLS-1$ + // [Hidden] Maximum number of log files in cycle: default 5 + prefs.putInt(IPreferenceKeys.PREF_MAX_FILES_IN_CYCLE, 5); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/Startup.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/Startup.java new file mode 100644 index 000000000..abc0aa209 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/Startup.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal; + +import org.eclipse.tcf.protocol.Protocol; + + +/** + * Class loaded by the TCF core framework when the framework is fired up. The static + * constructor of the class will trigger the registration of the listeners in order + * to log the communication from the point the framework started up. + * <p> + * <b>Note:</b> This will effectively trigger {@link CoreBundleActivator#start(org.osgi.framework.BundleContext)} + * to be called. + */ +public class Startup { + + static { + // We might get here on shutdown as well, and if TCF has not + // been loaded, than we will run into an NPE. Lets double check. + if (Protocol.getEventQueue() != null) { + // Execute the listener registration within the TCF dispatch thread + Protocol.invokeLater(new Runnable() { + @Override + public void run() { + LogManager.getInstance().initListeners(); + } + }); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelStateChangeListener.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelStateChangeListener.java new file mode 100644 index 000000000..49056f42e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelStateChangeListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal.listener; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.te.tcf.core.interfaces.listeners.IChannelStateChangeListener; + +/** + * TCF logging channel state listener implementation. + */ +public class ChannelStateChangeListener implements IChannelStateChangeListener { + + /* (non-Javadoc) + * @see org.eclipse.tcf.te.tcf.core.interfaces.listeners.IChannelStateChangeListener#stateChanged(org.eclipse.tcf.protocol.IChannel, int) + */ + @Override + public void stateChanged(IChannel channel, int state) { + Assert.isNotNull(channel); + + switch(state) { + case IChannel.STATE_OPEN: + ChannelTraceListenerManager.getInstance().onChannelOpened(channel); + break; + case IChannel.STATE_CLOSED: + ChannelTraceListenerManager.getInstance().onChannelClosed(channel); + break; + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListener.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListener.java new file mode 100644 index 000000000..d4b88e27e --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListener.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal.listener; + +import java.io.ByteArrayInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tcf.core.AbstractChannel.TraceListener; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.te.tcf.log.core.activator.CoreBundleActivator; +import org.eclipse.tcf.te.tcf.log.core.interfaces.IPreferenceKeys; +import org.eclipse.tcf.te.tcf.log.core.interfaces.ITracing; +import org.eclipse.tcf.te.tcf.log.core.internal.LogManager; +import org.eclipse.tcf.te.tcf.log.core.internal.nls.Messages; + +/** + * TCF logging channel trace listener implementation. + */ +public class ChannelTraceListener implements TraceListener { + /** + * Time format representing time with milliseconds. + */ + public static final DateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); //$NON-NLS-1$ + + /** + * Time format representing date and time with milliseconds. + */ + public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //$NON-NLS-1$ + + // Reference to the channel + private final IChannel channel; + + /** + * Constructor. + * + * @param channel The channel. Must not be <code>null</code>. + */ + public ChannelTraceListener(IChannel channel) { + Assert.isNotNull(channel); + this.channel = channel; + } + + /** + * Return the associated channel. + * + * @return The channel instance. + */ + protected final IChannel getChannel() { + return channel; + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.core.AbstractChannel.TraceListener#onChannelClosed(java.lang.Throwable) + */ + @Override + public void onChannelClosed(Throwable error) { + if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER)) { + CoreBundleActivator.getTraceHandler().trace("TraceListener.onChannelClosed ( " + error + " )", //$NON-NLS-1$ //$NON-NLS-2$ + ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER, this); + } + + // Get the current time stamp + String date = DATE_FORMAT.format(new Date(System.currentTimeMillis())); + + String message = NLS.bind(Messages.ChannelTraceListener_channelClosed_message, + new Object[] { + date, + Integer.toHexString(channel.hashCode()), + error + }); + + LogManager.getInstance().closeWriter(channel, message); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.core.AbstractChannel.TraceListener#onMessageReceived(char, java.lang.String, java.lang.String, java.lang.String, byte[]) + */ + @Override + public void onMessageReceived(char type, String token, String service, String name, byte[] data) { + if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER)) { + CoreBundleActivator.getTraceHandler().trace("TraceListener.onMessageReceived ( " + type //$NON-NLS-1$ + + ", " + token + ", " + service + ", " + name + ", ... )", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER, this); + } + + doLogMessage(type, token, service, name, data, true); + } + + /* (non-Javadoc) + * @see org.eclipse.tcf.core.AbstractChannel.TraceListener#onMessageSent(char, java.lang.String, java.lang.String, java.lang.String, byte[]) + */ + @Override + public void onMessageSent(final char type, String token, String service, String name, byte[] data) { + if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER)) { + CoreBundleActivator.getTraceHandler().trace("TraceListener.onMessageSent ( " + type //$NON-NLS-1$ + + ", " + token + ", " + service + ", " + name + ", ... )", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER, this); + } + + doLogMessage(type, token, service, name, data, false); + } + + /** + * Helper method to output the message to the logger. + */ + private void doLogMessage(final char type, String token, String service, String name, byte[] data, boolean received) { + // Filter out the heart beat messages if not overwritten by the preferences + boolean showHeartbeats = Platform.getPreferencesService().getBoolean(CoreBundleActivator.getUniqueIdentifier(), + IPreferenceKeys.PREF_SHOW_HEARTBEATS, false, null); + if (!showHeartbeats && name != null && name.toLowerCase().contains("heartbeat")) { //$NON-NLS-1$ + return; + } + + // Format the message + final String message = formatMessage(type, token, service, name, data, received); + // Get the file writer + FileWriter writer = LogManager.getInstance().getWriter(channel); + if (writer != null) { + try { + writer.write(message); + writer.write("\n"); //$NON-NLS-1$ + writer.flush(); + } catch (IOException e) { + /* ignored on purpose */ + } + } + } + + /** + * Format the trace message. + */ + protected String formatMessage(char type, String token, String service, String name, byte[] data, boolean received) { + // Get the current time stamp + String time = TIME_FORMAT.format(new Date(System.currentTimeMillis())); + + // Decode the arguments again for tracing purpose + String args = null; + if (data != null) { + StringBuilder builder = new StringBuilder(); + InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(data)); + try { + int c = reader.read(); + while (c != -1) { + builder.append(c != 0 ? Character.valueOf((char)c).charValue() : ' '); + c = reader.read(); + } + } catch (IOException ex) { /* ignored on purpose */ } + + if (builder.length() > 0) args = builder.toString().trim(); + } + + // Construct the full message + // + // The message format is: <time>: [<---|--->] <type> <token> <service>#<name> <args> + StringBuilder message = new StringBuilder(); + message.append(time).append(":"); //$NON-NLS-1$ + message.append(" ").append(received ? "<---" : "--->"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + message.append(" ").append(Character.valueOf(type)); //$NON-NLS-1$ + if (token != null) message.append(" ").append(token); //$NON-NLS-1$ + if (service != null) message.append(" ").append(service); //$NON-NLS-1$ + if (name != null) message.append(" ").append(name); //$NON-NLS-1$ + if (args != null && args.trim().length() > 0) message.append(" ").append(args.trim()); //$NON-NLS-1$ + + return message.toString(); + } + +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListenerManager.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListenerManager.java new file mode 100644 index 000000000..fd87fdf67 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/listener/ChannelTraceListenerManager.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal.listener; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.util.NLS; +import org.eclipse.tcf.core.AbstractChannel; +import org.eclipse.tcf.protocol.IChannel; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.te.tcf.log.core.activator.CoreBundleActivator; +import org.eclipse.tcf.te.tcf.log.core.interfaces.IPreferenceKeys; +import org.eclipse.tcf.te.tcf.log.core.interfaces.ITracing; +import org.eclipse.tcf.te.tcf.log.core.internal.LogManager; +import org.eclipse.tcf.te.tcf.log.core.internal.nls.Messages; + +/** + * TCF logging channel trace listener manager implementation. + */ +public class ChannelTraceListenerManager { + // The map of trace listeners per channel + private final Map<IChannel, AbstractChannel.TraceListener> listeners = new HashMap<IChannel, AbstractChannel.TraceListener>(); + + /* + * Thread save singleton instance creation. + */ + private static class LazyInstanceHolder { + public static ChannelTraceListenerManager instance = new ChannelTraceListenerManager(); + } + + /** + * Returns the singleton instance for the manager. + */ + public static ChannelTraceListenerManager getInstance() { + return LazyInstanceHolder.instance; + } + + /** + * Constructor. + */ + /* default */ ChannelTraceListenerManager() { + } + + /** + * New channel opened. Attach a channel trace listener. + * + * @param channel The channel. Must not be <code>null</code>. + */ + public void onChannelOpened(final IChannel channel) { + Assert.isNotNull(channel); + Assert.isTrue(Protocol.isDispatchThread()); + + // The trace listener interface does not have a onChannelOpenend method, but + // for consistency, log the channel opening similar to the others. + if (CoreBundleActivator.getTraceHandler().isSlotEnabled(0, ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER)) { + CoreBundleActivator.getTraceHandler().trace("TraceListener.onChannelOpened ( " + channel + " )", //$NON-NLS-1$ //$NON-NLS-2$ + ITracing.ID_TRACE_CHANNEL_TRACE_LISTENER, this); + } + + // The trace listeners can be accessed only via AbstractChannel + if (!(channel instanceof AbstractChannel)) return; + + // Get the preference key if or if not logging is enabled + boolean loggingEnabled = Platform.getPreferencesService().getBoolean(CoreBundleActivator.getUniqueIdentifier(), + IPreferenceKeys.PREF_LOGGING_ENABLED, false, null); + // If false, we are done here and wont create any console or trace listener. + if (!loggingEnabled) return; + + // As the channel has just opened, there should be no trace listener, but better be safe and check + AbstractChannel.TraceListener traceListener = listeners.remove(channel); + if (traceListener != null) ((AbstractChannel)channel).removeTraceListener(traceListener); + // Create a new trace listener instance + traceListener = new ChannelTraceListener(channel); + // Attach trace listener to the channel + ((AbstractChannel)channel).addTraceListener(traceListener); + // Remember the associated trace listener + listeners.put(channel, traceListener); + + // Log the channel opening + String date = ChannelTraceListener.DATE_FORMAT.format(new Date(System.currentTimeMillis())); + + String message = NLS.bind(Messages.ChannelTraceListener_channelOpened_message, + new Object[] { + date, + Integer.toHexString(channel.hashCode()) + }); + + // Get the file writer + FileWriter writer = LogManager.getInstance().getWriter(channel); + if (writer != null) { + try { + writer.write(message); + writer.write("\n"); //$NON-NLS-1$ + writer.flush(); + } catch (IOException e) { + /* ignored on purpose */ + } + } + } + + /** + * Channel closed. Detach the channel trace listener if any. + * + * @param channel The channel. Must not be <code>null</code>. + */ + public void onChannelClosed(final IChannel channel) { + Assert.isNotNull(channel); + Assert.isTrue(Protocol.isDispatchThread()); + + // The trace listeners can be accessed only via AbstractChannel + if (!(channel instanceof AbstractChannel)) return; + + // Remove the trace listener if any + final AbstractChannel.TraceListener traceListener = listeners.remove(channel); + if (traceListener != null) { + Protocol.invokeLater(new Runnable() { + @Override + public void run() { + ((AbstractChannel)channel).removeTraceListener(traceListener); + } + }); + } + } +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.java new file mode 100644 index 000000000..bf4ce6edf --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2011 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.tcf.te.tcf.log.core.internal.nls; + +import org.eclipse.osgi.util.NLS; + +/** + * Plug-in externalized strings management. + */ +public class Messages extends NLS { + + // The plug-in resource bundle name + private static final String BUNDLE_NAME = "org.eclipse.tcf.te.tcf.log.core.internal.nls.Messages"; //$NON-NLS-1$ + + /** + * Static constructor. + */ + static { + // Load message values from bundle file + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + // **** Declare externalized string id's down here ***** + + public static String ChannelTraceListener_channelOpened_message; + public static String ChannelTraceListener_channelClosed_message; + public static String ChannelTraceListener_logManagerDispose_message; + +} diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.properties b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.properties new file mode 100644 index 000000000..c77b0dbd8 --- /dev/null +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.log.core/src/org/eclipse/tcf/te/tcf/log/core/internal/nls/Messages.properties @@ -0,0 +1,8 @@ +# +# org.eclipse.tcf.te.tcf.log.core +# Externalized Strings. +# + +ChannelTraceListener_channelOpened_message=# {0}: Opened channel {1} +ChannelTraceListener_channelClosed_message=# {0}: Closed channel {1} (error={2}) +ChannelTraceListener_logManagerDispose_message=# {0}: Closed session |