| /********************************************************************** |
| * Copyright (c) 2012, 2015 Ericsson |
| * |
| * 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 |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Bernd Hufmann - Initial API and implementation |
| * Bernd Hufmann - Updated for support of LTTng Tools 2.1 |
| * Markus Schorn - Bug 448058: Use org.eclipse.remote in favor of RSE |
| * Bernd Hufmann - Update to org.eclipse.remote API 2.0 |
| **********************************************************************/ |
| package org.eclipse.tracecompass.internal.lttng2.control.ui.views.model.impl; |
| |
| import static java.text.MessageFormat.format; |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.List; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.remote.core.IRemoteConnection; |
| import org.eclipse.remote.core.IRemoteConnectionChangeListener; |
| import org.eclipse.remote.core.RemoteConnectionChangeEvent; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.tracecompass.internal.lttng2.control.core.model.TargetNodeState; |
| import org.eclipse.tracecompass.internal.lttng2.control.core.model.TraceDomainType; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.Activator; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.views.messages.Messages; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.views.model.ITraceControlComponent; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.views.property.TargetNodePropertySource; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.views.service.ILttngControlService; |
| import org.eclipse.tracecompass.internal.lttng2.control.ui.views.service.LTTngControlServiceFactory; |
| import org.eclipse.tracecompass.tmf.remote.core.proxy.RemoteSystemProxy; |
| import org.eclipse.tracecompass.tmf.remote.core.shell.ICommandShell; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.views.properties.IPropertySource; |
| |
| /** |
| * <p> |
| * Implementation of the trace node component. |
| * </p> |
| * |
| * @author Bernd Hufmann |
| */ |
| public class TargetNodeComponent extends TraceControlComponent implements IRemoteConnectionChangeListener { |
| |
| // ------------------------------------------------------------------------ |
| // Constants |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Path to icon file for this component (state connected). |
| */ |
| public static final String TARGET_NODE_CONNECTED_ICON_FILE = "icons/obj16/target_connected.gif"; //$NON-NLS-1$ |
| /** |
| * Path to icon file for this component (state disconnected). |
| */ |
| public static final String TARGET_NODE_DISCONNECTED_ICON_FILE = "icons/obj16/target_disconnected.gif"; //$NON-NLS-1$ |
| |
| private static final ILttngControlService NULL_CONTROL_SERVICE = new NullControlService(); |
| |
| // ------------------------------------------------------------------------ |
| // Attributes |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * The node connection state. |
| */ |
| private TargetNodeState fState = TargetNodeState.DISCONNECTED; |
| /** |
| * The image to be displayed in state disconnected. |
| */ |
| private Image fDisconnectedImage = null; |
| /** |
| * The remote proxy implementation. |
| */ |
| private @NonNull RemoteSystemProxy fRemoteProxy; |
| /** |
| * The control service for LTTng specific commands. |
| */ |
| private ILttngControlService fService = null; |
| /** |
| * The command shell for issuing commands. |
| */ |
| private ICommandShell fShell = null; |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Constructor |
| * |
| * @param name |
| * the name of the component |
| * @param parent |
| * the parent of the component |
| * @param proxy |
| * the remote proxy implementation |
| */ |
| public TargetNodeComponent(String name, ITraceControlComponent parent, @NonNull RemoteSystemProxy proxy) { |
| super(name, parent); |
| setImage(TARGET_NODE_CONNECTED_ICON_FILE); |
| fDisconnectedImage = Activator.getDefault().loadIcon(TARGET_NODE_DISCONNECTED_ICON_FILE); |
| fRemoteProxy = proxy; |
| fRemoteProxy.getRemoteConnection().addConnectionChangeListener(this); |
| setToolTip(fRemoteProxy.getRemoteConnection().getName()); |
| } |
| |
| /** |
| * Constructor (using default proxy) |
| * |
| * @param name |
| * the name of the component |
| * @param parent |
| * the parent of the component |
| * @param host |
| * the host connection implementation |
| */ |
| public TargetNodeComponent(String name, ITraceControlComponent parent, @NonNull IRemoteConnection host) { |
| this(name, parent, new RemoteSystemProxy(host)); |
| } |
| |
| @Override |
| public void dispose() { |
| fRemoteProxy.getRemoteConnection().removeConnectionChangeListener(this); |
| fRemoteProxy.dispose(); |
| disposeControlService(); |
| } |
| |
| private void disposeControlService() { |
| fService = null; |
| final ICommandShell shell = fShell; |
| if (shell != null) { |
| shell.dispose(); |
| fShell = null; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Accessors |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public Image getImage() { |
| if (fState == TargetNodeState.CONNECTED) { |
| return super.getImage(); |
| } |
| return fDisconnectedImage; |
| } |
| |
| @Override |
| public TargetNodeState getTargetNodeState() { |
| return fState; |
| } |
| |
| @Override |
| public void setTargetNodeState(TargetNodeState state) { |
| fState = state; |
| fireComponentChanged(TargetNodeComponent.this); |
| } |
| |
| @Override |
| public ILttngControlService getControlService() { |
| return fService == null ? NULL_CONTROL_SERVICE : fService; |
| } |
| |
| @Override |
| public void setControlService(ILttngControlService service) { |
| fService = service; |
| } |
| |
| @Override |
| public <T> @Nullable T getAdapter(Class<T> adapter) { |
| if (adapter == IPropertySource.class) { |
| return adapter.cast(new TargetNodePropertySource(this)); |
| } |
| return null; |
| } |
| |
| /** |
| * @return remote system proxy implementation |
| */ |
| public @NonNull RemoteSystemProxy getRemoteSystemProxy() { |
| return fRemoteProxy; |
| } |
| |
| /** |
| * @return all available sessions. |
| */ |
| public @NonNull TraceSessionComponent[] getSessions() { |
| List<ITraceControlComponent> compenents = getChildren(TraceSessionGroup.class); |
| if (!compenents.isEmpty()) { |
| TraceSessionGroup group = (TraceSessionGroup)compenents.get(0); |
| List<ITraceControlComponent> sessions = group.getChildren(TraceSessionComponent.class); |
| return sessions.toArray(new @NonNull TraceSessionComponent[sessions.size()]); |
| } |
| return new TraceSessionComponent[0]; |
| } |
| |
| /** |
| * @return node version |
| */ |
| public String getNodeVersion() { |
| // Control service is null during connection to node |
| if (getControlService() != NULL_CONTROL_SERVICE) { |
| return getControlService().getVersionString(); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if node supports filtering of events |
| * @param domain - the domain type ({@link TraceDomainType}) |
| * @return <code>true</code> if node supports filtering else <code>false</code> |
| */ |
| public boolean isEventFilteringSupported(TraceDomainType domain) { |
| if (domain.equals(TraceDomainType.KERNEL)) { |
| return getControlService().isVersionSupported("2.7.0"); //$NON-NLS-1$ |
| } |
| return getControlService().isVersionSupported("2.1.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if node supports networks streaming or not |
| * @return <code>true</code> if node supports filtering else <code>false</code> |
| * |
| */ |
| public boolean isNetworkStreamingSupported() { |
| return getControlService().isVersionSupported("2.1.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if node supports configuring buffer type or not |
| * @return <code>true</code> if node supports buffer type configuration else <code>false</code> |
| */ |
| public boolean isBufferTypeConfigSupported() { |
| return getControlService().isVersionSupported("2.2.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if node supports trace file rotation or not |
| * @return <code>true</code> if node supports trace file rotation else <code>false</code> |
| */ |
| public boolean isTraceFileRotationSupported() { |
| return getControlService().isVersionSupported("2.2.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns if node supports periodical flush for metadata or not |
| * @return <code>true</code> if node supports periodical flush for metadata else <code>false</code> |
| */ |
| public boolean isPeriodicalMetadataFlushSupported() { |
| return getControlService().isVersionSupported("2.2.0"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns if node supports snapshots or not |
| * @return <code>true</code> if it supports snapshots else <code>false</code> |
| * |
| */ |
| public boolean isSnapshotSupported() { |
| return getControlService().isVersionSupported("2.3.0"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns if node supports live or not |
| * @return <code>true</code> if it supports live else <code>false</code> |
| * |
| */ |
| public boolean isLiveSupported() { |
| return false; |
| // FIXME: Disable Live support until we have a better implementation |
| //return getControlService().isVersionSupported("2.4.0"); //$NON-NLS-1$; |
| } |
| /** |
| * Returns if node supports adding contexts on event |
| * @return <code>true</code> if it supports adding contexts on events else <code>false</code> |
| * |
| */ |
| public boolean isContextOnEventSupported() { |
| return !getControlService().isVersionSupported("2.2.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Checks if enabling of per syscall event is supported |
| * |
| * @return <code>true</code> if enabling of per syscall event is supported else <code>false</code> |
| */ |
| public boolean isPerSyscallEventsSupported() { |
| return getControlService().isVersionSupported("2.6.0"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns if node supports JUL logging or not |
| * @return <code>true</code> if it supports JUL logging else <code>false</code> |
| * |
| */ |
| public boolean isJulLoggingSupported() { |
| return getControlService().isVersionSupported("2.6.0"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns if node supports LOG4J logging or not |
| * @return <code>true</code> if it supports LOG4J logging else <code>false</code> |
| * |
| */ |
| public boolean isLog4jLoggingSupported() { |
| return getControlService().isVersionSupported("2.6.0"); //$NON-NLS-1$ |
| } |
| /** |
| * Returns if node supports Python logging or not |
| * @return <code>true</code> if it supports Python logging else <code>false</code> |
| * |
| */ |
| public boolean isPythonLoggingSupported() { |
| return getControlService().isVersionSupported("2.7.0"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Checks if given version is supported by this ILTTngControlService implementation. |
| * |
| * @param version The version to check |
| * @return <code>true</code> if version is supported else <code>false</code> |
| */ |
| public boolean isVersionSupported(String version) { |
| return getControlService().isVersionSupported(version); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Operations |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public void connectionChanged(RemoteConnectionChangeEvent e) { |
| if (fState == TargetNodeState.CONNECTING) { |
| return; |
| } |
| |
| switch (e.getType()) { |
| case RemoteConnectionChangeEvent.CONNECTION_CLOSED: |
| case RemoteConnectionChangeEvent.CONNECTION_ABORTED: |
| handleDisconnected(); |
| break; |
| case RemoteConnectionChangeEvent.CONNECTION_OPENED: |
| handleConnected(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /** |
| * Method to connect this node component to the remote target node. |
| */ |
| public void connect() { |
| if (fState == TargetNodeState.DISCONNECTED) { |
| try { |
| setTargetNodeState(TargetNodeState.CONNECTING); |
| Job job = new Job(format(Messages.TraceControl_OpenConnectionTo, getName())) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| fRemoteProxy.connect(checkNotNull(monitor)); |
| return Status.OK_STATUS; |
| } catch (Exception e) { |
| return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.TraceControl_ConnectionFailure, e); |
| } |
| } |
| }; |
| job.addJobChangeListener(new JobChangeAdapter() { |
| @Override |
| public void done(IJobChangeEvent event) { |
| IStatus status = event.getResult(); |
| if (status.isOK()) { |
| handleConnected(); |
| } else { |
| handleDisconnected(); |
| if (status.getSeverity() != IStatus.CANCEL) { |
| Activator.getDefault().getLog().log(status); |
| } |
| } |
| } |
| }); |
| job.schedule(); |
| } catch (Exception e) { |
| setTargetNodeState(TargetNodeState.DISCONNECTED); |
| Activator.getDefault().logError(Messages.TraceControl_ConnectionFailure + " (" + getName() + "). \n", e); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| |
| /** |
| * Method to disconnect this node component to the remote target node. |
| */ |
| public void disconnect() { |
| if (fState == TargetNodeState.CONNECTED) { |
| try { |
| setTargetNodeState(TargetNodeState.DISCONNECTING); |
| fRemoteProxy.disconnect(); |
| } catch (Exception e) { |
| Activator.getDefault().logError(Messages.TraceControl_DisconnectionFailure + " (" + getName() + "). \n", e); //$NON-NLS-1$ //$NON-NLS-2$ |
| } finally { |
| handleDisconnected(); |
| } |
| } |
| } |
| |
| /** |
| * Retrieves the trace configuration from the target node and populates the |
| * information in the tree model. The execution is done in a own job. |
| */ |
| public void getConfigurationFromNode() { |
| Job job = new Job(Messages.TraceControl_RetrieveNodeConfigurationJob) { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| |
| try { |
| // Get provider information from node |
| TraceProviderGroup providerGroup = new TraceProviderGroup(Messages.TraceControl_ProviderDisplayName, TargetNodeComponent.this); |
| addChild(providerGroup); |
| providerGroup.getProviderFromNode(monitor); |
| |
| // Get session information from node |
| TraceSessionGroup sessionGroup = new TraceSessionGroup(Messages.TraceControl_AllSessionsDisplayName, TargetNodeComponent.this); |
| addChild(sessionGroup); |
| sessionGroup.getSessionsFromNode(monitor); |
| } catch (ExecutionException e) { |
| removeAllChildren(); |
| return new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.TraceControl_RetrieveNodeConfigurationFailure, e); |
| } finally { |
| if (getTargetNodeState() == TargetNodeState.CONNECTING) { |
| setTargetNodeState(TargetNodeState.CONNECTED); |
| } |
| } |
| |
| return Status.OK_STATUS; |
| } |
| }; |
| job.setUser(true); |
| job.schedule(); |
| } |
| |
| /** |
| * Refresh the node configuration |
| */ |
| public void refresh() { |
| removeAllChildren(); |
| getConfigurationFromNode(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Helper function |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * @return returns the control service for LTTng specific commands. |
| * @throws ExecutionException |
| */ |
| private ILttngControlService createControlService() throws ExecutionException { |
| if (fService == null) { |
| try { |
| ICommandShell shell = fRemoteProxy.createCommandShell(); |
| fShell = shell; |
| fService = LTTngControlServiceFactory.getLttngControlService(shell); |
| } catch (ExecutionException e) { |
| disposeControlService(); |
| throw e; |
| } |
| } |
| return fService; |
| } |
| |
| /** |
| * Handles the connected event. |
| */ |
| private void handleConnected() { |
| try { |
| createControlService(); |
| getConfigurationFromNode(); |
| // Set connected only after the control service has been created and the jobs for creating the |
| // sub-nodes are completed. |
| } catch (final ExecutionException e) { |
| // Disconnect only if no control service, otherwise stay connected. |
| if (getControlService() == NULL_CONTROL_SERVICE) { |
| fState = TargetNodeState.CONNECTED; |
| disconnect(); |
| } |
| |
| // Notify user |
| Display.getDefault().asyncExec(() -> { |
| ErrorDialog er = new ErrorDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), |
| Messages.TraceControl_ErrorTitle, Messages.TraceControl_RetrieveNodeConfigurationFailure, |
| new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e), |
| IStatus.ERROR); |
| er.open(); |
| }); |
| Activator.getDefault().logError(Messages.TraceControl_RetrieveNodeConfigurationFailure + " (" + getName() + "). \n", e); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| /** |
| * Handles the disconnected event. |
| */ |
| private void handleDisconnected() { |
| disposeControlService(); |
| setTargetNodeState(TargetNodeState.DISCONNECTED); |
| removeAllChildren(); |
| } |
| |
| @Override |
| public void addChild(ITraceControlComponent component) { |
| if (getTargetNodeState() == TargetNodeState.DISCONNECTED) { |
| return; |
| } |
| super.addChild(component); |
| } |
| } |