I hate xml configuation files. They look awful. As a result, they can be very time-consuming to write, and are very error-prone. There's no type-safty checking whatsoever. I would rather keep configurations within my Java source code. After all, we programmers are the only people looking at these configurations, why create trouble for ourselves? Luckily, with annotation support in Java, we can now completely do away with xml files for developing Web applications. Here is my recent experience integrating Spring framework with DWR on a Jetty sever.

Spring

Spring framework seems to be very popular in enterprise Java world. The core idea of "inversion of control (IoC)" seems to be trival (I bet any good programmers are already doing IoC without knowing the name). But sometimes, taking a simple idea seriously can get you a lot of mileage. Map-Reduce is another example on top of my head. Anyway, I decided to use Spring to manage objects in one of my dependency-rapidly-getting-out-of-hand Web projejcts. Spring was known for its xml hell, but recently advances have given it annotation-based configuration and JavaConfig. Now we can do Spring configuration completely in Java source code.  To do this, I put these jars in my java built path:

  • spring.jar (Note that this is spring 2.5.6)
  • asm-3.2.jar
  • aspectj-1.6.5.jar
  • org.springframework.config.java-1.0.0.M4.jar
  • cglib-2.2.jar
  • dwr.jar
  • all jetty modules

JavaConfig needs at least one class annotated with @Configuation. Here's mine:

@Configuration
@AnnotationDrivenConfig
@ComponentScan("com.company.app")
public class AppConfig {
  // no need to list our own beans here
  // since we use autowiring and component scan
}

Yeah, the configuation file is pretty empty. In fact, @ComponentScan tells Spring to search for classes annotated with @Component (or several other stereotypes: @Controller, @Service, etc. See Spring doc for details) under "com.company.app" base package, and automatically inject all the dependencies labeled @Autowired. For example:

@Controller
@RemoteProxy(
  creator = SpringCreator.class,
  creatorParams =
    {
      @Param(name = "beanName", value = "UIController"),
      // this is needed due to a DWR bug
      @Param(name = "javascript", value = "UIController")
    },
  name = "UIController")
public class UIController {
  @Autowired
  private BackEnd backEnd;
  @Autowired
  private QueryProcessor queryProcessor;

// other stuff here
}

Both backEnd and queryProcessor will be automatically instantiated and injected here. This is really cool! What a time saver! Oh, don't forget to write setter for these private members. And the @RemoteProxy annotation is for DWR. More later.

Jetty

Now we can start our Web server this way too, without the damned web.xml and all that. Basically, we will embody a Jetty server in our Java application. Why Jetty? It's small, fast and flexible. I created a class to wrap it up and also created two servlets for it. One serves regular files, another handles ajax request using DWR. 

@Component
public class UIServer {
  public static final File HTML_FILE_DIR = new File("../Web");
  private int port = 80;
  private Server jettyServer;  

public void start() {
    try {
      // Create an instance of Jetty Web server
      jettyServer = new Server(port);
      ContextHandlerCollection contexts = new ContextHandlerCollection();
      jettyServer.setHandler(contexts);

// this servlet serves static files
      ServletContextHandler ctxDocs=
      new ServletContextHandler(contexts, "/", ServletContextHandler.SESSIONS);
      ctxDocs.setResourceBase(HTML_FILE_DIR.toString());
      ServletHolder ctxDocHolder= new ServletHolder();
      ctxDocHolder.setInitParameter("dirAllowed", "false");
      ctxDocHolder.setServlet(new DefaultServlet());
      ctxDocs.addServlet(ctxDocHolder, "/*");

// this DWR servlet handles UI requests
      ServletContextHandler ctxUI =
      new ServletContextHandler(contexts, "/ui", ServletContextHandler.SESSIONS );
      ServletHolder ctxUIHolder= new ServletHolder();
      ctxUIHolder.setInitParameter("debug", "true");
      ctxUIHolder.setInitParameter("jsonpEnabled", "true");
      ctxUIHolder.setInitParameter("crossDomainSessionSecurity", "false");

// Specify the classes (comma delimited fully qualified class names)
      // to be exposed to Web browser
      ctxUIHolder.setInitParameter("classes", "com.company.app.UIController");
      ctxUIHolder.setServlet(new DwrServlet());

ctxUI.addServlet(ctxUIHolder, "/*");
      contexts.setHandlers(new Handler[]{ctxUI, ctxDocs});

jettyServer.start();
      jettyServer.join();

} catch (Exception e) {
      e.printStackTrace();
    }
}

We now need to bootstrap our application with JavaConfig by creating an application context.  

public class App {

public static void main(String[] args) throws Exception {
    JavaConfigApplicationContext ctx =
      new JavaConfigApplicationContext(MidasConfig.class);

// so DWR knows where to find classes
    SpringCreator.setOverrideBeanFactory(ctx);

UIServer uiServer = ctx.getBean(UIServer.class);
    uiServer.start();
  }
}

DWR

DWR allows javascript on browser to directly call Java methods on server. It's like a RPC thing, which is pretty convenient. It's my favirate ajax communication layer. Notice we have already covered most of the configurations needed for DWR in the code fragments above. Here's some explanations. The @RemoteProxy annotation basically says that "expose this class to javascript". Within such classes, @RemoteMethod annotation makes annotated methods visible for javascript to call. More details please see DWR documentation.  In addition to such annotations, we did two tricks here to make DWR configuration completely devoid of any xml files. First we passed DWR some configurations through jetty's servletholder. Second, we used SpringCreator of DWR, which basically ask Spring for objects.  

Well, it took me lots of googling to put this together. Hope it would be useful for someone.



Comments

comments powered by Disqus