/* $Id$ */
package ibis.ipl.impl.nio;
import ibis.io.Conversion;
import ibis.ipl.PortType;
import ibis.ipl.SendPortDisconnectUpcall;
import ibis.ipl.impl.Ibis;
import ibis.ipl.impl.ReceivePortIdentifier;
import ibis.ipl.impl.SendPort;
import ibis.ipl.impl.SendPortConnectionInfo;
import ibis.ipl.impl.WriteMessage;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.GatheringByteChannel;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class NioSendPort extends SendPort implements Protocol {
private static Logger logger = LoggerFactory.getLogger(NioSendPort.class);
private final NioAccumulator accumulator;
NioSendPort(Ibis ibis, PortType type, String name,
SendPortDisconnectUpcall cU, Properties props) throws IOException {
super(ibis, type, name, cU, props);
if (type.hasCapability("sendport.blocking")) {
accumulator = new BlockingChannelNioAccumulator(this);
}
else if (type.hasCapability("sendport.nonblocking")) {
accumulator = new NonBlockingChannelNioAccumulator(this);
}
else if (type.hasCapability("sendport.thread")) {
accumulator = new ThreadNioAccumulator(this,
((NioIbis)ibis).sendReceiveThread());
}
else if (type.hasCapability(PortType.CONNECTION_ONE_TO_ONE)
|| type.hasCapability(PortType.CONNECTION_ONE_TO_MANY)) {
accumulator = new BlockingChannelNioAccumulator(this);
} else {
accumulator = new NonBlockingChannelNioAccumulator(this);
}
initStream(accumulator);
}
protected void handleSendException(WriteMessage w, IOException e) {
logger.debug("handleSendException", e);
}
protected SendPortConnectionInfo doConnect(ReceivePortIdentifier receiver,
long timeoutMillis, boolean fillTimeout) throws IOException {
// FIXME: Retry on "receiveport not ready"
// FIXME: implement fillTimeout in the lower layers!
// make the connection. Will throw an Exception if if failed
Channel channel = ((NioIbis)ibis).factory.connect(this, receiver,
timeoutMillis);
if (!(channel instanceof GatheringByteChannel)) {
logger.error("factory returned wrong type of channel");
throw new IOException("factory returned wrong type of channel");
}
// close output stream (if it exist). The new receiver needs the
// stream headers and such.
if (out != null) {
logger.info("letting all the other"
+ " receivers know there's a new connection");
out.writeByte(NEW_RECEIVER);
}
if (logger.isDebugEnabled()) {
logger.debug("done connecting " + ident + " to " + receiver);
}
SendPortConnectionInfo c = accumulator.add(
(GatheringByteChannel) channel, receiver);
initStream(accumulator);
return c;
}
protected void sendDisconnectMessage(ReceivePortIdentifier receiver,
SendPortConnectionInfo c) throws IOException {
// tell out peer someone is going to have to disconnect
out.writeByte(CLOSE_ONE_CONNECTION);
accumulator.removeConnection(receiver);
byte[] receiverBytes = receiver.toBytes();
byte[] receiverLength = new byte[Conversion.INT_SIZE];
Conversion.defaultConversion.int2byte(receiverBytes.length,
receiverLength, 0);
out.writeArray(receiverLength);
out.writeArray(receiverBytes);
out.flush();
}
protected void announceNewMessage() throws IOException {
out.writeByte(NEW_MESSAGE);
if (type.hasCapability(PortType.COMMUNICATION_NUMBERED)) {
out.writeLong(ibis.registry().getSequenceNumber(name));
}
}
protected void closePort() {
try {
out.writeByte(CLOSE_ALL_CONNECTIONS);
out.close();
} catch (Throwable e) {
// IGNORE
}
out = null;
}
}