/******************************************************************************* * Copyright (c) 2007, 2013 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 * Martin Oberhuber (Wind River) - [269682] Get port from RSE Property * Uwe Stieber (Wind River) - [271227] Fix compiler warnings in org.eclipse.tcf.rse * Anna Dushistova (MontaVista) - [285373] TCFConnectorService should send CommunicationsEvent.BEFORE_CONNECT and CommunicationsEvent.BEFORE_DISCONNECT * Liping Ke (Intel Corp.)- [326490] Add authentication to the TCF Connector Service and attach stream subs/unsubs method * Jeff Johnston (RedHat) - [350752] TCFConnectorService doesn't recognize connections with SSL transport *******************************************************************************/ package org.eclipse.tcf.internal.rse; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.rse.core.model.IHost; import org.eclipse.rse.core.model.IPropertySet; import org.eclipse.rse.core.model.PropertyType; import org.eclipse.rse.core.model.SystemSignonInformation; import org.eclipse.rse.core.subsystems.CommunicationsEvent; import org.eclipse.rse.services.files.RemoteFileException; import org.eclipse.rse.ui.subsystems.StandardConnectorService; import org.eclipse.tcf.core.TransientPeer; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.protocol.IService; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.IFileSystem; import org.eclipse.tcf.services.ILocator; import org.eclipse.tcf.services.IStreams; import org.eclipse.tcf.services.ISysMonitor; import org.eclipse.tcf.services.ITerminals; public class TCFConnectorService extends StandardConnectorService implements ITCFSessionProvider{ public static final String PROPERTY_SET_NAME = "TCF Connection Settings"; //$NON-NLS-1$ public static final String PROPERTY_TRANSPORT_NAME = "Transport.Name"; //$NON-NLS-1$ public static final String PROPERTY_LOGIN_REQUIRED = "Login.Required"; //$NON-NLS-1$ public static final String PROPERTY_PWD_REQUIRED="Pwd.Required"; //$NON-NLS-1$ public static final String PROPERTY_LOGIN_PROMPT = "Login.Prompt"; //$NON-NLS-1$ public static final String PROPERTY_PASSWORD_PROMPT = "Password.Prompt"; //$NON-NLS-1$ public static final String PROPERTY_COMMAND_PROMPT = "Command.Prompt"; //$NON-NLS-1$ private IChannel channel; private Throwable channel_error; private final List<Runnable> wait_list = new ArrayList<Runnable>(); private boolean poll_timer_started; private boolean streams_subscribed = false; private boolean streams_connecting = false; private final HashSet<String> stream_ids = new HashSet<String>(); /* subscribe the stream service on this TCP connection */ private final IStreams.StreamsListener streams_listener = new IStreams.StreamsListener() { public void created(String stream_type, String stream_id, String context_id) { if (streams_connecting) { stream_ids.add(stream_id); } else { getService(IStreams.class).disconnect(stream_id, new IStreams.DoneDisconnect() { public void doneDisconnect(IToken token, Exception error) { if (error != null) channel.terminate(error); } }); } } public void disposed(String stream_type, String stream_id) { stream_ids.remove(stream_id); } }; public TCFConnectorService(IHost host, int port) { super(Messages.TCFConnectorService_Name, Messages.TCFConnectorService_Description, host, port); getTCFPropertySet(); } public IPropertySet getTCFPropertySet() { IPropertySet tcfSet = getPropertySet(PROPERTY_SET_NAME); if (tcfSet == null) { tcfSet = createPropertySet(PROPERTY_SET_NAME, Messages.PropertySet_Description); //add default values if not set tcfSet.addProperty(PROPERTY_TRANSPORT_NAME, "TCP", PropertyType.getEnumPropertyType(new String[] {"TCP", "SSL"})); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ tcfSet.addProperty(PROPERTY_LOGIN_REQUIRED, "false", PropertyType.getEnumPropertyType(new String[] {"true", "false"})); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ tcfSet.addProperty(PROPERTY_PWD_REQUIRED, "false", PropertyType.getEnumPropertyType(new String[] {"true", "false"})); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ tcfSet.addProperty(PROPERTY_LOGIN_PROMPT, "ogin: ", PropertyType.getStringPropertyType()); //$NON-NLS-1$ tcfSet.addProperty(PROPERTY_PASSWORD_PROMPT, "assword: ", PropertyType.getStringPropertyType()); //$NON-NLS-1$ tcfSet.addProperty(PROPERTY_COMMAND_PROMPT, "#", PropertyType.getStringPropertyType()); //$NON-NLS-1$ } return tcfSet; } /** * @return true if the associated connector service requires a password. */ public final boolean requiresPassword() { return false; } @Override protected void internalConnect(final IProgressMonitor monitor) throws Exception { assert !Protocol.isDispatchThread(); final Exception[] res = new Exception[1]; // Fire comm event to signal state about to change fireCommunicationsEvent(CommunicationsEvent.BEFORE_CONNECT); monitor.beginTask("Connecting " + getHostName(), 1); //$NON-NLS-1$ synchronized (res) { Protocol.invokeLater(new Runnable() { public void run() { try { if (!connectTCFChannel(res, monitor)) add_to_wait_list(this); } catch (Throwable x) { synchronized (res) { if (x instanceof Exception) res[0] = (Exception)x; else res[0] = new Exception(x); res.notify(); } } } }); res.wait(); } if (res[0] != null) throw res[0]; monitor.done(); } @Override protected void internalDisconnect(final IProgressMonitor monitor) throws Exception { assert !Protocol.isDispatchThread(); final Exception[] res = new Exception[1]; // Fire comm event to signal state about to change fireCommunicationsEvent(CommunicationsEvent.BEFORE_DISCONNECT); monitor.beginTask("Disconnecting " + getHostName(), 1); //$NON-NLS-1$ try { /* Disconnecting TCP channel */ synchronized (res) { Protocol.invokeLater(new Runnable() { public void run() { if (!disconnectTCFChannel(res, monitor)) add_to_wait_list(this); } }); res.wait(); } if (res[0] != null) throw res[0]; } catch (Exception e) { e.printStackTrace(); throw new RemoteFileException("Error creating Terminal", e); //$NON-NLS-1$ } finally { monitor.done(); } } public boolean isConnected() { final boolean res[] = new boolean[1]; try { Protocol.invokeAndWait(new Runnable() { public void run() { res[0] = channel != null && channel.getState() == IChannel.STATE_OPEN; } }); } catch (IllegalStateException e) { res[0] = false; } return res[0]; } private void add_to_wait_list(Runnable cb) { wait_list.add(cb); if (poll_timer_started) return; Protocol.invokeLater(1000, new Runnable() { public void run() { poll_timer_started = false; run_wait_list(); } }); poll_timer_started = true; } private void run_wait_list() { if (wait_list.isEmpty()) return; Runnable[] r = wait_list.toArray(new Runnable[wait_list.size()]); wait_list.clear(); for (int i = 0; i < r.length; i++) r[i].run(); } private boolean connectTCFChannel(Exception[] res, IProgressMonitor monitor) { if (channel != null) { switch (channel.getState()) { case IChannel.STATE_OPEN: case IChannel.STATE_CLOSED: synchronized (res) { if (channel_error instanceof Exception) res[0] = (Exception)channel_error; else if (channel_error != null) res[0] = new Exception(channel_error); else res[0] = null; res.notify(); return true; } } } if (monitor.isCanceled()) { synchronized (res) { res[0] = new Exception("Canceled"); //$NON-NLS-1$ if (channel != null) channel.terminate(res[0]); res.notify(); return true; } } if (channel == null) { String host = getHostName().toLowerCase(); int port = getConnectPort(); if (port <= 0) { // Default fallback port = TCFConnectorServiceManager.TCF_PORT; } IPeer peer = null; String port_str = Integer.toString(port); String transport = getTCFPropertySet().getPropertyValue(TCFConnectorService.PROPERTY_TRANSPORT_NAME); ILocator locator = Protocol.getLocator(); for (IPeer p : locator.getPeers().values()) { Map<String, String> attrs = p.getAttributes(); if (transport.equals(attrs.get(IPeer.ATTR_TRANSPORT_NAME)) && //$NON-NLS-1$ host.equalsIgnoreCase(attrs.get(IPeer.ATTR_IP_HOST)) && port_str.equals(attrs.get(IPeer.ATTR_IP_PORT))) { peer = p; break; } } if (peer == null) { Map<String, String> attrs = new HashMap<String, String>(); attrs.put(IPeer.ATTR_ID, "RSE:" + host + ":" + port_str); //$NON-NLS-1$ //$NON-NLS-2$ attrs.put(IPeer.ATTR_NAME, getName()); attrs.put(IPeer.ATTR_TRANSPORT_NAME, transport); attrs.put(IPeer.ATTR_IP_HOST, host); attrs.put(IPeer.ATTR_IP_PORT, port_str); peer = new TransientPeer(attrs); } channel = peer.openChannel(); channel.addChannelListener(new IChannel.IChannelListener() { public void onChannelOpened() { assert channel != null; run_wait_list(); } public void congestionLevel(int level) { } public void onChannelClosed(Throwable error) { assert channel != null; channel.removeChannelListener(this); channel_error = error; if (wait_list.isEmpty()) { fireCommunicationsEvent(CommunicationsEvent.CONNECTION_ERROR); } else { run_wait_list(); } channel = null; channel_error = null; streams_subscribed = false; stream_ids.clear(); } }); assert channel.getState() == IChannel.STATE_OPENING; } return false; } private boolean disconnectTCFChannel(Exception[] res, IProgressMonitor monitor) { if (channel == null || channel.getState() == IChannel.STATE_CLOSED) { synchronized (res) { res[0] = null; res.notify(); return true; } } if (monitor.isCanceled()) { synchronized (res) { res[0] = new Exception("Canceled"); //$NON-NLS-1$ res.notify(); return true; } } if (channel.getState() == IChannel.STATE_OPEN) channel.close(); return false; } public <V extends IService> V getService(Class<V> service_interface) { if (channel == null || channel.getState() != IChannel.STATE_OPEN) throw new Error("Not connected"); //$NON-NLS-1$ V m = channel.getRemoteService(service_interface); if (m == null) throw new Error("Remote peer does not support " + service_interface.getName() + " service"); //$NON-NLS-1$ //$NON-NLS-2$ return m; } public ISysMonitor getSysMonitorService() { return getService(ISysMonitor.class); } public IFileSystem getFileSystemService() { return getService(IFileSystem.class); } public IChannel getChannel() { return channel; } public String getSessionHostName() { String hostName = ""; IHost host = getHost(); if (host != null) hostName = host.getHostName(); return hostName; } public String getSessionUserId() { return getUserId(); } public String getSessionPassword() { String password = ""; SystemSignonInformation ssi = getSignonInformation(); if (ssi != null) { password = ssi.getPassword(); } return password; } public void onStreamsConnecting() { if (!streams_subscribed) { streams_subscribed = true; IStreams streams = getService(IStreams.class); if (streams != null) { streams.subscribe(ITerminals.NAME, streams_listener, new IStreams.DoneSubscribe() { public void doneSubscribe(IToken token, Exception error) { if (error != null) channel.terminate(error); } }); } } streams_connecting = true; } public void onStreamsID(String id) { stream_ids.remove(id); } public void onStreamsConnected() { streams_connecting = false; for (String id : stream_ids) { getService(IStreams.class).disconnect(id, new IStreams.DoneDisconnect() { public void doneDisconnect(IToken token, Exception error) { if (error != null) channel.terminate(error); } }); } } }