package ibis.ipl.impl.smartsockets;
import ibis.ipl.MessageUpcall;
import ibis.ipl.NoSuchPropertyException;
import ibis.ipl.PortType;
import ibis.ipl.ReadMessage;
import ibis.ipl.ReceivePort;
import ibis.ipl.ReceivePortIdentifier;
import ibis.ipl.impl.IbisIdentifier;
import ibis.ipl.impl.SendPortIdentifier;
import ibis.smartsockets.direct.DirectSocketAddress;
import ibis.smartsockets.hub.servicelink.CallBack;
import ibis.smartsockets.hub.servicelink.ServiceLink;
import ibis.util.ThreadPool;
import java.io.IOException;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SmartSocketsUltraLightReceivePort implements ReceivePort, CallBack, Runnable {
protected static final Logger logger
= LoggerFactory.getLogger("ibis.ipl.impl.smartsockets.ReceivePort");
private final PortType type;
private final String name;
private final MessageUpcall upcall;
// private final Properties properties;
private final ReceivePortIdentifier id;
// private final SmartSocketsIbis ibis;
private boolean allowUpcalls = false;
private boolean closed = false;
private final LinkedList<SmartSocketsUltraLightReadMessage> messages =
new LinkedList<SmartSocketsUltraLightReadMessage>();
SmartSocketsUltraLightReceivePort(SmartSocketsIbis ibis, PortType type,
String name, MessageUpcall upcall, Properties properties) throws IOException {
// this.ibis = ibis;
this.type = type;
this.name = name;
this.upcall = upcall;
// this.properties = properties;
this.id = new ibis.ipl.impl.ReceivePortIdentifier(name, ibis.ident);
ServiceLink link = ibis.getServiceLink();
if (link == null) {
throw new IOException("No ServiceLink available");
}
if (logger.isDebugEnabled()) {
logger.debug("Registering ultralight receive port " + name);
}
link.register(name, this);
if (type.hasCapability(PortType.RECEIVE_AUTO_UPCALLS) && upcall != null) {
ThreadPool.createNew(this, "ConnectionHandler");
}
}
public synchronized void close() {
closed = true;
notifyAll();
}
public void close(long timeoutMillis) {
close();
}
private synchronized boolean getClosed() {
return closed;
}
public SendPortIdentifier[] connectedTo() {
return new SendPortIdentifier[0];
}
public void disableConnections() {
// empty ?
}
public void enableConnections() {
// empty ?
}
public synchronized void disableMessageUpcalls() {
allowUpcalls = false;
}
public synchronized void enableMessageUpcalls() {
// TODO Auto-generated method stub
allowUpcalls = true;
notifyAll();
}
public PortType getPortType() {
return type;
}
public ReceivePortIdentifier identifier() {
return id;
}
public SendPortIdentifier[] lostConnections() {
return new SendPortIdentifier[0];
}
public String name() {
return name;
}
public SendPortIdentifier[] newConnections() {
return new SendPortIdentifier[0];
}
public ReadMessage poll() throws IOException {
// TODO Auto-generated method stub
return null;
}
public ReadMessage receive() throws IOException {
return receive(0L);
}
public ReadMessage receive(long timeoutMillis) throws IOException {
return getMessage(timeoutMillis);
}
public String getManagementProperty(String key) throws NoSuchPropertyException {
// TODO Auto-generated method stub
return null;
}
public Map<String, String> managementProperties() {
// TODO Auto-generated method stub
return null;
}
public void printManagementProperties(PrintStream stream) {
// TODO Auto-generated method stub
}
public void setManagementProperties(Map<String, String> properties) throws NoSuchPropertyException {
// TODO Auto-generated method stub
}
public void setManagementProperty(String key, String value) throws NoSuchPropertyException {
// TODO Auto-generated method stub
}
public void gotMessage(DirectSocketAddress src, DirectSocketAddress srcProxy, int opcode,
boolean returnToSender, byte [][] message) {
logger.debug("Got message from + " + src);
if (returnToSender || opcode != 0xDEADBEEF || message == null || message.length == 0
|| message[0] == null || message[0].length == 0) {
logger.warn("Received malformed message from " + src.toString() + " ("
+ returnToSender + ", " + opcode + ", " + (message== null) + ", "
+ message.length + ", " + (message[0] == null) + ", " + message[0].length + ")");
return;
}
IbisIdentifier source = null;
try {
source = new IbisIdentifier(message[0]);
if (logger.isDebugEnabled()) {
logger.debug("Message was send by " + source);
}
} catch (Exception e) {
logger.warn("Message from contains malformed IbisIdentifier", e);
return;
}
SmartSocketsUltraLightReadMessage rm = null;
try {
rm = new SmartSocketsUltraLightReadMessage(this,
new SendPortIdentifier("anonymous", source), message[1]);
} catch (Exception e) {
logger.warn("Message from contains malformed data", e);
return;
}
synchronized (this) {
messages.addLast(rm);
notifyAll();
}
}
private synchronized SmartSocketsUltraLightReadMessage getMessage(long timeout) {
long endTime = System.currentTimeMillis() + timeout;
while (!closed && messages.size() == 0) {
if (timeout > 0) {
long waitTime = endTime - System.currentTimeMillis();
if (waitTime <= 0) {
break;
}
try {
wait(waitTime);
} catch (InterruptedException e) {
// ignore
}
} else {
try {
wait();
} catch (InterruptedException e) {
// ignore
}
}
}
if (closed || messages.size() == 0) {
return null;
}
return messages.removeFirst();
}
private synchronized boolean waitUntilUpcallAllowed() {
while (!closed && !allowUpcalls) {
try {
wait();
} catch (InterruptedException e) {
// ignored
}
}
return !closed;
}
private void performUpcall(SmartSocketsUltraLightReadMessage message) {
if (waitUntilUpcallAllowed()) {
try {
// Notify the message that is is processed from an upcall,
// so that finish() calls can be detected.
message.setInUpcall(true);
upcall.upcall(message);
} catch(IOException e) {
if (!message.isFinished()) {
message.finish(e);
return;
}
logger.error("Got unexpected exception in upcall, continuing ...", e);
} catch(Throwable t) {
if (!message.isFinished()) {
IOException ioex =
new IOException("Got Throwable: " + t.getMessage());
ioex.initCause(t);
message.finish(ioex);
}
return;
} finally {
message.setInUpcall(false);
}
}
}
protected void newUpcallThread() {
ThreadPool.createNew(this, "ConnectionHandler");
}
public void run() {
while (!getClosed()) {
SmartSocketsUltraLightReadMessage message = getMessage(0L);
if (message != null) {
performUpcall(message);
if (message.finishCalledInUpcall()) {
// A new thread has take our place
return;
}
}
}
}
}