/* $Id$ */
package ibis.ipl.impl;
import ibis.io.DataOutputStream;
import ibis.io.Replacer;
import ibis.io.SerializationFactory;
import ibis.io.SerializationOutput;
import ibis.ipl.AlreadyConnectedException;
import ibis.ipl.ConnectionClosedException;
import ibis.ipl.ConnectionFailedException;
import ibis.ipl.ConnectionTimedOutException;
import ibis.ipl.ConnectionsFailedException;
import ibis.ipl.IbisConfigurationException;
import ibis.ipl.PortType;
import ibis.ipl.SendPortDisconnectUpcall;
import ibis.util.TypedProperties;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of the {@link ibis.ipl.SendPort} interface, to be extended
* by specific Ibis implementations.
*/
public abstract class SendPort extends Manageable implements ibis.ipl.SendPort {
/** Debugging output. */
private static final Logger logger = LoggerFactory.getLogger("ibis.ipl.impl.SendPort");
private static final String ALLOW_COMM_IN_UPCALL = "ibis.upcall.communication";
private static final String ALLOW_CONN_IN_UPCALL = "ibis.upcall.connections";
private static final String[][] propertiesList = new String[][] {
{ ALLOW_COMM_IN_UPCALL, "false",
"Boolean: when set, communication is allowed from inside upcalls,"
+ " without first calling finish()."},
{ ALLOW_CONN_IN_UPCALL, "false",
"Boolean: when set, connection setup is allowed from inside upcalls,"
+ " without first calling finish()."},
};
/** The type of this port. */
public final PortType type;
/** The name of this port. */
public final String name;
/** The identification of this sendport. */
public final SendPortIdentifier ident;
/** Set when connection downcalls are supported. */
private final boolean connectionDowncalls;
/** Connection upcall handler, or <code>null</code>. */
public final SendPortDisconnectUpcall connectUpcall;
/**
* The connections lost since the last call to {@link #lostConnections()}.
*/
protected ArrayList<ReceivePortIdentifier> lostConnections
= new ArrayList<ReceivePortIdentifier>();
/** The current connections. */
protected HashMap<ReceivePortIdentifier, SendPortConnectionInfo> receivers
= new HashMap<ReceivePortIdentifier, SendPortConnectionInfo>();
/** Set when a message is currently being used. */
private boolean aMessageIsAlive = false;
/**
* Counts the number of threads waiting for a message in
* a {@link #newMessage()} call.
*/
private int waitingForMessage = 0;
/** Set when this port is closed. */
private boolean closed = false;
/** The Ibis instance of this send port. */
protected Ibis ibis;
/** Object replacer for serialization streams. */
private final Replacer replacer;
/** The serialization output stream. */
protected SerializationOutput out;
/** The underlying data output stream. */
protected DataOutputStream dataOut;
/** The write message for this port. */
protected final WriteMessage w;
/** Collected exceptions for multicast ports. */
private CollectedWriteException collectedExceptions;
/** Properties. */
protected final Properties properties;
/**
* The number of messages sent with this sendport. Actually counts the
* number of finish() calls.
*/
private long nMessages = 0;
/**
* The number of bytes in these messages. Counted once, even for
* one-to-many sendports.
*/
private long messageBytes = 0;
/**
* The total number of bytes written for these messages. In contrast
* to messageBytes, this one counts the number of bytes actually
* put on the network. If the message goes through an N-way output
* stream splitter, the message is counted N times. See the counting
* in ibis.io.
*/
private long bytes = 0;
/**
* Cumulative value of totalWritten(), including in-between calls of
* resetWritten(). Note that totalWritten() and resetWritten() can be
* redefined by subclasses.
*/
private long prevBytes = 0;
/** Counts the number of connections set up with this sendport. */
private long nConnections = 0;
/**
* Counts the number of times a connection was lost (as opposed to
* closed).
*/
private long nLostConnections = 0;
/** Counts the number of connections that were explicitly closed. */
private long nClosedConnections = 0;
/**
* When set, allow newMessage calls in message upcalls without
* first calling finish().
*/
private final boolean allowCommunicationInUpcall;
/**
* When set, allow connection setup in message upcalls without
* first calling finish().
*/
private final boolean allowConnectionsInUpcall;
/**
* Constructs a <code>SendPort</code> with the specified parameters.
* Note that all property checks are already performed in the
* <code>Ibis.createSendPort</code> methods.
* @param ibis the ibis instance.
* @param type the port type.
* @param name the name of the <code>SendPort</code>.
* @param connectUpcall the connection upcall object, or <code>null</code>.
* @param properties the port properties.
* @exception IOException is thrown in case of trouble.
*/
@SuppressWarnings("unchecked")
protected SendPort(Ibis ibis, PortType type, String name,
SendPortDisconnectUpcall connectUpcall, Properties properties) throws IOException {
this.ibis = ibis;
this.type = type;
this.name = name;
this.ident = ibis.createSendPortIdentifier(name, ibis.ident);
this.connectionDowncalls = type.hasCapability(PortType.CONNECTION_DOWNCALLS);
this.connectUpcall = connectUpcall;
this.properties = ibis.properties();
if (properties != null) {
for (Enumeration<String> e = (Enumeration<String>)properties.propertyNames(); e.hasMoreElements();) {
String key = e.nextElement();
String value = properties.getProperty(key);
this.properties.setProperty(key, value);
}
}
String replacerName = this.properties.getProperty("ibis.serialization.replacer");
if (replacerName != null) {
try {
Class<? extends Replacer> replacerClass = (Class<? extends Replacer>) Class.forName(replacerName);
this.replacer = replacerClass.newInstance();
} catch(Throwable e) {
throw new IOException("Could not instantiate replacer class "
+ replacerName);
}
} else {
this.replacer = null;
}
TypedProperties tp = new TypedProperties(this.properties);
allowCommunicationInUpcall = tp.getBooleanProperty(ALLOW_COMM_IN_UPCALL, false);
allowConnectionsInUpcall = tp.getBooleanProperty(ALLOW_CONN_IN_UPCALL, false);
ibis.register(this);
if (logger.isDebugEnabled()) {
logger.debug(ibis.identifier() + ": Sendport '" + name
+ "' created");
}
w = createWriteMessage();
addValidKey("Messages");
addValidKey("MessageBytes");
addValidKey("Bytes");
addValidKey("Connections");
addValidKey("LostConnections");
addValidKey("ClosedConnections");
}
/**
* Returns a map mapping hard-coded property names to their descriptions.
*
* @return the name/description map.
*/
public static Map<String, String> getDescriptions() {
Map<String, String> result = new LinkedHashMap<String, String>();
for (String[] element : propertiesList) {
result.put(element[0], element[2]);
}
return result;
}
public synchronized Map<IbisIdentifier, Set<String>> getConnectionTypes() {
HashMap<IbisIdentifier, Set<String>> result = new HashMap<IbisIdentifier, Set<String>>();
for (ReceivePortIdentifier port : receivers.keySet()) {
SendPortConnectionInfo i = receivers.get(port);
if (i != null) {
IbisIdentifier id = port.ibis;
Set<String> s = result.get(id);
if (s == null) {
s = new HashSet<String>();
}
s.add(i.connectionType());
result.put(id, s);
}
}
return result;
}
/**
* Returns a list of recognized properties.
*/
public static List<String> getPropertyNames() {
ArrayList<String> result = new ArrayList<String>();
for (String[] element : propertiesList) {
result.add(element[0]);
}
return result;
}
protected synchronized void updateProperties() {
setProperty("ClosedConnections", "" + nClosedConnections);
setProperty("LostConnections", "" + nLostConnections);
setProperty("Connections", "" + nConnections);
setProperty("Messages", "" + nMessages);
setProperty("MessageBytes", "" + messageBytes);
setProperty("Bytes", "" + bytes);
}
protected void doProperties(Map<String, String> properties) {
for (Map.Entry<String,String> entry : properties.entrySet()) {
doProperty(entry.getKey(), entry.getValue());
}
}
protected void doProperty(String key, String value) {
if (key.equals("Bytes")) {
bytes = Long.parseLong(value);
prevBytes = Long.parseLong(value);
resetWritten();
} else if (key.equals("ClosedConnections")) {
nClosedConnections = Long.parseLong(value);
} else if (key.equals("nConnections")) {
nConnections = Long.parseLong(value);
} else if (key.equals("Messages")) {
nMessages = Long.parseLong(value);
} else if (key.equals("MessageBytes")) {
messageBytes = Long.parseLong(value);
} else if (key.equals("LostConnections")) {
nLostConnections = Long.parseLong(value);
}
}
/**
* Creates a new write message. May be redefined.
* @return the new write message.
*/
protected WriteMessage createWriteMessage() {
return new WriteMessage(this);
}
protected long totalWritten() {
return dataOut.bytesWritten();
}
protected void resetWritten() {
dataOut.resetBytesWritten();
}
private void createOut() throws IOException {
String serialization;
if (type.hasCapability(PortType.SERIALIZATION_DATA)) {
serialization = "data";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT_SUN)) {
serialization = "sun";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT_IBIS)) {
serialization = "ibis";
} else if (type.hasCapability(PortType.SERIALIZATION_OBJECT)) {
serialization = "object";
} else {
serialization = "byte";
}
out = SerializationFactory.createSerializationOutput(serialization,
dataOut);
if (replacer != null) {
out.setReplacer(replacer);
}
}
public PortType getPortType() {
return type;
}
public synchronized ibis.ipl.ReceivePortIdentifier[] lostConnections() {
if (! connectionDowncalls) {
throw new IbisConfigurationException("SendPort.lostConnections()"
+ " called but connectiondowncalls not configured");
}
ibis.ipl.ReceivePortIdentifier[] result = lostConnections.toArray(
new ibis.ipl.ReceivePortIdentifier[lostConnections.size()]);
lostConnections.clear();
return result;
}
public String name() {
return name;
}
public ibis.ipl.SendPortIdentifier identifier() {
return ident;
}
public void connect(ibis.ipl.ReceivePortIdentifier receiver)
throws ConnectionFailedException {
connect(receiver, 0, true);
}
public ibis.ipl.ReceivePortIdentifier connect(ibis.ipl.IbisIdentifier id,
String name) throws ConnectionFailedException {
if (logger.isDebugEnabled()) {
logger.debug("Sendport '" + this.name + "' connecting to "
+ name + " at " + id);
}
return connect(id, name, 0, true);
}
private void checkConnect(ReceivePortIdentifier r) throws ConnectionFailedException {
if (receivers.size() > 0
&& ! type.hasCapability(PortType.CONNECTION_ONE_TO_MANY)
&& ! type.hasCapability(PortType.CONNECTION_MANY_TO_MANY)) {
throw new IbisConfigurationException("Sendport already has a "
+ "connection and OneToMany or ManyToMany not requested");
}
if (getInfo(r) != null) {
throw new AlreadyConnectedException("Already connected", r);
}
}
public synchronized void connect(ibis.ipl.ReceivePortIdentifier receiver,
long timeout, boolean fillTimeout) throws ConnectionFailedException {
if (! allowConnectionsInUpcall && ReceivePort.threadsInUpcallSet.contains(Thread.currentThread())) {
throw new ConnectionFailedException("Connection attempt in upcall is not allowed",
receiver);
}
if (logger.isDebugEnabled()) {
logger.debug("Sendport '" + name + "' connecting to " + receiver);
}
if (aMessageIsAlive) {
throw new ConnectionFailedException(
"A message was alive while adding a new connection", receiver);
}
if (timeout < 0) {
throw new ConnectionFailedException(
"connect(): timeout must be >= 0", receiver);
}
ReceivePortIdentifier r = (ReceivePortIdentifier) receiver;
checkConnect(r);
try {
addConnectionInfo(r, doConnect(r, timeout, fillTimeout));
} catch(ConnectionFailedException e) {
throw e;
} catch(Throwable e1) {
throw new ConnectionFailedException("Got unexpected exception", r, e1);
}
nConnections++;
}
public ibis.ipl.ReceivePortIdentifier[] connect(
Map<ibis.ipl.IbisIdentifier, String> ports)
throws ConnectionsFailedException {
return connect(ports, 0, true);
}
public ibis.ipl.ReceivePortIdentifier[] connect(
Map<ibis.ipl.IbisIdentifier, String> ports, long timeout,
boolean fillTimeout) throws ConnectionsFailedException {
ibis.ipl.ReceivePortIdentifier [] ids =
new ibis.ipl.ReceivePortIdentifier[ports.size()];
int index = 0;
for (Map.Entry<ibis.ipl.IbisIdentifier, String> entry : ports.entrySet()) {
ids[index++] = ibis.createReceivePortIdentifier(entry.getValue(),
(IbisIdentifier) entry.getKey());
}
connect(ids, timeout, fillTimeout); // may throw an exception
return ids;
}
public void connect(ibis.ipl.ReceivePortIdentifier[] ports)
throws ConnectionsFailedException {
connect(ports, 0, true);
}
public synchronized void connect(ibis.ipl.ReceivePortIdentifier[] ports,
long timeout, boolean fillTimeout) throws ConnectionsFailedException {
ArrayList<ibis.ipl.ReceivePortIdentifier> succes
= new ArrayList<ibis.ipl.ReceivePortIdentifier>();
LinkedList<ibis.ipl.ReceivePortIdentifier> todo
= new LinkedList<ibis.ipl.ReceivePortIdentifier>();
HashMap<ibis.ipl.ReceivePortIdentifier, Throwable> results
= new HashMap<ibis.ipl.ReceivePortIdentifier, Throwable>();
long deadline = 0;
// Calculate the deadline (if needed).
if (timeout > 0) {
deadline = System.currentTimeMillis() + timeout;
}
// Create a list of the connections that we need to set up.
for (ibis.ipl.ReceivePortIdentifier rp : ports) {
todo.add(rp);
}
if (todo.size() > 0) {
if (! allowConnectionsInUpcall && ReceivePort.threadsInUpcallSet.contains(Thread.currentThread())) {
throw new ConnectionsFailedException("Connection attempt in upcall is not allowed");
}
}
// Keep iterating over the list of connection to set up until the list
// is empty or until we reach the deadline.
while (todo.size() > 0) {
long time = 0;
// Check if we have reached the deadline. If so, break out of
// the loop.
if (deadline != 0) {
time = (deadline - System.currentTimeMillis()) / todo.size();
if (time <= 0) {
break;
}
}
// Remove the next target from the list,
ibis.ipl.ReceivePortIdentifier rp = todo.removeFirst();
// Attempt a connection setup. If successful, the target adress is
// added to the 'succes' list and any previous exceptions are
// discarded. If unsuccessful, the exception is saved for future
// reference. Depending on the value of fillTimeout, the target is
// put back in the list to be retried later.
// TODO: do we need an exp. backoff here?
try {
connect(rp, time, false);
succes.add(rp);
results.remove(rp);
} catch(Throwable e) {
results.put(rp, e);
if (fillTimeout) {
// We may get another chance!
// TODO: should we filter out some exceptions from which we
// can never recover? (e.g. if the ibis doesn't exist
// anymore)?
todo.addLast(rp);
}
}
}
// We are done OR we ran out of time OR we tried everyone at once and
// are not supposed to continue.
if (succes.size() != ports.length) {
// Some connections have failed. Throw a ConnectionsFailedException
// to inform the user of this.
// Gather all exceptions from the result map. Add new once for
// targets that have not been tried at all.
ConnectionsFailedException ex = new ConnectionsFailedException();
for (ibis.ipl.ReceivePortIdentifier rp : todo) {
Throwable tmp = results.get(rp);
if (tmp == null) {
ex.add(new ConnectionTimedOutException(
"Out of time, connection not even tried", rp));
} else if (tmp instanceof ConnectionFailedException) {
ex.add((ConnectionFailedException) tmp);
} else {
ex.add(new ConnectionFailedException(
"Connection failed", rp, tmp));
}
}
// Add a list of connections that were successful.
ex.setObtainedConnections(succes.toArray(
new ibis.ipl.ReceivePortIdentifier[succes.size()]));
throw ex;
}
}
public ibis.ipl.ReceivePortIdentifier connect(
ibis.ipl.IbisIdentifier id, String name, long timeout,
boolean fillTimeout) throws ConnectionFailedException {
ReceivePortIdentifier r =
ibis.createReceivePortIdentifier(name, (IbisIdentifier) id);
connect(r, timeout, fillTimeout);
return r;
}
private synchronized void addConnectionInfo(ReceivePortIdentifier ri,
SendPortConnectionInfo connection) {
if (logger.isDebugEnabled()) {
logger.debug("SendPort '" + name + "': added connection to " + ri);
}
addInfo(ri, connection);
}
public ibis.ipl.WriteMessage newMessage() throws IOException {
if (! allowCommunicationInUpcall && ReceivePort.threadsInUpcallSet.contains(Thread.currentThread())) {
throw new IOException("Communication in upcall is not allowed");
}
synchronized(this) {
if (closed) {
throw new IOException("newMessage call on closed sendport");
}
if (out == null) {
try {
createOut();
} catch (Throwable t) {
// TODO: is this correct ? -- J.
throw new IOException("Lost connection!");
}
}
while (aMessageIsAlive) {
waitingForMessage++;
try {
wait();
} catch(InterruptedException e) {
// ignored
}
waitingForMessage--;
// We waited, so the port may be closed now ...
if (closed) {
throw new IOException("newMessage call on closed sendport");
}
}
aMessageIsAlive = true;
}
announceNewMessage();
w.initMessage(out);
return w;
}
public void close() throws IOException {
ReceivePortIdentifier[] ports;
synchronized(this) {
ports = receivers.keySet().toArray(new ReceivePortIdentifier[receivers.size()]);
boolean alive = receivers.size() > 0 && aMessageIsAlive;
if (alive) {
throw new ConnectionClosedException(
"Closed a sendport port while a message is alive!");
}
if (logger.isDebugEnabled()) {
logger.debug("SendPort '" + name + "': start close()");
}
if (closed) {
throw new ConnectionClosedException("Port already closed");
}
closed = true;
nClosedConnections += ports.length;
}
try {
// We must create the outputstream if it doesn't exist yet,
// otherwise the close message may get lost!! -- Jason
if (out == null) {
try {
createOut();
} catch (Throwable t) {
// TODO: Not sure how to handle this... typically occurs
// when the stream is already closed by the other side.
// Let's continue and see what happens -- J.
}
}
// closePort should close all streams. (out and dataOut).
closePort();
} finally {
// Don't keep the lock on the sendport while calling ibis.deregister()!
// Otherwise, deadlocks may arise when someone is calling ibis.printStatistics()
// at the same time. --Ceriel
for (int i = 0; i < ports.length; i++) {
SendPortConnectionInfo c = removeInfo(ports[i]);
try {
c.closeConnection();
} catch(Throwable e) {
// ignore and just continue (?)
}
}
ibis.deRegister(this);
if (logger.isDebugEnabled()) {
logger.debug("SendPort '" + name + "': close() done");
}
}
}
public void disconnect(ibis.ipl.IbisIdentifier id, String name)
throws IOException {
disconnect(ibis.createReceivePortIdentifier(name, (IbisIdentifier) id));
}
public synchronized void disconnect(ibis.ipl.ReceivePortIdentifier receiver)
throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Sendport '" + this.name + "' disconnecting from "
+ receiver.name() + " at " + receiver.ibisIdentifier());
}
ReceivePortIdentifier r = (ReceivePortIdentifier) receiver;
if (aMessageIsAlive) {
throw new IOException(
"Trying to disconnect while a message is alive!");
}
SendPortConnectionInfo c = removeInfo(r);
if (c == null) {
throw new IOException("Cannot disconnect from " + r
+ " since we are not connected with it");
}
try {
sendDisconnectMessage(r, c);
} finally {
c.closeConnection();
if (receivers.size() == 0) {
if (out != null) {
try {
out.close();
} catch(Throwable e) {
// ignored
}
}
out = null;
}
}
nClosedConnections++;
}
public synchronized ibis.ipl.ReceivePortIdentifier[] connectedTo() {
return receivers.keySet().toArray(
new ibis.ipl.ReceivePortIdentifier[receivers.size()]);
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Protected methods, internal use. May be redefined or called
// by an implementation.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* Called in case an Ibis died or left. The connections to it must be
* removed.
* @param id the IbisIdentifier of the Ibis that left/died.
*/
protected synchronized void killConnectionsWith(ibis.ipl.IbisIdentifier id) {
ReceivePortIdentifier[] keys = receivers.keySet().toArray(new ReceivePortIdentifier[receivers.size()]);
for (ReceivePortIdentifier r : keys) {
if (r.ibisIdentifier().equals(id)) {
receivers.get(r).closeConnection();
removeInfo(r);
}
}
}
/**
* Returns the connection information for the specified receiveport
* identifier.
* @param id the identification of the receiveport.
* @return the connection information, or <code>null</code> if not
* present.
*/
protected synchronized SendPortConnectionInfo getInfo(
ReceivePortIdentifier id) {
return receivers.get(id);
}
/**
* Adds a connection entry for the specified receiveport identifier.
* @param id the identification of the receiveport.
* @param info the associated connection information.
*/
private synchronized void addInfo(ReceivePortIdentifier id,
SendPortConnectionInfo info) {
receivers.put(id, info);
}
/**
* Removes the connection entry for the specified receiveport identifier.
* @param id the identification of the receiveport.
* @return the removed connection.
*/
protected synchronized SendPortConnectionInfo removeInfo(
ReceivePortIdentifier id) {
return receivers.remove(id);
}
/**
* Returns an array with entries for each connection.
* @return the connections.
*/
public synchronized SendPortConnectionInfo[] connections() {
return receivers.values().toArray(
new SendPortConnectionInfo[receivers.size()]);
}
/**
* Implements the SendPort side of a message finish.
* This method is called by the {@link WriteMessage#finish()}
* implementation.
* @param w the write message that calls this method.
* @param cnt the number of bytes written.
*/
protected void finishMessage(WriteMessage w, long cnt)
throws IOException {
ibis.ipl.ReceivePortIdentifier[] ports = null;
try {
synchronized(this) {
aMessageIsAlive = false;
ports = connectedTo();
if (waitingForMessage > 0) {
// NotifyAll, because we don't know who is waiting, and what for.
notifyAll();
}
nMessages++;
messageBytes += cnt;
bytes = prevBytes + totalWritten();
if (collectedExceptions != null) {
IOException e = collectedExceptions;
collectedExceptions = null;
throw e;
}
}
} finally {
ibis.addSentPerIbis(cnt, ports);
}
}
/**
* Implements the SendPort side of a message finish with exception.
* This method is called by the
* {@link WriteMessage#finish(java.io.IOException)} implementation.
* @param w the write message that calls this method.
* @param e the exception that was passed on to the
* {@link WriteMessage#finish(java.io.IOException)} call.
*/
protected void finishMessage(WriteMessage w, IOException e) {
try {
finishMessage(w, 0L);
} catch(IOException ex) {
// ignored
}
}
/**
* Returns the number of bytes written to the current data output stream.
* This method is called by the {@link WriteMessage} implementation.
*/
protected long bytesWritten() {
return dataOut.bytesWritten();
}
/**
* Called when a method from {@link WriteMessage} receives an
* <code>IOException</code>.
* It calls the implementation-specific {@link #handleSendException}
* method and then rethrows the exception unless we are dealing with a
* multicast port, in which case the exception is saved.
*/
protected void gotSendException(WriteMessage w, IOException e)
throws IOException {
handleSendException(w, e);
if (type.hasCapability(PortType.CONNECTION_ONE_TO_ONE)
|| type.hasCapability(PortType.CONNECTION_MANY_TO_ONE)) {
// Otherwise exception will be saved until the finish.
throw e;
}
if (collectedExceptions == null) {
collectedExceptions = new CollectedWriteException();
}
collectedExceptions.add(e);
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Public methods, to be called by Ibis implementations.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* This method must be called by an implementation when it detects that a
* connection to a particular receive port is lost. It takes care of the
* user upcall if requested, and updates the administration accordingly.
* @param id identifies the receive port.
* @param cause the exception that describes the reason for the loss of
* the connection.
*/
public void lostConnection(ReceivePortIdentifier id, Throwable cause) {
if (logger.isDebugEnabled() && cause != null) {
logger.debug("lostConnection to " + id + ", cause " + cause, cause);
}
if (connectionDowncalls) {
synchronized(this) {
lostConnections.add(id);
}
}
if (connectUpcall != null) {
try {
connectUpcall.lostConnection(this, id, cause);
} catch(Throwable e) {
logger.error("Unexpected exception in lostConnection(), "
+ "this Java instance will be terminated" , e);
System.exit(1);
}
}
SendPortConnectionInfo c = removeInfo(id);
if (c != null) {
try {
nLostConnections++;
c.closeConnection();
} catch(Throwable e) {
// ignored
}
}
}
/**
* This method (re-)initializes the {@link ibis.io.DataOutputStream}, and
* closes the serialization stream if there was one.
* This method should be called from the implementation-specific
* constructor, and from each
* {@link #doConnect(ReceivePortIdentifier, long,boolean)} call.
* @param dataOut the {@link ibis.io.DataOutputStream} to be used when
* creating a new serialization stream is created.
*/
public void initStream(DataOutputStream dataOut) {
this.dataOut = dataOut;
prevBytes += totalWritten();
resetWritten();
// Close the serialization stream. A new one will be created when
// needed.
if (out != null) {
try {
out.close();
} catch(Throwable e) {
// ignored
}
}
out = null;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Protected methods, to be implemented by Ibis implementations.
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* This method must notify the receiveports that a new message is coming,
* if needed. It must also deal with sequencing, if implemented/required.
* @exception IOException may be thrown when this notification fails.
*/
protected abstract void announceNewMessage() throws IOException;
/**
* This method must set up a connection with the specified receive port.
* @param receiver identifies the receive port.
* @param timeout the timout, in milliseconds.
* @exception IOException may be thrown when the connection fails.
* @return the {@link SendPortConnectionInfo} associated with the
* connection.
*/
protected abstract SendPortConnectionInfo doConnect(
ReceivePortIdentifier receiver, long timeout, boolean fillTimeout)
throws IOException;
/**
* This method must notify the specified receive port that this sendport
* has disconnected from it.
* @param receiver identifies the receive port.
* @param c the connection information.
* @exception IOException is thrown in case of trouble.
*/
protected abstract void sendDisconnectMessage(ReceivePortIdentifier receiver,
SendPortConnectionInfo c) throws IOException;
/**
* This method should notify the connected receiveport(s) that this
* sendport is being closed.
* @exception IOException may be thrown when communication with the
* receiveport(s) fails for some reason.
*/
protected abstract void closePort() throws IOException;
/**
* This method is called when a {@link WriteMessage} method receives an
* <code>IOException</code>. The implementation should try and find out
* which connection(s) were lost, and call the
* {@link #lostConnection(ReceivePortIdentifier, Throwable)}
* method for each of them.
* @param w the {@link WriteMessage}.
* @param e the exception that was thrown.
*/
protected abstract void handleSendException(WriteMessage w, IOException e);
/**
* Number of messages sent.
*/
synchronized long getMessageCount() {
return nMessages;
}
/**
* Number of bytes written by the user in messages.
*/
synchronized long getBytesWritten() {
return messageBytes;
}
/**
* Number of bytes sent out on the network. Includes extra traffic sent
* if a message is broadcasted.
*/
synchronized long getBytesSent() {
return bytes;
}
}