As mentioned in the previous post, the SimpleFramework API and server is a minimal alternative in the world of Java application servers like Glassfish, Tomcat. Hosted on GitHub under an Apache v2.0 license, they offer an embeddable HTTP, Servlet and WebSocket capable stack and API. They claim their performance is better than other Java based web servers.

The Dependency Injection Framework Trap

Whether they live up to their name, and is truly easy and simple to use is somewhat debatable, though. They favour Spring for dependency injection, which means part of the code is specified in XML, and is “magically” coupled during runtime. There might be benefits to that, but the downside is that it becomes difficult to read and debug. In fact, their demo application will not start for whatever reason, and all that is offered in terms of output is an InvalidArgumentException without further details deep down from the Spring code.

Don’t get me wrong, the Google Guice framework is not much better. It might specify its module coupling in Java code instead of XML, however, when it comes to following the code and debugging, it is just as obscure. Implementations tend to be hidden behind layers of interfaces. As for the promise of easily re-wireable code, they both fall short. When it comes down to it, a refactoring will typically involve changes in both class content and class relationships, so a dependency injection framework is more likely to get in the way than help.

Finally, when it comes to testability, dependency injection is of course crucial. Being able to inject mocks or other testing objects keeps unit tests small. However, a wiring framework is not needed to describe the class relationships. In fact, my personal experience is that Guice based code is more cumbersome to use in test related code. It takes considerable effort to track down which classes to include in a unit test, and which to leave out, and then to create a test module of it all. At least Spring leaves the class constructors intact and simple.

Making it work

So, with that rant out of the way, what does it take to make the Simple Framework WebSocket Chat Demo work? My solution was to simply shortcut the 114 line XML Spring configuration, and write the same class relations in Java. The following 20 lines achieves the same as the XML.

Furthermore, it’s worth noting that the SimpleFramework is structured in multiple Maven projects, with dependencies between each other, as seen below the code snippet. It highlights another potential problem with the project. A lot of the server code required to make the Chat demo run is in another demo package. It suggest quite a lot of custom code has to be written to use the server API.

  public static void main(String[] args) throws Exception {
    new LogConfigurer(new File("etc/log4j.xml")).configure();

    Map<String, String> content = new HashMap<>();
    content.put(".*", "text/html");
    ContentTypeResolver typeResolver = new ContentTypeResolver(content);
    FileManager manager = new FileManager(new File("data/chat"));
    FileResolver fileResolver = new FileResolver(manager, "index.html", "index.html");
    Resource fileSystemResource = new FileSystemResource(fileResolver, typeResolver);
    Resource chatRoomLogin = new ChatRoomLogin(fileSystemResource);
    Service chatRoom = new ChatRoom();
    StringResource failureResource = new StringResource("An error occured serving a resource!",
        "text/plain; charset=UTF-8", "UTF-8", Status.INTERNAL_SERVER_ERROR);
    Resource notFoundResource = new StringResource("Resource could not be found!!", "text/plain; charset=UTF-8",
        "UTF-8", Status.NOT_FOUND);
    Map<String, Resource> resources = new HashMap<>();
    resources.put("/login.html", chatRoomLogin);
    ResourceEngine resourceEngine = new RegularExpressionEngine(resources, notFoundResource);
    ResourceContainer resourceContainer = new ResourceContainer(resourceEngine, failureResource);
    TraceAnalyzer analyzer = new LogAnalyzer();
    Container webContainer = new WebContainer(resourceContainer, "Chat/1.0");
    Router webSocketRouter = new DirectRouter(chatRoom);
    Container webSocketContainer = new RouterContainer(webContainer, webSocketRouter, 2);
    WebServer server = new WebServer(webSocketContainer, analyzer, PORT);

    server.start();
  }

   

The GitHub Maven packages and dependencies at the time of writing:

simple (core code)
simple-test (unit tests) -> “simple”

simple-demo (common demo code) -> “simple”
simple-demo-chat -> “simple”, “simple-demo”
simple-demo-graph -> “simple”, “simple-demo”
simple-demo-rest -> “simple”, “simple-demo”, “simple-demo-chat”

It is not entirely clear whether the last demo example, “simple-demo-rest”, depends on the “simple-chat” project as the XML suggests, or whether the duplicate ChatRoom class was intended to be used. It might be that the XML was copied, but not updated - highlighting the previous point about hard to maintain and read dependency injection code.

Focusing only on the “simple-demo-chat” example then, this would be what it could look like in the Eclipse Package Explorer, with each directory created as a separate Java project.

Eclipse setup

Finally, here’s the full main-file hack to make the chat example work. Notice the required helper classes from demo packages.

SfChatDemoMain.txt
GitHub Raw
/* Copyright rememberjava.com. Licensed under GPL 3. See http://rememberjava.com/license */
package com.rememberjava.http;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import org.simpleframework.demo.http.WebContainer;
import org.simpleframework.demo.http.WebServer;
import org.simpleframework.demo.http.resource.ContentTypeResolver;
import org.simpleframework.demo.http.resource.FileSystemResource;
import org.simpleframework.demo.http.resource.RegularExpressionEngine;
import org.simpleframework.demo.http.resource.Resource;
import org.simpleframework.demo.http.resource.ResourceContainer;
import org.simpleframework.demo.http.resource.ResourceEngine;
import org.simpleframework.demo.http.resource.StringResource;
import org.simpleframework.demo.io.FileManager;
import org.simpleframework.demo.io.FileResolver;
import org.simpleframework.demo.log4j.LogConfigurer;
import org.simpleframework.demo.trace.LogAnalyzer;
import org.simpleframework.http.Status;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.socket.service.DirectRouter;
import org.simpleframework.http.socket.service.Router;
import org.simpleframework.http.socket.service.RouterContainer;
import org.simpleframework.http.socket.service.Service;
import org.simpleframework.transport.trace.TraceAnalyzer;

public class SfChatDemoMain {

  private static final int PORT = 6060;

  public static void main(String[] args) throws Exception {
    new LogConfigurer(new File("etc/log4j.xml")).configure();

    Map<String, String> content = new HashMap<>();
    content.put(".*", "text/html");
    ContentTypeResolver typeResolver = new ContentTypeResolver(content);
    FileManager manager = new FileManager(new File("data/chat"));
    FileResolver fileResolver = new FileResolver(manager, "index.html", "index.html");
    Resource fileSystemResource = new FileSystemResource(fileResolver, typeResolver);
    Resource chatRoomLogin = new ChatRoomLogin(fileSystemResource);
    Service chatRoom = new ChatRoom();
    StringResource failureResource = new StringResource("An error occured serving a resource!",
        "text/plain; charset=UTF-8", "UTF-8", Status.INTERNAL_SERVER_ERROR);
    Resource notFoundResource = new StringResource("Resource could not be found!!", "text/plain; charset=UTF-8",
        "UTF-8", Status.NOT_FOUND);
    Map<String, Resource> resources = new HashMap<>();
    resources.put("/login.html", chatRoomLogin);
    ResourceEngine resourceEngine = new RegularExpressionEngine(resources, notFoundResource);
    ResourceContainer resourceContainer = new ResourceContainer(resourceEngine, failureResource);
    TraceAnalyzer analyzer = new LogAnalyzer();
    Container webContainer = new WebContainer(resourceContainer, "Chat/1.0");
    Router webSocketRouter = new DirectRouter(chatRoom);
    Container webSocketContainer = new RouterContainer(webContainer, webSocketRouter, 2);
    WebServer server = new WebServer(webSocketContainer, analyzer, PORT);

    server.start();
  }
}