onsdag 7 april 2010

Actors in java with akka

I am currently working my way through the akka tutorial, except that im using java instead of scala. I am also trying to make it in smaller steps, and trying to keep it runnable as much as possible. I have removed import statements and such to make it shorter, the full code can be found at github: http://github.com/bobo/akka_sample_java

Lets start withough any akka or other magic, except for Guice.
I started by creating a chatserver:


public class ChatServer {

    @Inject
    public ChatServer() {
    }

    private List<Message> messages = new ArrayList<Message>();
    private Set<ChatSession> sessions = new HashSet<ChatSession>();
    private Vector<byte[]> storage = new Vector<byte[]>();

    public void login(ChatSession chatSession) {
        sessions.add(chatSession);
    }

    public List<Message> getLog() {
        return Collections.unmodifiableList(messages);
    }

    public void sendMessage(Message message) {
        storeMessage(message);
        for (ChatSession chatSession : sessions) {
            chatSession.recieveMessage(message);
        }
    }

    private void storeMessage(Message message) {
        messages.add(message);
        storage.add(message.toString().getBytes());
    }
}

This is just a simple class, that stores messages in byteformat in a vector.
It might seem weird to have the Vector store byte[] instead of message, but later we will change it to a PersistentVector, and that uses byte[] to store it in the backend.

And we need some clients:



public class ChatSession {

    private String userName;
    private final List<Message> messages = new ArrayList<Message>();
    @Inject
    private ChatServer chatServer;

    public void login() {
        chatServer.login(this);
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserName() {
        return userName;
    }

    public void recieveMessage(Message message) {
        messages.add(message);
    }

    public void sendMessage(String text) {
        Message message = new Message(userName, text);
        chatServer.sendMessage(message);
    }

    public List<Message> getLog() {
        return messages;
    }
}


Here we inject the Chatserver with @Inject directly on the variable declaration

we can use it in a simple testcase:



public class IntegrationTest {
    Module m;
    @Before
    public void setup() {
        m = new AbstractModule() {
            @Override
            protected void configure() {
                bind(ChatSession.class);
                bind(ChatServer.class).asEagerSingleton();
            }
        };
    }

    @Test
    public void integrationTest() throws InterruptedException {
        Injector inj = Guice.createInjector(m);
        final ChatSession userOne = inj.getInstance(ChatSession.class);
        userOne.setUserName("userOne");
        userOne.login();
        final ChatSession userTwo = inj.getInstance(ChatSession.class);
        userTwo.setUserName("userTwo");
        userTwo.login();
        userOne.sendMessage("Hello");
        userTwo.sendMessage("Another Message 1");
        Thread.sleep(400); //wait for messages to finish
        assertEquals(2, userOne.getLog().size());
        assertEquals(2, userTwo.getLog().size());
        assertEquals(userTwo.getLog(), userOne.getLog());
    }
}


Here we create two chatsessions with our guice injector. And since we have bound ChatServer as a singleton they will have the same instance of chatserver injected.


Now the next step is to add akka to the project. i will add that to a seperate post

2 kommentarer:

  1. Hi Mikael,

    I don't know much about the active objects, but it seems to me that the ChatServer can be accessed by clients concurrently. If that is the case, you need some form of synchronisation on the internals of the ChatServer.

    SvaraRadera
  2. I think that is one of the points (although i actually don't make that point, maybe i should). Since the ChatServer after beeing transformed by akka and activeobjects, it behaves as an actor. And the synchronisation problem should be solved?
    also with the @transactionrequired annotation, everything is done in a transaction, and rolled back and retried if something has changed.

    if i have not missunderstood something that is :-)

    SvaraRadera