/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.gui.communication.views.contributors;
import java.util.Collection;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.MessageBox;
import de.rcenvironment.core.communication.sshconnection.SshConnectionContext;
import de.rcenvironment.core.communication.sshconnection.SshConnectionService;
import de.rcenvironment.core.communication.sshconnection.api.SshConnectionListener;
import de.rcenvironment.core.communication.sshconnection.api.SshConnectionListenerAdapter;
import de.rcenvironment.core.communication.sshconnection.api.SshConnectionSetup;
import de.rcenvironment.core.gui.communication.views.internal.AnchorPoints;
import de.rcenvironment.core.gui.communication.views.model.NetworkGraphNodeWithContext;
import de.rcenvironment.core.gui.communication.views.model.SimpleNetworkViewNode;
import de.rcenvironment.core.gui.communication.views.spi.NetworkViewContributor;
import de.rcenvironment.core.gui.communication.views.spi.SelfRenderingNetworkViewNode;
import de.rcenvironment.core.gui.communication.views.spi.StandardUserNodeActionNode;
import de.rcenvironment.core.gui.communication.views.spi.StandardUserNodeActionType;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.incubator.ServiceRegistry;
import de.rcenvironment.core.utils.incubator.ServiceRegistryPublisherAccess;
import de.rcenvironment.core.utils.ssh.jsch.SshSessionConfiguration;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Contributes the subtree showing the list of current SSH connections.
*
* @author Brigitte Boden
* @author Robert Mischke
*/
public class SshConnectionSetupsListContributor extends NetworkViewContributorBase {
private static final AnchorPoints PARENT_ANCHOR = AnchorPoints.SSH_REMOTE_ACCESS_SECTION_PARENT_NODE;
/**
* Tree wrapper {@link SshSessionConfiguration}.
*
* @author Brigitte Boden
*/
private final class SshConnectionSetupNode implements SelfRenderingNetworkViewNode, StandardUserNodeActionNode {
private final String connectionId;
private final SshConnectionSetup sshConnectionSetup;
SshConnectionSetupNode(String connectionId, SshConnectionSetup setup) {
super();
this.connectionId = connectionId;
this.sshConnectionSetup = setup;
wrapperMap.put(setup, this);
}
@Override
public NetworkViewContributor getContributor() {
return SshConnectionSetupsListContributor.this;
}
@Override
public boolean isActionApplicable(StandardUserNodeActionType actionType) {
switch (actionType) {
case START:
return !sshConnectionSetup.isConnected();
case STOP:
return sshConnectionSetup.isConnected();
case EDIT:
case DELETE:
return !sshConnectionSetup.isConnected();
default:
return false;
}
}
@Override
public void performAction(StandardUserNodeActionType actionType) {
switch (actionType) {
case START:
display.asyncExec(new Runnable() {
@Override
public void run() {
if (sshConnectionSetup.getUsePassphrase() && !sshConnectionSetup.getStorePassphrase()) {
EnterPassphraseDialog dialog = new EnterPassphraseDialog(treeViewer.getTree().getShell());
if (dialog.open() == Window.OK) {
sshConnectionService.setAuthPhraseForSshConnection(connectionId, dialog.getPassphrase(),
dialog.getStorePassphrase());
sshConnectionService.connectSession(connectionId, dialog.getPassphrase());
}
} else {
sshConnectionService.connectSession(connectionId);
}
}
});
break;
case STOP:
sshConnectionService.disconnectSession(connectionId);
break;
case EDIT:
performEdit();
break;
case DELETE:
sshConnectionService.disposeConnection(connectionId);
break;
default:
break;
}
}
private void performEdit() {
EditSshConnectionDialog dialog =
new EditSshConnectionDialog(treeViewer.getTree().getShell(), sshConnectionSetup.getDisplayName(),
sshConnectionSetup.getHost(), sshConnectionSetup.getPort(), sshConnectionSetup.getUsername(),
sshConnectionSetup.getKeyfileLocation(), sshConnectionSetup.getUsePassphrase(),
sshConnectionSetup.getStorePassphrase(),
sshConnectionSetup.getConnectOnStartUp());
if (sshConnectionSetup.getStorePassphrase()) {
// Retrieve stored passphrase
String passphrase = sshConnectionService.retreiveSshConnectionPassword(sshConnectionSetup.getId());
if (passphrase != null) {
dialog.setPassphrase(passphrase);
}
}
final String id = sshConnectionSetup.getId();
if (dialog.open() == Window.OK) {
final String connectionName = dialog.getConnectionName();
final boolean connectImmediately = dialog.getConnectImmediately();
final String host = dialog.getHost();
final int port = dialog.getPort();
final String username = dialog.getUsername();
final String passphrase = dialog.getPassphrase();
final boolean storePassphrase = dialog.shouldStorePassPhrase();
final String keyfileLocation = dialog.getKeyfileLocation();
final boolean usePassphrase = dialog.getUsePassphrase();
ConcurrencyUtils.getAsyncTaskService().execute(new Runnable() {
@TaskDescription("Edit SSH Connection.")
@Override
public void run() {
sshConnectionService.editSshConnection(new SshConnectionContext(id, connectionName, host, port, username,
keyfileLocation, usePassphrase, connectImmediately));
sshConnectionService.setAuthPhraseForSshConnection(id, passphrase, storePassphrase);
if (connectImmediately) {
sshConnectionService.connectSession(id, passphrase);
}
}
});
}
}
@Override
public String getText() {
String status = "connected";
if (!sshConnectionService.isConnected(connectionId)) {
status = "disconnected";
}
return StringUtils.format("%s (%s)", sshConnectionSetup.getDisplayName(), status);
}
@Override
public Image getImage() {
if (sshConnectionService.isConnected(connectionId)) {
return connectedImage;
}
return disconnectedImage;
}
@Override
public boolean getHasChildren() {
return false;
}
}
private static final int ROOT_PRIORITY = 30;
private final Log log = LogFactory.getLog(getClass());
private SelfRenderingNetworkViewNode rootNode;
private final Image connectedImage;
private final Image disconnectedImage;
private final ServiceRegistryPublisherAccess serviceRegistryAccess;
private SshConnectionService sshConnectionService;
private Collection<SshConnectionSetup> sshConnectionSetups;
private final WeakHashMap<SshConnectionSetup, SshConnectionSetupNode> wrapperMap = new WeakHashMap<>();
public SshConnectionSetupsListContributor() {
super();
connectedImage = ImageDescriptor.createFromURL(
getClass().getResource("/resources/icons/connectSsh.png")).createImage(); //$NON-NLS-1$
disconnectedImage = ImageDescriptor.createFromURL(
getClass().getResource("/resources/icons/disconnectSsh.png")).createImage(); //$NON-NLS-1$
serviceRegistryAccess = ServiceRegistry.createPublisherAccessFor(this);
sshConnectionService = serviceRegistryAccess.getService(SshConnectionService.class);
sshConnectionSetups = sshConnectionService.getAllSshConnectionSetups();
registerListeners();
}
@Override
public int getRootElementsPriority() {
return ROOT_PRIORITY;
}
@Override
public Object[] getTopLevelElements(Object parentNode) {
if (parentNode != PARENT_ANCHOR) {
return null;
}
boolean hasChildren = sshConnectionSetups != null && !sshConnectionSetups.isEmpty();
rootNode =
new SimpleNetworkViewNode("SSH Remote Access Connections", disconnectedImage, this,
hasChildren);
return new Object[] { rootNode };
}
@Override
public int getInstanceDataElementsPriority() {
return 0;
}
/**
* A custom method as we don't strictly separate the main UI code from the contributors.
*/
public void showAddConnectionDialog() {
AddSshConnectionDialog dialog = new AddSshConnectionDialog(treeViewer.getTree().getShell());
if (dialog.open() == Window.OK) {
final String connectionName = dialog.getConnectionName();
final boolean connectImmediately = dialog.getConnectImmediately();
final String host = dialog.getHost();
final int port = dialog.getPort();
final String username = dialog.getUsername();
final String passphrase = dialog.getPassphrase();
final boolean storePassphrase = dialog.shouldStorePassPhrase();
final String keyfileLocation = dialog.getKeyfileLocation();
final boolean usePassphrase = dialog.getUsePassphrase();
ConcurrencyUtils.getAsyncTaskService().execute(new Runnable() {
@TaskDescription("Create new SSH Connection.")
@Override
public void run() {
String id =
sshConnectionService.addSshConnection(connectionName, host, port, username, keyfileLocation, usePassphrase,
connectImmediately);
sshConnectionService.setAuthPhraseForSshConnection(id, passphrase, storePassphrase);
if (connectImmediately) {
sshConnectionService.connectSession(id, passphrase);
}
}
});
}
}
@Override
public Object[] getChildrenForNetworkInstanceNode(NetworkGraphNodeWithContext instanceNode) {
throw newUnexpectedCallException();
}
@Override
public boolean hasChildren(Object parentNode) {
throw newUnexpectedCallException();
}
@Override
public Object[] getChildren(Object node) {
if (node != rootNode) {
throw newUnexpectedCallException();
}
if (sshConnectionSetups.isEmpty()) {
return EMPTY_ARRAY;
}
final SshConnectionSetupNode[] nodes = new SshConnectionSetupNode[sshConnectionSetups.size()];
int pos = 0;
for (SshConnectionSetup setup : sshConnectionSetups) {
SshConnectionSetupNode setupNode = new SshConnectionSetupNode(setup.getId(), setup);
nodes[pos++] = setupNode;
}
return nodes;
}
@Override
public Object getParent(Object node) {
if (node == rootNode) {
return PARENT_ANCHOR;
} else {
return rootNode;
}
}
@Override
public String getText(Object node) {
throw newUnexpectedCallException();
}
@Override
public Image getImage(Object node) {
throw newUnexpectedCallException();
}
@Override
public void dispose() {
connectedImage.dispose();
disconnectedImage.dispose();
serviceRegistryAccess.dispose();
}
private void registerListeners() {
SshConnectionListener listener = new SshConnectionListenerAdapter() {
@Override
public void onCollectionChanged(final Collection<SshConnectionSetup> connections) {
display.asyncExec(new Runnable() {
@Override
public void run() {
sshConnectionSetups = connections;
if (treeViewer.getControl().isDisposed()) {
return;
}
treeViewer.refresh(PARENT_ANCHOR, false);
treeViewer.setExpandedState(rootNode, true);
}
});
}
@Override
public void onConnected(final SshConnectionSetup setup) {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (treeViewer.getControl().isDisposed()) {
return;
}
Object node = getSetupNodeForSetup(setup);
treeViewer.refresh(node);
treeViewer.setExpandedState(node, true);
}
});
}
@Override
public void onConnectionAttemptFailed(final SshConnectionSetup setup, final String reason, boolean firstConsecutiveFailure,
boolean willAutoRetry) {
display.asyncExec(new Runnable() {
@Override
public void run() {
MessageBox dialog = new MessageBox(treeViewer.getTree().getShell(), SWT.ICON_ERROR | SWT.OK);
dialog
.setMessage(StringUtils.format("SSH connection attempt to host %s on port %s failed:\n%s",
setup.getHost(),
setup.getPort(), reason));
dialog.open();
}
});
display.asyncExec(new Runnable() {
@Override
public void run() {
if (treeViewer.getControl().isDisposed()) {
return;
}
Object node = getSetupNodeForSetup(setup);
treeViewer.refresh(node);
}
});
}
@Override
public void onConnectionClosed(final SshConnectionSetup setup, boolean willAutoRetry) {
display.asyncExec(new Runnable() {
@Override
public void run() {
if (treeViewer.getControl().isDisposed()) {
return;
}
Object node = getSetupNodeForSetup(setup);
treeViewer.refresh(node);
}
});
}
};
serviceRegistryAccess.registerService(SshConnectionListener.class, listener);
}
private SshConnectionSetupNode getSetupNodeForSetup(SshConnectionSetup setup) {
return wrapperMap.get(setup);
}
}