package org.myrobotlab.net;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.URI;
import java.util.Iterator;
import org.myrobotlab.framework.Message;
import org.myrobotlab.framework.ServiceEnvironment;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.service.RemoteAdapter;
import org.myrobotlab.service.Runtime;
import org.myrobotlab.service.interfaces.CommunicationInterface;
import org.myrobotlab.service.interfaces.ServiceInterface;
import org.slf4j.Logger;
/**
* FIXME - make abstract class Server with abstract connection info &
* serializers stop & start are all the same (between UdpServer & TcpServer)
*
* @author grperry
*
*
*/
public class UdpServer implements Runnable {
public final static Logger log = LoggerFactory.getLogger(UdpServer.class);
DatagramSocket serverSocket;
RemoteAdapter myService;
Integer serverPort;
Thread serverThread;
boolean isRunning = false;
public UdpServer(RemoteAdapter s) {
myService = s;
}
public void start(int serverPort) {
this.serverPort = serverPort;
if (serverThread != null) {
stop();
}
serverThread = new Thread(this, String.format("%s.udp.%d", myService.getName(), this.serverPort));
serverThread.start();
}
public void stop() {
if (serverThread != null) {
serverThread.interrupt();
}
}
// FIXME FIXME FIXME - large amount of changes to tcp - application
// logic which handles the "Messaging" should be common to both
// tcp & udp & xmpp
@Override
public void run() {
isRunning = true;
try {
serverSocket = new DatagramSocket(serverPort);
log.info(String.format("%s UdpServer listening on %s:%d", myService.getName(), serverSocket.getLocalAddress(), serverSocket.getLocalPort()));
byte[] b = new byte[65507]; // max udp size 65507 + 8 byte
// header = 65535
ByteArrayInputStream b_in = new ByteArrayInputStream(b);
DatagramPacket dgram = new DatagramPacket(b, b.length);
while (isRunning) {
serverSocket.receive(dgram); // receives all datagrams
// FIXME - do we need o re-create???
ObjectInputStream o_in = new ObjectInputStream(b_in);
try {
Message msg = (Message) o_in.readObject();
dgram.setLength(b.length); // must reset length field!
b_in.reset();
/*
* if ("getConnections".equals(msg.method)) {
*
* // get connections List<Connection> conn =
* getConnections(new URI(String.format("tcp:/%s:%d",
* dgram.getAddress(), dgram.getPort()))); // send them back
* for (int i = 0; i < conn.size(); ++i) { Message
* newConnMsg = createMessage(null, "publishConnection",
* conn); byte[] msgBuf =
* org.myrobotlab.codec.CodecUtils.getBytes(newConnMsg);
* DatagramPacket dgp = new DatagramPacket(msgBuf,
* msgBuf.length, dgram.getAddress(), dgram.getPort());
* socket.send(dgp); }
*
* // we will have to search for them again } else if
* ("publishConnection".equals(msg.method)) {
* myService.invoke("onCommOptions", msg.data[0]); } else
*/
if ("register".equals(msg.method)) {
// FIXME name should be "Runtime" representing the
// static
// BEGIN ENCAPSULATION --- ENCODER BEGIN
// -------------
// IMPORTANT - (should be in Encoder) - create the
// key
// for foreign service environment
// Runtime.addServiceEnvironment(name, protocolKey)
URI protocolKey = new URI(String.format("udp://%s:%d", serverSocket.getInetAddress().getHostAddress(), serverSocket.getPort()));
String mrl = String.format("mrl://%s/%s", myService.getName(), protocolKey.toString());
URI mrlURI = new URI(mrl);
// IMPORTANT - this is an optimization and probably
// should be in the Comm interface defintion
CommunicationInterface cm = myService.getComm();
cm.addRemote(mrlURI, protocolKey);
// check if the URI is already defined - if not - we
// will
// send back the services which we want to export -
// Security will filter appropriately
ServiceEnvironment foreignProcess = Runtime.getEnvironment(mrlURI);
ServiceInterface si = (ServiceInterface) msg.data[0];
// HMMM a vote for String vs URI here - since we
// need to
// catch syntax !!!
si.setInstanceId(mrlURI);
// if security ... msg within msg
// getOutbox().add(createMessage(Runtime.getInstance().getName(),
// "register", inboundMsg));
Runtime.register(si, mrlURI);// <-- not an INVOKE
// !!!
// // -
// no security ! :P
if (foreignProcess == null) {
// not defined we will send export
// TODO - Security filters - default export
// (include
// exclude) - mapset of name
ServiceEnvironment localProcess = Runtime.getLocalServicesForExport();
Iterator<String> it = localProcess.serviceDirectory.keySet().iterator();
String name;
ServiceInterface toRegister;
while (it.hasNext()) {
name = it.next();
toRegister = localProcess.serviceDirectory.get(name);
// the following will wrap a message within
// a message and send it remotely
// This Thread CANNOT Write on The
// ObjectOutputStream directly -
// IT SHOULD NEVER DO ANY METHOD WHICH CAN
// BLOCK !!!! - 3 days of bug chasing when
// it wrote to ObjectOutputStream and oos
// blocked when the buffer was full -
// causing deadlock
// putting it on the inbox will move it to a
// different thread
Message sendService = myService.createMessage(null, "register", toRegister);
Message outbound = myService.createMessage(myService.getName(), "sendRemote", new Object[] { protocolKey, sendService });
myService.getInbox().add(outbound);
}
}
// BEGIN ENCAPSULATION --- ENCODER END -------------
} else {
// ++udpRx;
myService.getOutbox().add(msg);
}
} catch (Exception e) {
log.error("processing msg threw", e);
}
dgram.setLength(b.length); // must reset length field!
b_in.reset(); // reset so next read is from start of byte[]
// again
} // while isRunning
} catch (SocketException se) {
log.error("UdpListener could not listen", se);
} catch (Exception e) {
log.error("wtf error", e);
}
}
public void shutdown() {
isRunning = false;
if ((serverSocket != null) && (!serverSocket.isClosed())) {
serverSocket.close();
}
}
}