/* $Id$ */
package ibis.ipl.impl.smartsockets;
import ibis.io.BufferedArrayOutputStream;
import ibis.io.Conversion;
import ibis.io.OutputStreamSplitter;
import ibis.io.SplitterException;
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.SendPortIdentifier;
import ibis.ipl.impl.WriteMessage;
import ibis.smartsockets.virtual.VirtualSocket;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
final class SmartSocketsSendPort extends SendPort implements SmartSocketsProtocol {
private class Conn extends SendPortConnectionInfo {
VirtualSocket s;
OutputStream out;
Conn(VirtualSocket s, SmartSocketsSendPort port, ReceivePortIdentifier target)
throws IOException {
super(port, target);
this.s = s;
out = s.getOutputStream();
splitter.add(out);
}
public String connectionType() {
return s.toString();
}
public void closeConnection() {
try {
s.close();
} catch (Throwable e) {
// ignored
} finally {
try {
splitter.remove(out);
} catch (IOException e) {
// ignore
}
}
}
}
final OutputStreamSplitter splitter;
final BufferedArrayOutputStream bufferedStream;
SmartSocketsSendPort(Ibis ibis, PortType type, String name,
SendPortDisconnectUpcall cU, Properties props) throws IOException {
super(ibis, type, name, cU, props);
splitter = new OutputStreamSplitter(
!type.hasCapability(PortType.CONNECTION_ONE_TO_ONE) &&
!type.hasCapability(PortType.CONNECTION_MANY_TO_ONE),
type.hasCapability(PortType.CONNECTION_ONE_TO_MANY) ||
type.hasCapability(PortType.CONNECTION_MANY_TO_MANY));
bufferedStream = new BufferedArrayOutputStream(splitter);
initStream(bufferedStream);
}
protected long totalWritten() {
return splitter.bytesWritten();
}
protected void resetWritten() {
splitter.resetBytesWritten();
}
SendPortIdentifier getIdent() {
return ident;
}
protected SendPortConnectionInfo doConnect(ReceivePortIdentifier receiver,
long timeoutMillis, boolean fillTimeout) throws IOException {
VirtualSocket s = ((SmartSocketsIbis) ibis).connect(this, receiver, (int) timeoutMillis,
fillTimeout);
Conn c = new Conn(s, this, receiver);
if (out != null) {
out.writeByte(NEW_RECEIVER);
}
initStream(bufferedStream);
return c;
}
protected void sendDisconnectMessage(ReceivePortIdentifier receiver,
SendPortConnectionInfo conn) throws IOException {
out.writeByte(CLOSE_ONE_CONNECTION);
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();
// FIXME!
//
// This is here to make sure the close is processed before a new
// connections can be made (by this sendport). Without this ack,
// an application that uses a single sendport that connects/disconnects
// for each message may get an 'AlreadyConnectedException', because the
// connect overtakes the disconnect...
//
// Unfortunately, it also causes a deadlock in 1-to-1 explict receive
// applications -- J
Conn c = (Conn) conn;
c.s.getInputStream().read();
}
protected void announceNewMessage() throws IOException {
out.writeByte(NEW_MESSAGE);
if (type.hasCapability(PortType.COMMUNICATION_NUMBERED)) {
out.writeLong(ibis.registry().getSequenceNumber(name));
}
}
protected void finishMessage(WriteMessage w, long cnt)
throws IOException {
if (type.hasCapability(PortType.CONNECTION_ONE_TO_MANY)
|| type.hasCapability(PortType.CONNECTION_MANY_TO_MANY)) {
// exception may have been saved by the splitter. Get them
// now.
SplitterException e = splitter.getExceptions();
if (e != null) {
gotSendException(w, e);
}
}
super.finishMessage(w, cnt);
}
protected void handleSendException(WriteMessage w, IOException x) {
ReceivePortIdentifier[] ports = null;
synchronized (this) {
ports = receivers.keySet()
.toArray(new ReceivePortIdentifier[0]);
}
if (x instanceof SplitterException) {
SplitterException e = (SplitterException) x;
Exception[] exceptions = e.getExceptions();
OutputStream[] streams = e.getStreams();
for (int i = 0; i < ports.length; i++) {
Conn c = (Conn) getInfo(ports[i]);
for (int j = 0; j < streams.length; j++) {
if (c.out == streams[j]) {
lostConnection(ports[i], exceptions[j]);
break;
}
}
}
} else {
// Just close all connections. ???
for (int i = 0; i < ports.length; i++) {
lostConnection(ports[i], x);
}
}
}
protected void closePort() {
try {
out.writeByte(CLOSE_ALL_CONNECTIONS);
out.close();
bufferedStream.close();
} catch (Throwable e) {
// ignored
}
out = null;
}
}