/*******************************************************************************
* Copyright (c) 2007, 2010 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.tm.internal.tcf.core;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.eclipse.tm.tcf.core.AbstractChannel;
import org.eclipse.tm.tcf.core.ChannelPIPE;
import org.eclipse.tm.tcf.core.ChannelTCP;
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.ITransportProvider;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.ILocator;
public class TransportManager {
private static final Collection<AbstractChannel> channels =
new LinkedList<AbstractChannel>();
private static final Collection<Protocol.ChannelOpenListener> listeners =
new LinkedList<Protocol.ChannelOpenListener>();
private static final HashMap<String,ITransportProvider> transports =
new HashMap<String,ITransportProvider>();
static {
addTransportProvider(new ITransportProvider() {
public String getName() {
return "TCP";
}
public IChannel openChannel(IPeer peer) {
assert getName().equals(peer.getTransportName());
Map<String,String> attrs = peer.getAttributes();
String host = attrs.get(IPeer.ATTR_IP_HOST);
String port = attrs.get(IPeer.ATTR_IP_PORT);
if (host == null) throw new IllegalArgumentException("No host name");
return new ChannelTCP(peer, host, parsePort(port), false);
}
});
addTransportProvider(new ITransportProvider() {
public String getName() {
return "SSL";
}
public IChannel openChannel(IPeer peer) {
assert getName().equals(peer.getTransportName());
Map<String,String> attrs = peer.getAttributes();
String host = attrs.get(IPeer.ATTR_IP_HOST);
String port = attrs.get(IPeer.ATTR_IP_PORT);
if (host == null) throw new IllegalArgumentException("No host name");
return new ChannelTCP(peer, host, parsePort(port), true);
}
});
addTransportProvider(new ITransportProvider() {
public String getName() {
return "PIPE";
}
public IChannel openChannel(IPeer peer) {
assert getName().equals(peer.getTransportName());
String name = peer.getAttributes().get("PipeName");
if (name == null) name = "//./pipe/TCF-Agent";
return new ChannelPIPE(peer, name);
}
});
addTransportProvider(new ITransportProvider() {
public String getName() {
return "Loop";
}
public IChannel openChannel(IPeer peer) {
assert getName().equals(peer.getTransportName());
return new ChannelLoop(peer);
}
});
}
private static int parsePort(String port) {
if (port == null) throw new Error("No port number");
try {
return Integer.parseInt(port);
}
catch (NumberFormatException x) {
IllegalArgumentException y = new IllegalArgumentException(
"Invalid value of \"Port\" attribute. Must be decimal number.");
y.initCause(x);
throw y;
}
}
public static void addTransportProvider(ITransportProvider transport) {
String name = transport.getName();
assert name != null;
synchronized (transports) {
if (transports.get(name) != null) throw new Error("Already registered: " + name);
transports.put(name, transport);
}
}
public static void removeTransportProvider(ITransportProvider transport) {
String name = transport.getName();
assert name != null;
synchronized (transports) {
if (transports.get(name) == transport) transports.remove(name);
}
}
public static IChannel openChannel(IPeer peer) {
String name = peer.getTransportName();
if (name == null) throw new Error("No transport name");
ITransportProvider transport = null;
synchronized (transports) {
transport = transports.get(name);
if (transport == null) throw new Error("Unknown transport name: " + name);
}
return transport.openChannel(peer);
}
public static void channelOpened(final AbstractChannel channel) {
assert !channels.contains(channel);
channels.add(channel);
Protocol.ChannelOpenListener[] array = listeners.toArray(new Protocol.ChannelOpenListener[listeners.size()]);
for (Protocol.ChannelOpenListener l : array) {
try {
l.onChannelOpen(channel);
}
catch (Throwable x) {
Protocol.log("Exception in channel listener", x);
}
}
}
public static void channelClosed(final AbstractChannel channel, final Throwable x) {
assert channels.contains(channel);
channels.remove(channel);
}
public static IChannel[] getOpenChannels() {
return channels.toArray(new IChannel[channels.size()]);
}
public static void addChanelOpenListener(Protocol.ChannelOpenListener listener) {
assert listener != null;
listeners.add(listener);
}
public static void removeChanelOpenListener(Protocol.ChannelOpenListener listener) {
listeners.remove(listener);
}
/**
* Transmit TCF event message.
* The message is sent to all open communication channels � broadcasted.
*
* This is internal API, TCF clients should use {@code org.eclipse.tm.tcf.protocol.Protocol}.
*/
public static void sendEvent(String service_name, String event_name, byte[] data) {
for (AbstractChannel c : channels) {
// Skip channels that are executing "redirect" command - STATE_OPENING
if (c.getState() == IChannel.STATE_OPEN) {
IService s = c.getLocalService(service_name);
if (s != null) c.sendEvent(s, event_name, data);
}
}
}
/**
* Call back after TCF messages sent by this host up to this moment are delivered
* to their intended targets. This method is intended for synchronization of messages
* across multiple channels.
*
* Note: Cross channel synchronization can reduce performance and throughput.
* Most clients don't need cross channel synchronization and should not call this method.
*
* @param done will be executed by dispatch thread after communication
* messages are delivered to corresponding targets.
*
* This is internal API, TCF clients should use {@code org.eclipse.tm.tcf.protocol.Protocol}.
*/
public static void sync(final Runnable done) {
final Set<IToken> set = new HashSet<IToken>();
ILocator.DoneSync done_sync = new ILocator.DoneSync() {
public void doneSync(IToken token) {
assert set.contains(token);
set.remove(token);
if (set.isEmpty()) done.run();
}
};
for (AbstractChannel c : channels) {
if (c.getState() == IChannel.STATE_OPEN) {
ILocator s = c.getRemoteService(ILocator.class);
if (s != null) set.add(s.sync(done_sync));
}
}
if (set.isEmpty()) Protocol.invokeLater(done);
}
}