/******************************************************************************* * 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 *******************************************************************************/ package org.eclipse.tcf.internal.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.tcf.core.AbstractChannel; import org.eclipse.tcf.core.ChannelPIPE; import org.eclipse.tcf.core.ChannelTCP; 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.ITransportProvider; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.ILocator; /** * Class TansportManager provides static methods for other core and internal TCF * classes which concern the maintenance of Channels, ChannelOpenListeners and * TransportProviders </br> * * These methods are used by Protocol, TransientPeer, AbstractChannel, and others. */ public class TransportManager { /** * Collection of Channels */ private static final Collection<AbstractChannel> channels = new LinkedList<AbstractChannel>(); /** * Collection of ChannelOpenListeners */ private static final Collection<Protocol.ChannelOpenListener> listeners = new LinkedList<Protocol.ChannelOpenListener>(); /** * Map of TransportProviders and their names */ 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); } }); } /** * Converts a port number from String representation to int * * @param port port number * @return port number as an int */ 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; } } /** * Adds different transport providers to the private collection of * transports, if a transport was already in the collection, then it a Error * is thrown * * This is internal API, TCF clients should use {@code org.eclipse.tcf.protocol.Protocol} * * @param transport * TransportProviders such as TCP, Pipe, etc.. */ 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); } } /** * Remove transport providers to the private collection of transports, if a * transport if a transport was already in the collection * * This is internal API, TCF clients should use {@code org.eclipse.tcf.protocol.Protocol} * * @param transport TransportProviders such as TCP, Pipe, etc.. */ public static void removeTransportProvider(ITransportProvider transport) { String name = transport.getName(); assert name != null; synchronized (transports) { if (transports.get(name) == transport) transports.remove(name); } } /** * Opens the Channel to which the peer belongs to, using the underlying * TransportProvider. The channel is not added to the list of open channels * yet because the channel is not fully open. * * @param peer * on which to open the channel * @return the channel instance to be opened */ 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); } /** * Adds the channel to the collection of open channels. The channel is fully * open by this time. * * @param channel the channel */ 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); } } } /** * Removes the channel from the collection of channels. This method is called by the Channel whenever it closes. * * @param channel channel to close * @param x Error to throw */ public static void channelClosed(final AbstractChannel channel, final Throwable x) { assert channels.contains(channel); channels.remove(channel); } /** * Returns an array of all the open channels * * This is internal API, TCF clients should use {@code org.eclipse.tcf.protocol.Protocol} */ public static IChannel[] getOpenChannels() { return channels.toArray(new IChannel[channels.size()]); } /** * Add a Channel Open Listener, i.e: a listener that will be notified when a channel is opened * * This is internal API, TCF clients should use {@code org.eclipse.tcf.protocol.Protocol} * * @param listener listener to be added */ public static void addChanelOpenListener(Protocol.ChannelOpenListener listener) { assert listener != null; listeners.add(listener); } /** * Remove a Channel Open Listener, i.e: a listener that will be notified when a channel is opened * * This is internal API, TCF clients should use {@code org.eclipse.tcf.protocol.Protocol} * * @param listener to be removed */ 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.tcf.protocol.Protocol}. * * @param service_name service name * @param event_name * @param data */ 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.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); } }