/*******************************************************************************
* Copyright (c) 2007, 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
* Martin Oberhuber (Wind River) - [269682] Get port from RSE Property
* Uwe Stieber (Wind River) - [271227] Fix compiler warnings in org.eclipse.tm.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.tm.internal.tcf.rse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
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.tm.tcf.core.AbstractPeer;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.IService;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IFileSystem;
import org.eclipse.tm.tcf.services.ILocator;
import org.eclipse.tm.tcf.services.IStreams;
import org.eclipse.tm.tcf.services.ISysMonitor;
import org.eclipse.tm.tcf.services.ITerminals;
import org.eclipse.tm.tcf.util.TCFTask;
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_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 bSubscribed = false;
/* subscribe the stream service on this TCP connection */
private IStreams.StreamsListener streamListener = new IStreams.StreamsListener() {
public void created(String stream_type, String stream_id,
String context_id) {
}
/**
* Called when a stream is disposed.
*
* @param stream_type
* - source type of the stream.
* @param stream_id
* - ID of the stream.
*/
public void disposed(String stream_type, String 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_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() {
if (!connectTCFChannel(res, monitor))
add_to_wait_list(this);
}
});
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 {
/* First UnSubscribe TCP channel */
unsubscribe();
/* 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];
Protocol.invokeAndWait(new Runnable() {
public void run() {
res[0] = channel != null && channel.getState() == IChannel.STATE_OPEN;
}
});
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);
ILocator locator = Protocol.getLocator();
for (IPeer p : locator.getPeers().values()) {
Map<String, String> attrs = p.getAttributes();
if (("TCP".equals(attrs.get(IPeer.ATTR_TRANSPORT_NAME)) || //$NON-NLS-1$
"SSL".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, "TCP"); //$NON-NLS-1$
attrs.put(IPeer.ATTR_IP_HOST, host);
attrs.put(IPeer.ATTR_IP_PORT, port_str);
peer = new AbstractPeer(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();
}
bSubscribed = false;
channel = null;
channel_error = null;
}
});
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 boolean isSubscribed() {
return bSubscribed;
}
public void unsubscribe() throws IOException {
if (bSubscribed) {
new TCFTask<Object>() {
public void run() {
IStreams streams = getService(IStreams.class);
streams.unsubscribe(ITerminals.NAME, streamListener,
new IStreams.DoneUnsubscribe() {
public void doneUnsubscribe(IToken token,
Exception error) {
done(this);
}
});
}
}.getIO();
bSubscribed = false;
}
}
public void subscribe() throws RemoteFileException {
try {
new TCFTask<Object>() {
public void run() {
if (bSubscribed) {
done(this);
}
else {
bSubscribed = true;
IStreams streams = getService(IStreams.class);
streams.subscribe(ITerminals.NAME, streamListener,
new IStreams.DoneSubscribe() {
public void doneSubscribe(IToken token,
Exception error) {
if (error != null) {
bSubscribed = false;
error(error);
}
else
done(this);
}
});
}}
}.getIO();
}
catch (Exception e) {
e.printStackTrace();//$NON-NLS-1$
throw new RemoteFileException(
"Error When Subscribe Terminal streams!", e); //$NON-NLS-1$
}
}
}