Monday, October 12, 2009

CJLibrary Java - Client/Server and Signals

I chose Java as primary language for my Internet & Multimedia Lab class, and I decided to start the CJLibrary Java project. Eventually, I'll port some useful code from my CJLibrary (for AS3) to CJLibrary Java.

Here's the documentation for CJLibrary Java.

Currently the project mainly consists of the idv.cjcat.net and the idv.cjcat.signals packages; the former is for client-server connection and the latter is an Observer Pattern framework inspired by Robert Penner's AS3 Signals project.

The idv.cjcat.net package encapsulates complex client-server connection handling and exposes a set of user-friendly interface, including the use of the idv.cjcat.signals package.

Here's a quick look at how the idv.cjcat.net package works.

//creates an EchoServer object and starts the server on port 2266
Sever server = new EchoServer(2266);
server.start();

//creates two clients that connects to the server
Client client1 = new EchoClient();
Client client2 = new EchoClient();
client1.connect("localhost", 2266);
client2.connect("localhost", 2266);

Note that the Server and Client classes are both abstract. You have to extend them to create concrete subclasses. The createListener() method is the only method that has to be implemented. In this method you should return your desired SocketListener object, which is also abstract and whose mainLoop() method should be implemented.

Here's the EchoServer class:
(Sorry for the ugly intending. I'm still unable to get the code highlighter to work properly.)
public class EchoServer extends Server {
public EchoServer(int port) {
super(port);
}
@Override
protected SocketListener createListener() {
return new EchoSocketListener();
}
}

As you can see, this concrete class is only overriding the createListener() method to tell this server which socket listener it should use, which listens to socket input.

The same logic is the same for the EchoClient class:
public class EchoClient extends Client {
@Override
protected SocketListener createListener() {
return new PrinterSocketListener();
}
}


Now let's take a look at the EchoSocketListener, which sends back, i.e. echoes, whatever it receives.
public class EchoSocketListener extends LazySocketListener {
public EchoSocketListener() {
onReceive().add(new SignalListener() {
@Override
public void update(Signal signal) {
getWriter().println(signal.getMessage());
}
});
}
}

This class extends the LasySocketListener class instead of SocketListener, because the LasySocketListener class further encapsulates the socket IO, providing a getReader() and getWriter() accessors to give user an even easier way of socket IO handling.

This class also demonstrates the usage of the idv.cjcat.signals package. The onReceive() accessor returns a Signaller object that dispatches signal objects of the Signal class. The add() method adds a listener that listens to signals of the Signal class, therefore the listener must implement the SignalListener interface and implement the update(Signal signal) method. This listener object simply tells the writter to print whatever message the signal is containing, which is the direct message sent over by the other end of the socket.


Finally, here's the PrinterSocketListener class for the client, which simply prints out whatever message received.
public class PrinterSocketListener extends LazySocketListener {
public PrinterSocketListener() {
onReceive().add(new SignalListener() {
@Override
public void update(Signal signal) {
System.out.println("client received: "+ signal.getMessage());
}
});
}
}


Now in the main program, the following additional lines of code would cause the two clients to send messages to the server and receive messages that are echoed back.
//sends messages to the server
client1.send("Hello. I'm client #1.");
client2.send("Hello. I'm client #2.");


And this is what you'll get from the console output:
Hello. I'm client #1.
Hello. I'm client #2.

These are messages that are echoed back by the server, and the order might be reversed, since the two clients have independent socket listener threads.