package org.jgroups; import org.jgroups.annotations.MBean; import org.jgroups.annotations.ManagedAttribute; import org.jgroups.logging.Log; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.DefaultSocketFactory; import org.jgroups.util.SocketFactory; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** * A channel represents a group communication endpoint (like BSD datagram sockets). A client joins a * group by connecting the channel to a group and leaves it by disconnecting. Messages sent over the * channel are received by all group members that are connected to the same group (that is, all * members that have the same group name). * <p/> * * The FSM for a channel is roughly as follows: a channel is created (<em>unconnected</em>). The * channel is connected to a group (<em>connected</em>). Messages can now be sent and received. The * channel is disconnected from the group (<em>unconnected</em>). The channel could now be connected * to a different group again. The channel is closed (<em>closed</em>). * <p/> * * Only a single sender is allowed to be connected to a channel at a time, but there can be more * than one channel in an application. * <p/> * * Messages can be sent to the group members using the <em>send</em> method and messages can be * received setting a {@link Receiver} in {@link #setReceiver(Receiver)} and implementing the * {@link Receiver#receive(Message)} callback. * <p> * * A channel instance is created using the public constructor. * <p/> * Various degrees of sophistication in message exchange can be achieved using building blocks on * top of channels; e.g., light-weight groups, synchronous message invocation, or remote method * calls. Channels are on the same abstraction level as sockets, and should really be simple to use. * Higher-level abstractions are all built on top of channels. * * @author Bela Ban * @since 2.0 * @see java.net.DatagramPacket * @see java.net.MulticastSocket * @see JChannel */ @MBean(description="Channel") public abstract class Channel /* implements Transport */ { protected UpHandler up_handler=null; // when set, <em>all</em> events are passed to it ! protected Set<ChannelListener> channel_listeners=null; protected Receiver receiver=null; protected SocketFactory socket_factory=new DefaultSocketFactory(); @ManagedAttribute(description="Whether or not to discard messages sent by this channel",writable=true) protected boolean discard_own_messages=false; protected abstract Log getLog(); public abstract ProtocolStack getProtocolStack(); public SocketFactory getSocketFactory() { return socket_factory; } public void setSocketFactory(SocketFactory factory) { socket_factory=factory; ProtocolStack stack=getProtocolStack(); Protocol prot=stack != null? stack.getTopProtocol() : null; if(prot != null) prot.setSocketFactory(factory); } /** * Connects the channel to a group. The client is now able to receive group messages, views and * to send messages to (all or single) group members. This is a null operation if already * connected. * <p> * * All channels with the same name form a group, that means all messages sent to the group will * be received by all channels connected to the same cluster name. * <p> * * @param cluster_name * The name of the channel to connect to. * @exception Exception * The protocol stack cannot be started * @exception IllegalStateException * The channel is closed */ abstract public void connect(String cluster_name) throws Exception; /** * Connects this channel to a group and gets a state from a specified state provider. * <p/> * * This method essentially invokes * <code>connect<code> and <code>getState<code> methods successively. * If FLUSH protocol is in channel's stack definition only one flush is executed for both connecting and * fetching state rather than two flushes if we invoke <code>connect<code> and <code>getState<code> in succession. * * If the channel is closed an exception will be thrown. * * * @param cluster_name the cluster name to connect to. Cannot be null. * @param target the state provider. If null state will be fetched from coordinator, unless this channel is coordinator. * @param timeout the timeout for state transfer. * * @exception Exception Connecting to the cluster or state transfer was not successful * @exception IllegalStateException The channel is closed and therefore cannot be used * */ abstract public void connect(String cluster_name, Address target, long timeout) throws Exception; /** * Disconnects the channel if it is connected. If the channel is closed or disconnected, this * operation is ignored<br/> * The channel can then be connected to the same or a different cluster again. * * @see #connect(String) */ abstract public void disconnect(); /** * Destroys the channel and its associated resources (e.g., the protocol stack). After a channel * has been closed, invoking methods on it throws the <code>ChannelClosed</code> exception (or * results in a null operation). It is a null operation if the channel is already closed. * <p> * If the channel is connected to a group, <code>disconnect()</code> will be called first. */ abstract public void close(); /** * Determines whether the channel is open; ie. the protocol stack has been created (may not be * connected though). * * @return true is channel is open, false otherwise */ abstract public boolean isOpen(); /** * Determines whether the channel is connected to a group. * * @return true if channel is connected to cluster (group) and can send/receive messages, false otherwise */ abstract public boolean isConnected(); /** * Returns a map of statistics of the various protocols and of the channel itself. * * @return Map<String,Map>. A map where the keys are the protocols ("channel" pseudo key is used * for the channel itself") and the values are property maps. */ public abstract Map<String,Object> dumpStats(); /** * Sends a message. The message contains * <ol> * <li>a destination address (Address). A <code>null</code> address sends the message to all * group members. * <li>a source address. Can be left empty as it will be assigned automatically * <li>a byte buffer. The message contents. * <li>several additional fields. They can be used by application programs (or patterns). E.g. a * message ID, flags etc * </ol> * * @param msg * The message to be sent. Destination and buffer should be set. A null destination * means to send to all group members. * @exception IllegalStateException * thrown if the channel is disconnected or closed */ abstract public void send(Message msg) throws Exception; /** * Helper method to create a Message with given parameters and invoke {@link #send(Message)}. * * @param dst * Destination address for message. If null, message will be sent to all current group * members * @param obj * A serializable object. Will be marshalled into the byte buffer of the Message. If it * is <em>not</em> serializable, an exception will be thrown * @throws Exception * exception thrown if message sending was not successful */ abstract public void send(Address dst, Object obj) throws Exception; /** * Sends a message. See {@link #send(Address,byte[],int,int)} for details * * @param dst * destination address for message. If null, message will be sent to all current group * members * @param buf * buffer message payload * @throws Exception * exception thrown if message sending was not successful */ abstract public void send(Address dst, byte[] buf) throws Exception; /** * Sends a message to a destination. * * @param dst * The destination address. If null, the message will be sent to all cluster nodes (= * group members) * @param buf * The buffer to be sent * @param offset * The offset into the buffer * @param length * The length of the data to be sent. Has to be <= buf.length - offset. This will send * <code>length</code> bytes starting at <code>offset</code> * @throws Exception * If send() failed */ abstract public void send(Address dst, byte[] buf, int offset, int length) throws Exception; /** * Enables access to event mechanism of a channel and is normally not used by clients directly. * * @param sends an Event to a specific protocol layer and receive a response. * @return a response from a particular protocol layer targeted by Event parameter */ public Object down(Event evt) { return null; } /** * Gets the current view. The view may only be available after a successful * <code>connect()</code>. The result of calling this method on an unconnected channel is * implementation defined (may return null). Calling this method on a closed channel returns a * null view. * * @return The current view. */ abstract public View getView(); /** * Returns the channel's own address. The result of calling this method on an unconnected channel * is implementation defined (may return null). Calling this method on a closed channel returns * null. Addresses can be used as destination in the <code>send()</code> operation. * * @return The channel's address (opaque) */ abstract public Address getAddress(); /** * Returns the logical name of this channel if set. * * @return The logical name or null (if not set) */ abstract public String getName(); /** * Returns the logical name of a given member. The lookup is from the local cache of logical * address / logical name mappings and no remote communication is performed. * * @param member * @return The logical name for <code>member</code> */ abstract public String getName(Address member); /** * Sets the logical name for the channel. The name will stay associated with this channel for the * channel's lifetime (until close() is called). This method should be called <em>before</em> * calling connect(). * * @param name */ abstract public void setName(String name); /** * Returns the cluster name of the group of which the channel is a member. This is the object * that was the argument to <code>connect()</code>. Calling this method on a closed channel * returns <code>null</code>. * * @return The cluster name */ abstract public String getClusterName(); public String getProperties() { return "n/a"; } /** * Sets this channel event handler to be a recipient off all events . These will not be received * by the channel (except connect/disconnect, state retrieval and the like). This can be used by * building blocks on top of a channel; thus the channel is used as a pass-through medium, and * the building blocks take over some of the channel's tasks. However, tasks such as connection * management and state transfer is still handled by the channel. * * @param the * handler to handle channel events */ public void setUpHandler(UpHandler up_handler) { this.up_handler=up_handler; } /** * Returns UpHandler installed for this channel * * @return the installed UpHandler implementation */ public UpHandler getUpHandler() { return up_handler; } /** * Adds a ChannelListener instance that will be notified when a channel event such as connect, * disconnect or close occurs. * * @param listener * to be notified */ public synchronized void addChannelListener(ChannelListener listener) { if(listener == null) return; if(channel_listeners == null) channel_listeners=new CopyOnWriteArraySet<ChannelListener>(); channel_listeners.add(listener); } /** * Removes a ChannelListener previously installed * * @param listener to be removed */ public synchronized void removeChannelListener(ChannelListener listener) { if(channel_listeners != null && listener != null) channel_listeners.remove(listener); } /** * Clears all installed ChannelListener instances * */ public synchronized void clearChannelListeners() { if(channel_listeners != null) channel_listeners.clear(); } /** * Sets the receiver for this channel. Receiver will in turn handle all messages, view changes, * implement state transfer logic and so on. * * @param r the receiver instance for this channel * @see Receiver * * */ public void setReceiver(Receiver r) { receiver=r; } /** * Returns a receiver for this channel if it has been installed using * {@link Channel#setReceiver(Receiver)} , null otherwise * * @return a receiver installed on this channel */ public Receiver getReceiver() { return receiver; } /** * When set to true, all messages sent by a member A will be discarded by A. * * @param flag */ public void setDiscardOwnMessages(boolean flag) {discard_own_messages=flag;} /** * Returns true if this channel will discard its own messages, false otherwise * * @return */ public boolean getDiscardOwnMessages() {return discard_own_messages;} abstract public boolean flushSupported(); /** * Performs a partial flush in a cluster for flush participants. * <p/> * All pending messages are flushed out only for the flush participants. The remaining members in * a cluster are not included in the flush. The flush participants should be a proper subset of a * current view. * <p/> * * @param automatic_resume * if true call {@link #stopFlush()} after the flush * @see #startFlush(boolean) */ abstract public void startFlush(List<Address> flushParticipants, boolean automatic_resume) throws Exception; /** * Will perform a flush of the system, ie. all pending messages are flushed out of the system and * all members ack their reception. After this call returns, no member will be sending any * messages until {@link #stopFlush()} is called. * <p/> * In case of flush collisions, a random sleep time backoff algorithm is employed and the flush * is reattempted for numberOfAttempts. Therefore this method is guaranteed to return after * timeout x numberOfAttempts milliseconds. * * @param automatic_resume * if true call {@link #stopFlush()} after the flush */ abstract public void startFlush(boolean automatic_resume) throws Exception; abstract public void stopFlush(); abstract public void stopFlush(List<Address> flushParticipants); /** * Retrieves the full state from the target member. * <p> * State transfer is initiated by invoking getState on this channel. The state provider in turn * invokes {@link MessageListener#getState(java.io.OutputStream)} callback and sends a state to * this node, the state receiver. After the state arrives to the state receiver * {@link MessageListener#setState(java.io.InputStream)} callback is invoked to install the * state. * * @param target * The state provider. If null the coordinator is used by default * @param timeout * The number of milliseconds to wait for the operation to complete successfully. 0 * waits until the state has been received * * @see MessageListener#getState(java.io.OutputStream) * @see MessageListener#setState(java.io.InputStream) * * @exception IllegalStateException * The channel was closed or disconnected, or the flush (if present) failed * @exception StateTransferException * raised if there was a problem during the state transfer */ abstract public void getState(Address target, long timeout) throws Exception; protected void notifyChannelConnected(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelConnected(c); } catch(Throwable t) { getLog().error("exception in channelConnected() callback", t); } } } protected void notifyChannelDisconnected(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelDisconnected(c); } catch(Throwable t) { getLog().error("exception in channelDisonnected() callback", t); } } } protected void notifyChannelClosed(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelClosed(c); } catch(Throwable t) { getLog().error("exception in channelClosed() callback", t); } } } }