//******************************************************************************
//
// File: ChannelGroup.java
// Package: edu.rit.mp
// Unit: Class edu.rit.mp.ChannelGroup
//
// This Java source file is copyright (C) 2009 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.mp;
import edu.rit.util.Logger;
import edu.rit.util.PrintStreamLogger;
import edu.rit.util.Range;
import edu.rit.util.Timer;
import edu.rit.util.TimerTask;
import edu.rit.util.TimerThread;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;
/**
* Class ChannelGroup provides a group of {@linkplain Channel}s for sending and
* receiving messages in the Message Protocol (MP).
*
* Creating Channels
*
* A channel group can be used to create channels in two ways: by accepting a
* connection request from another process, and by requesting a connection to
* another process.
*
* The channel group can be configured to listen to a certain host and port for
* connection requests. Configure the host and port by by calling the
* listen() method. To start accepting connection requests, call the
* startListening() method.
*
* If desired, an application can receive notification of newly created channels
* by providing a {@linkplain ConnectListener} object to the channel group's
* setConnectListener() method. Specify a connect listener object
* before calling the startListening() method, otherwise the
* application may not receive some notifications.
*
* When a connection request arrives, the channel group sets up a new channel
* object for communicating over the connection. If a connect listener has been
* registered, the channel group then passes the channel to the connect
* listener's farEndConnected() method, which does whatever the
* application needs to record the new channel's presence. The channel does not
* start sending and receiving messages until after the connect listener's
* farEndConnected() method (if any) has returned.
*
* The application can also call the channel group's connect() method
* to request a connection to another host and port. The channel group sets up a
* new channel object for communicating over the connection. If a connect
* listener has been registered, the channel group then passes the channel to
* the connect listener's nearEndConnected() method, which does
* whatever the application needs to record the new channel's presence. The
* channel does not start sending and receiving messages until after the connect
* listener's nearEndConnected() method (if any) has returned. The
* connect() method also returns the new channel.
*
* Once a connection has been set up and a channel object has been created on
* each side, the applications can use their respective channels to send and
* receive messages.
*
* If a channel group does not need to accept incoming connection requests, the
* channel group need not listen to any host and port. The channel group can
* still be used to make outgoing connection requests.
*
* Channel Group IDs
*
* Each channel group has a channel group ID. The channel group ID is an
* integer, initially 0, that can be changed by the setChannelGroupId()
* method. The channel group attaches no significance to the channel group ID;
* it is provided for the use of the application using the channel group.
*
* You can query a channel group object to determine its channel group ID. You
* can also query a channel object to determine the ID of the channel group at
* the near end of the channel and the ID of the channel group at the far end of
* the channel.
*
* Sending Messages
*
* To send a message, the application creates a message buffer (class
* {@linkplain Buf}) specifying where to get the items to be sent. The
* application calls the channel group's send() method, passing the
* channel on which to send the message, the message buffer, and the message
* tag. (If the message tag is not specified, it defaults to 0.) The channel
* group extracts the items from the message buffer and sends a message over the
* channel's connection. When the send() method returns, the message
* has been fully sent, but the message may not have been fully received yet.
*
* The far end application must receive the message from the channel at the
* other end of the connection. If no application is receiving the message, the
* send() method may block (because of flow control). This in turn may
* lead to a deadlock.
*
* At most one outgoing message at a time may be in progress on a channel. If a
* second thread tries to send a message on a channel while a first thread is
* still sending a message on that channel, the second thread will block until
* the first thread has finished sending the message.
*
* Receiving Messages
*
* To receive a message, the application creates a message buffer (class
* {@linkplain Buf}) specifying where to put the items to be received. The
* application calls the channel group's receive() method, passing the
* channel from which to receive the message, the desired message tag, and the
* message buffer. The application can specify "any channel" instead of a
* specific channel. The application can specify a range of tags or "any tag"
* instead of a specific tag. Any number of threads can have receive requests
* pending at the same time.
*
* When the channel group receives a message from a channel, the channel group
* tries to match the message with the pending receive requests. A message
* matches a receive request if (a) the message's channel is the same as the
* receive request's channel, or the receive request specified "any channel;"
* and (b) the message's tag is the same as the receive request's tag, or the
* message's tag falls within the receive request's range of tags, or the
* receive request specified "any tag;" and (c) the message's item type is the
* same as the receive request's item type (as given by the message buffer). The
* pending receive requests are maintained in FIFO order. If no receive request
* matches the message, the channel group does not read the message until such
* time as a matching receive request occurs. If more than one receive request
* matches the message, the channel group chooses the first matching receive
* request.
*
* Once the channel group has matched the incoming message with the receive
* request, the channel group reads the items from the message and stores them
* into the message buffer. If there are fewer items in the message than the
* length of the message buffer, the extra items at the end of the message
* buffer are not set to anything. If there are more items in the message than
* the length of the message buffer, the extra items are read from the message
* and discarded. Once the message has been read, the receive() method
* returns a {@linkplain Status} object reporting the channel on which the
* message arrived, the message tag, and the actual number of items in the
* message (which may or may not be the same as the number of items in the
* message buffer).
*
* If the receive requests do not match properly with the incoming messages, a
* deadlock may occur.
*
* Sending and Receiving Within the Same Process
*
* Each channel group has a "loopback" channel that is used to send messages
* within the same process. To obtain the loopback channel, call the
* loopbackChannel() method. Then one thread can send messages using
* the loopback channel while a different thread receives messages using the
* loopback channel. If the same thread both sends and receives using the
* loopback channel, a deadlock may occur.
*
* The loopback channel uses the copy() method of class {@linkplain
* Buf} to transfer data items directly from the source buffer to the
* destination buffer. The loopback channel does not do any network
* communication.
*
* Non-Blocking Send and Receive Operations
*
* The send() method described so far does a blocking send
* operation; the send() method does not return until the message has
* been fully sent. There is also a non-blocking send operation,
* sendNoWait(), which includes an {@linkplain IORequest} argument. The
* sendNoWait() method initiates the send operation and returns
* immediately. This allows the caller to continue processing while the channel
* group sends the message in a separate thread. To wait for the message to be
* fully sent, the caller must call the IORequest object's
* waitForFinish() method.
*
* Likewise, the receive() method described so far does a blocking
* receive operation; the receive() method does not return until
* the message has been fully received. There is also a non-blocking
* receive operation, receiveNoWait(), which includes an
* {@linkplain IORequest} argument. The receiveNoWait() method
* initiates the receive operation and returns immediately. This allows the
* caller to continue processing while the channel group receives the message in
* a separate thread. To wait for the message to be fully received, the caller
* must call the IORequest object's waitForFinish() method, which
* returns a {@linkplain Status} object giving the results of the receive
* operation.
*
* @author Alan Kaminsky
* @version 11-Mar-2009
*/
public class ChannelGroup
{
// Hidden data members.
// Channel group ID.
int myChannelGroupId;
// Server socket channel for accepting incoming connections, or null if not
// accepting incoming connections.
ServerSocketChannel myServerSocketChannel;
// I/O request list for matching incoming messages to receive I/O requests.
IORequestList myIORequestList;
// Alternate class loader for use when receiving objects.
ClassLoader myClassLoader;
// Loopback channel.
LoopbackChannel myLoopbackChannel;
// List of open channels.
List myChannelList;
// Accepting thread, or null if not accepting.
AcceptThread myAcceptThread;
// Registered connect listener, or null if none.
ConnectListener myConnectListener;
// For logging error messages.
Logger myLogger;
// For timeouts during channel setup.
TimerThread myTimerThread;
// Hidden helper classes.
/**
* Class AcceptThread provides a thread that accepts incoming connections.
*
* @author Alan Kaminsky
* @version 11-Mar-2009
*/
private class AcceptThread
extends Thread
{
public AcceptThread()
{
setDaemon (true);
start();
}
public void run()
{
acceptloop : for (;;)
{
// Wait for an incoming connection.
SocketChannel connection = null;
try
{
connection = myServerSocketChannel.accept();
}
catch (ClosedChannelException exc)
{
myLogger.log
("ChannelGroup: Channel closed",
exc);
break acceptloop;
}
catch (IOException exc)
{
myLogger.log
("ChannelGroup: I/O error while accepting connection",
exc);
break acceptloop;
}
// Set up channel over connection.
if (connection != null)
{
try
{
farEndConnect (connection);
}
catch (IOException exc)
{
// Clear thread's interrupted status, otherwise accept()
// above will throw an exception.
Thread.interrupted();
myLogger.log
("ChannelGroup: I/O error while setting up channel",
exc);
try { connection.close(); } catch (IOException exc2) {}
}
}
}
}
}
// Exported constructors.
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will not listen for connection requests. To listen for
* connection requests at a later time, call the listen() method
* followed by the startListening method.
*
* The channel group will log error messages on the standard error.
*/
public ChannelGroup()
{
this (new PrintStreamLogger());
}
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will listen for connection requests on the given host and
* port. To start actively listening, call the startListening()
* method.
*
* The channel group will log error messages on the standard error.
*
* @param theListenAddress Host and port at which to listen.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theListenAddress is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public ChannelGroup
(InetSocketAddress theListenAddress)
throws IOException
{
this (theListenAddress, new PrintStreamLogger());
}
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will listen for connection requests using the given server
* socket channel. The server socket channel must be bound to a host and
* port. To start actively listening, call the startListening()
* method.
*
* The channel group will log error messages on the standard error.
*
* @param theServerSocketChannel Server socket channel.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theServerSocketChannel is
* null.
* @exception IOException
* Thrown if an I/O error occurred. Thrown if
* theServerSocketChannel is not bound.
*/
public ChannelGroup
(ServerSocketChannel theServerSocketChannel)
throws IOException
{
this (theServerSocketChannel, new PrintStreamLogger());
}
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will not listen for connection requests. To listen for
* connection requests at a later time, call the listen() method
* followed by the startListening method.
*
* The channel group will log error messages using the given logger.
*
* @param theLogger Logger for error messages.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theLogger is null.
*/
public ChannelGroup
(Logger theLogger)
{
if (theLogger == null)
{
throw new NullPointerException
("ChannelGroup(): theLogger is null");
}
myIORequestList = new IORequestList();
myLoopbackChannel = new LoopbackChannel (this);
myChannelList = new LinkedList();
myChannelList.add (myLoopbackChannel);
myLogger = theLogger;
myTimerThread = new TimerThread();
myTimerThread.setDaemon (true);
myTimerThread.start();
}
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will listen for connection requests on the given host and
* port. To start actively listening, call the startListening()
* method.
*
* The channel group will log error messages using the given logger.
*
* @param theListenAddress Host and port at which to listen.
* @param theLogger Logger for error messages.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theListenAddress is null.
* Thrown if theLogger is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public ChannelGroup
(InetSocketAddress theListenAddress,
Logger theLogger)
throws IOException
{
this (theLogger);
listen (theListenAddress);
}
/**
* Construct a new channel group. The channel group ID is initially 0. The
* channel group will listen for connection requests using the given server
* socket channel. The server socket channel must be bound to a host and
* port. To start actively listening, call the startListening()
* method.
*
* The channel group will log error messages using the given logger.
*
* @param theServerSocketChannel Server socket channel.
* @param theLogger Logger for error messages.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theServerSocketChannel is
* null. Thrown if theLogger is null.
* @exception IOException
* Thrown if an I/O error occurred. Thrown if
* theServerSocketChannel is not bound.
*/
public ChannelGroup
(ServerSocketChannel theServerSocketChannel,
Logger theLogger)
throws IOException
{
this (theLogger);
listen (theServerSocketChannel);
}
// Exported operations.
/**
* Set this channel group's channel group ID.
*
* @param theChannelGroupId Channel group ID.
*/
public void setChannelGroupId
(int theChannelGroupId)
{
myChannelGroupId = theChannelGroupId;
}
/**
* Obtain this channel group's channel group ID.
*
* @return Channel group ID.
*/
public int getChannelGroupId()
{
return myChannelGroupId;
}
/**
* Obtain this channel group's listen address. This is the near end host and
* port to which this channel group is listening for connection requests. If
* this channel group is not listening for connection requests, null is
* returned.
*
* @return Near end address, or null.
*/
public synchronized InetSocketAddress listenAddress()
{
return
myServerSocketChannel == null ?
null :
(InetSocketAddress)
myServerSocketChannel.socket().getLocalSocketAddress();
}
/**
* Listen for connection requests on the given host and port. To start
* actively listening, call the startListening() method.
*
* @param theListenAddress Host and port at which to listen.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theListenAddress is null.
* @exception IllegalStateException
* (unchecked exception) Thrown if listening has already started.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public synchronized void listen
(InetSocketAddress theListenAddress)
throws IOException
{
if (theListenAddress == null)
{
throw new NullPointerException
("ChannelGroup.listen(): theListenAddress is null");
}
ServerSocketChannel channel = ServerSocketChannel.open();
channel.socket().bind (theListenAddress);
listen (channel);
}
/**
* Listen for connection requests using the given server socket channel. The
* server socket channel must be bound to a host and port. To start actively
* listening, call the startListening() method.
*
* @param theServerSocketChannel Server socket channel.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theServerSocketChannel is
* null.
* @exception IllegalStateException
* (unchecked exception) Thrown if listening has already started.
* @exception IOException
* Thrown if an I/O error occurred. Thrown if
* theServerSocketChannel is not bound.
*/
public synchronized void listen
(ServerSocketChannel theServerSocketChannel)
throws IOException
{
if (theServerSocketChannel == null)
{
throw new NullPointerException
("ChannelGroup.listen(): theServerSocketChannel is null");
}
if (! theServerSocketChannel.socket().isBound())
{
throw new IOException
("ChannelGroup.listen(): theServerSocketChannel is not bound");
}
if (myAcceptThread != null)
{
throw new IllegalStateException
("ChannelGroup.listen(): Listening has already started");
}
if (myIORequestList == null)
{
throw new IOException
("ChannelGroup.listen(): Channel group closed");
}
myServerSocketChannel = theServerSocketChannel;
}
/**
* Register the given connect listener with this channel group. Thereafter,
* this channel group will report each connected channel by calling
* theConnectListener's nearEndConnected() method (if the
* connection request originated in this process) or
* farEndConnected() method (if the connection request originated
* in another process). It is assumed that these methods will not do any
* lengthy processing and will not block the calling thread.
*
* At most one connect listener may be registered. If a connect listener is
* already registered, it is replaced with the given connect listener. If
* theConnectListener is null, any registered connect listener is
* discarded, and this channel group will not report connected channels.
*
* Call the setConnectListener() method before calling the
* startListening() method, otherwise the application may not
* receive some connection notifications.
*
* @param theConnectListener Connect listener, or null.
*/
public synchronized void setConnectListener
(ConnectListener theConnectListener)
{
myConnectListener = theConnectListener;
}
/**
* Start actively listening for connection requests.
*
* @exception IllegalStateException
* (unchecked exception) Thrown if a host and port or a server socket
* channel upon which to listen has not been specified. Thrown if
* listening has already started.
*/
public synchronized void startListening()
{
if (myServerSocketChannel == null)
{
throw new IllegalStateException
("ChannelGroup.startListening(): No server socket channel");
}
if (myAcceptThread != null)
{
throw new IllegalStateException
("ChannelGroup.listen(): Listening has already started");
}
myAcceptThread = new AcceptThread();
}
/**
* Create a new channel connected to the given far end host and port. In the
* far end computer, there must be a channel group listening to the given
* host and port. Once the connection is set up, if a connect listener has
* been registered, the channel group calls the connect listener's
* nearEndConnected() method to report the new channel.
*
* @param theFarEndAddress Host and port of far end channel group.
*
* @return New channel.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public Channel connect
(InetSocketAddress theFarEndAddress)
throws IOException
{
synchronized (this)
{
if (myIORequestList == null)
{
throw new IOException
("ChannelGroup.connect(): Channel group closed");
}
}
SocketChannel connection = null;
try
{
connection = SocketChannel.open (theFarEndAddress);
return nearEndConnect (connection);
}
catch (IOException exc)
{
// Clear thread's interrupted status.
Thread.interrupted();
if (connection != null)
{
try { connection.close(); } catch (IOException exc2) {}
}
throw exc;
}
}
/**
* Obtain this channel group's loopback channel. If this channel group is
* closed, null is returned.
*
* @return Loopback channel, or null.
*/
public synchronized Channel loopbackChannel()
{
return myLoopbackChannel;
}
/**
* Send a message to the given channel. The message uses a tag of 0. The
* message items come from the given item source buffer.
*
* The send() method does not return until the message has been
* fully sent. (The message may not have been fully received yet.)
*
* The send() method assumes that theChannel was created
* by this channel group. If not, the send() method's behavior is
* unspecified.
*
* @param theChannel Channel.
* @param theSrc Item source buffer.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theChannel is null or
* theSrc is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void send
(Channel theChannel,
Buf theSrc)
throws IOException
{
IORequest req = new IORequest();
sendNoWait (theChannel, 0, theSrc, req);
req.waitForFinish();
}
/**
* Send a message to the given channel with the given tag. The message items
* come from the given item source buffer.
*
* The send() method does not return until the message has been
* fully sent. (The message may not have been fully received yet.)
*
* The send() method assumes that theChannel was created
* by this channel group. If not, the send() method's behavior is
* unspecified.
*
* @param theChannel Channel.
* @param theTag Message tag.
* @param theSrc Item source buffer.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theChannel is null or
* theSrc is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void send
(Channel theChannel,
int theTag,
Buf theSrc)
throws IOException
{
IORequest req = new IORequest();
sendNoWait (theChannel, theTag, theSrc, req);
req.waitForFinish();
}
/**
* Send (non-blocking) a message to the given channel. The message uses a
* tag of 0. The message items come from the given item source buffer.
* theIORequest is the IORequest object to be associated with the
* send operation.
*
* The sendNoWait() method returns immediately. To wait for the
* message to be fully sent, call theIORequest.waitForFinish().
*
* The sendNoWait() method assumes that theChannel was
* created by this channel group. If not, the sendNoWait() method's
* behavior is unspecified.
*
* @param theChannel Channel.
* @param theSrc Item source buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theChannel is null,
* theSrc is null, or theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void sendNoWait
(Channel theChannel,
Buf theSrc,
IORequest theIORequest)
throws IOException
{
sendNoWait (theChannel, 0, theSrc, theIORequest);
}
/**
* Send (non-blocking) a message to the given channel with the given tag.
* The message items come from the given item source buffer.
* theIORequest is the IORequest object to be associated with the
* send operation.
*
* The sendNoWait() method returns immediately. To wait for the
* message to be fully sent, call theIORequest.waitForFinish().
*
* The sendNoWait() method assumes that theChannel was
* created by this channel group. If not, the sendNoWait() method's
* behavior is unspecified.
*
* @param theChannel Channel.
* @param theTag Message tag.
* @param theSrc Item source buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theChannel is null,
* theSrc is null, or theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void sendNoWait
(Channel theChannel,
int theTag,
Buf theSrc,
IORequest theIORequest)
throws IOException
{
// Note: This method is not synchronized. Synchronization happens inside
// theChannel.send().
// Verify preconditions.
if (myIORequestList == null)
{
throw new IOException
("ChannelGroup.sendNoWait(): Channel group closed");
}
if (theSrc == null)
{
throw new NullPointerException
("ChannelGroup.sendNoWait(): Source buffer is null");
}
theIORequest.initialize (theChannel, theTag, theTag, theSrc);
theChannel.send (theIORequest);
}
/**
* Receive a message from the given channel. If theChannel is null,
* a message will be received from any channel in this channel group. The
* message must have a tag of 0. The message items are stored in the given
* item destination buffer.
*
* The receive() method does not return until the message has been
* fully received.
*
* The receive() method assumes that theChannel was
* created by this channel group. If not, the receive() method's
* behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theDst Item destination buffer.
*
* @return Status object giving the outcome of the message reception.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public Status receive
(Channel theChannel,
Buf theDst)
throws IOException
{
IORequest req = new IORequest();
receiveNoWait (theChannel, 0, 0, theDst, req);
return req.waitForFinish();
}
/**
* Receive a message from the given channel with the given tag. If
* theChannel is null, a message will be received from any channel
* in this channel group. The message items are stored in the given item
* destination buffer.
*
* The receive() method does not return until the message has been
* fully received.
*
* The receive() method assumes that theChannel was
* created by this channel group. If not, the receive() method's
* behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theTag Message tag.
* @param theDst Item destination buffer.
*
* @return Status object giving the outcome of the message reception.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public Status receive
(Channel theChannel,
int theTag,
Buf theDst)
throws IOException
{
IORequest req = new IORequest();
receiveNoWait (theChannel, theTag, theTag, theDst, req);
return req.waitForFinish();
}
/**
* Receive a message from the given channel with the given range of tags. If
* theChannel is null, a message will be received from any channel
* in this channel group. If theTagRange is null, a message will be
* received with any tag. The message items are stored in the given item
* destination buffer.
*
* The receive() method does not return until the message has been
* fully received.
*
* The receive() method assumes that theChannel was
* created by this channel group. If not, the receive() method's
* behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theTagRange Message tag range, or null to receive any tag.
* @param theDst Item destination buffer.
*
* @return Status object giving the outcome of the message reception.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public Status receive
(Channel theChannel,
Range theTagRange,
Buf theDst)
throws IOException
{
IORequest req = new IORequest();
if (theTagRange == null)
{
receiveNoWait
(theChannel, Integer.MIN_VALUE, Integer.MAX_VALUE, theDst, req);
}
else
{
receiveNoWait
(theChannel, theTagRange.lb(), theTagRange.ub(), theDst, req);
}
return req.waitForFinish();
}
/**
* Receive (non-blocking) a message from the given channel. If
* theChannel is null, a message will be received from any channel
* in this channel group. The message must have a tag of 0. The message
* items are stored in the given item destination buffer.
* theIORequest is the IORequest object to be associated with the
* receive operation.
*
* The receiveNoWait() method returns immediately. To wait for the
* message to be fully received, call theIORequest.waitForFinish().
*
* The receiveNoWait() method assumes that theChannel was
* created by this channel group. If not, the receiveNoWait()
* method's behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theDst Item destination buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null or
* theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void receiveNoWait
(Channel theChannel,
Buf theDst,
IORequest theIORequest)
throws IOException
{
receiveNoWait (theChannel, 0, 0, theDst, theIORequest);
}
/**
* Receive (non-blocking) a message from the given channel with the given
* tag. If theChannel is null, a message will be received from any
* channel in this channel group. The message items are stored in the given
* item destination buffer. theIORequest is the IORequest object to
* be associated with the receive operation.
*
* The receiveNoWait() method returns immediately. To wait for the
* message to be fully received, call theIORequest.waitForFinish().
*
* The receiveNoWait() method assumes that theChannel was
* created by this channel group. If not, the receiveNoWait()
* method's behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theTag Message tag.
* @param theDst Item destination buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null or
* theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void receiveNoWait
(Channel theChannel,
int theTag,
Buf theDst,
IORequest theIORequest)
throws IOException
{
receiveNoWait (theChannel, theTag, theTag, theDst, theIORequest);
}
/**
* Receive (non-blocking) a message from the given channel with the given
* range of tags. If theChannel is null, a message will be received
* from any channel in this channel group. If theTagRange is null,
* a message will be received with any tag. The message items are stored in
* the given item destination buffer. theIORequest is the IORequest
* object to be associated with the receive operation.
*
* The receiveNoWait() method returns immediately. To wait for the
* message to be fully received, call theIORequest.waitForFinish().
*
* The receiveNoWait() method assumes that theChannel was
* created by this channel group. If not, the receiveNoWait()
* method's behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theTagRange Message tag range, or null to receive any tag.
* @param theDst Item destination buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null or
* theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void receiveNoWait
(Channel theChannel,
Range theTagRange,
Buf theDst,
IORequest theIORequest)
throws IOException
{
if (theTagRange == null)
{
receiveNoWait
(theChannel,
Integer.MIN_VALUE,
Integer.MAX_VALUE,
theDst,
theIORequest);
}
else
{
receiveNoWait
(theChannel,
theTagRange.lb(),
theTagRange.ub(),
theDst,
theIORequest);
}
}
/**
* Receive (non-blocking) a message from the given channel with the given
* tag range. If theChannel is null, a message will be received
* from any channel in this channel group. The message items are stored in
* the given item destination buffer. theIORequest is the IORequest
* object to be associated with the receive operation.
*
* The receiveNoWait() method returns immediately. To wait for the
* message to be fully received, call theIORequest.waitForFinish().
*
* The receiveNoWait() method assumes that theChannel was
* created by this channel group. If not, the receiveNoWait()
* method's behavior is unspecified.
*
* @param theChannel Channel, or null to receive from any channel.
* @param theTagLb Message tag range lower bound.
* @param theTagUb Message tag range upper bound.
* @param theDst Item destination buffer.
* @param theIORequest IORequest object.
*
* @exception NullPointerException
* (unchecked exception) Thrown if theDst is null or
* theIORequest is null.
* @exception IOException
* Thrown if an I/O error occurred.
*/
private void receiveNoWait
(Channel theChannel,
int theTagLb,
int theTagUb,
Buf theDst,
IORequest theIORequest)
throws IOException
{
// Note: This method is not synchronized. Synchronization happens inside
// myIORequestList.add().
// Verify preconditions.
if (myIORequestList == null)
{
throw new IOException
("ChannelGroup.receiveNoWait(): Channel group closed");
}
if (theDst == null)
{
throw new NullPointerException
("ChannelGroup.receiveNoWait(): Destination buffer is null");
}
if (theChannel != null)
{
synchronized (theChannel)
{
// Check whether channel is closed.
if (theChannel.myReadState == Channel.READ_CLOSED)
{
throw new IOException
("ChannelGroup.receiveNoWait(): Channel closed");
}
}
}
theIORequest.initialize (theChannel, theTagLb, theTagUb, theDst);
myIORequestList.add (theIORequest);
}
/**
* Specify an alternate class loader for this channel group. When objects
* are received in a message via this channel group, the given class loader
* will be used to load the objects' classes. If
* setAlternateClassLoader() is never called, or if
* theClassLoader is null, an alternate class loader will not be
* used.
*
* @param theClassLoader Alternate class loader, or null.
*/
public synchronized void setAlternateClassLoader
(ClassLoader theClassLoader)
{
myClassLoader = theClassLoader;
}
/**
* Close this channel group. Any pending receive requests will fail with a
* {@linkplain ChannelGroupClosedException}.
*/
public synchronized void close()
{
// Stop listening for connections.
if (myServerSocketChannel != null)
{
try
{
myServerSocketChannel.close();
}
catch (IOException exc)
{
}
}
// Close all channels.
if (myChannelList != null)
{
while (! myChannelList.isEmpty())
{
myChannelList.get(0).close();
}
}
// Report failure to all pending receive requests.
if (myIORequestList != null)
{
myIORequestList.reportFailure
(new ChannelGroupClosedException ("Channel group closed"));
}
// Enable garbage collection of fields.
myServerSocketChannel = null;
myIORequestList = null;
myClassLoader = null;
myLoopbackChannel = null;
myChannelList = null;
myAcceptThread = null;
}
/**
* Finalize this channel group.
*/
protected void finalize()
{
close();
}
/**
* Dump the state of this channel group on the given print stream. For
* debugging.
*
* @param out Print stream.
* @param prefix String to print at the beginning of each line.
*/
public void dump
(PrintStream out,
String prefix)
{
out.println (prefix+getClass().getName()+"@"+Integer.toHexString(System.identityHashCode(this)));
out.println (prefix+"myChannelGroupId = "+myChannelGroupId);
out.println (prefix+"myServerSocketChannel = "+myServerSocketChannel);
out.println (prefix+"myIORequestList:");
myIORequestList.dump (out, prefix+"\t");
out.println (prefix+"myClassLoader = "+myClassLoader);
out.println (prefix+"myLoopbackChannel = "+myLoopbackChannel);
out.println (prefix+"myChannelList:");
out.println (prefix+"\t"+myChannelList.size()+" entries");
for (Channel c : myChannelList)
{
c.dump (out, prefix+"\t");
}
out.println (prefix+"myAcceptThread = "+myAcceptThread);
out.println (prefix+"myConnectListener = "+myConnectListener);
out.println (prefix+"myLogger = "+myLogger);
out.println (prefix+"myTimerThread = "+myTimerThread);
}
// Hidden operations.
/**
* Create a new network channel using the given socket channel. The
* connection request originated from the near end. If this channel group is
* closed, null is returned.
*
* @param theSocketChannel Socket channel.
*
* @return New channel.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
Channel nearEndConnect
(SocketChannel theSocketChannel)
throws IOException
{
// Note: This method is not synchronized. Synchronization happens inside
// createNetworkChannel().
// Turn on socket's TCP no-delay option.
Socket socket = theSocketChannel.socket();
socket.setTcpNoDelay (true);
// Send channel group ID to far end.
ByteBuffer buf = ByteBuffer.allocate (4);
buf.putInt (myChannelGroupId);
buf.flip();
if (theSocketChannel.write (buf) != 4)
{
throw new IOException
("ChannelGroup.nearEndConnect(): Cannot send channel group ID");
}
// Receive channel group ID from far end with a 30-second timeout.
buf.clear();
final Thread thread = Thread.currentThread();
Timer timer = myTimerThread.createTimer (new TimerTask()
{
public void action (Timer theTimer)
{
thread.interrupt();
}
});
timer.start (30000L);
if (theSocketChannel.read (buf) != 4)
{
throw new IOException
("ChannelGroup.nearEndConnect(): Cannot receive channel group ID");
}
timer.stop();
buf.flip();
int farChannelGroupId = buf.getInt();
// Set up channel.
Channel channel =
createNetworkChannel (theSocketChannel, farChannelGroupId);
// Inform listener if any.
if (myConnectListener != null)
{
myConnectListener.nearEndConnected (this, channel);
}
// Start the channel sending and receiving messages.
channel.start();
return channel;
}
/**
* Create a new network channel using the given socket channel. The
* connection request originated from the far end. If this channel group is
* closed, null is returned.
*
* @param theSocketChannel Socket channel.
*
* @return New channel.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
Channel farEndConnect
(SocketChannel theSocketChannel)
throws IOException
{
// Note: This method is not synchronized. Synchronization happens inside
// createNetworkChannel().
// Turn on socket's TCP no-delay option.
Socket socket = theSocketChannel.socket();
socket.setTcpNoDelay (true);
// Start a 30-second timeout for receiving channel group ID.
final Thread thread = Thread.currentThread();
Timer timer = myTimerThread.createTimer (new TimerTask()
{
public void action (Timer theTimer)
{
thread.interrupt();
}
});
timer.start (30000L);
try
{
// Receive channel group ID from far end.
ByteBuffer buf = ByteBuffer.allocate (4);
if (theSocketChannel.read (buf) != 4)
{
throw new IOException
("ChannelGroup.farEndConnect(): Cannot receive channel group ID");
}
timer.stop();
buf.flip();
int farChannelGroupId = buf.getInt();
// Send channel group ID to far end.
buf.clear();
buf.putInt (myChannelGroupId);
buf.flip();
if (theSocketChannel.write (buf) != 4)
{
throw new IOException
("ChannelGroup.farEndConnect(): Cannot send channel group ID");
}
// Set up channel.
Channel channel =
createNetworkChannel (theSocketChannel, farChannelGroupId);
// Inform listener if any.
if (myConnectListener != null)
{
myConnectListener.farEndConnected (this, channel);
}
// Start the channel sending and receiving messages.
channel.start();
return channel;
}
// Stop timer when an IOException is thrown.
catch (IOException exc)
{
timer.stop();
throw exc;
}
}
/**
* Create a new network channel using the given socket channel. If this
* channel group is closed, null is returned.
*
* @param theSocketChannel Socket channel.
* @param theFarChannelGroupId Far end channel group ID.
*
* @return New channel, or null.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
synchronized Channel createNetworkChannel
(SocketChannel theSocketChannel,
int theFarChannelGroupId)
throws IOException
{
Channel channel = null;
if (myIORequestList != null)
{
channel =
new NetworkChannel
(this, theSocketChannel, theFarChannelGroupId);
myChannelList.add (channel);
}
return channel;
}
/**
* Remove the given channel from this channel group.
*
* @param Channel.
*/
synchronized void removeChannel
(Channel channel)
{
if (myChannelList != null)
{
myChannelList.remove (channel);
}
}
}
Ads help maintain this website.