Extreme Java When vanilla Java is not enough

8Jul/110

TDD with Wicket

I'm starting to get in love with Wicket. I can easily isolate webpages, programming and testing. While my fictional webdesigner makes evil plans about the pages, my programmer (that's me) can develop all pages using TDD. And I'm not only talking about making unit tests for UI. I'm talking about creating the test BEFORE the implementation. See how I'm achieving this:

Let's create a new page. Since the design will be made by the designer, I only invest on the essential tags. But, even before creating my HTML, I can create the tests. From the requirement, I will have two screens: one list of all cities grouped by state, and a page with a list of vCards for that city. First, the tests (after all, it's TDD!):

@Test
public void testRendering() {
  WicketTester t = new WicketTester(CitiesPage.class);
  t.startPage(CitiesPage.class);
  t.assertRenderedPage(CitiesPage.class);
}
@Test
public void testStateBinding() {
  WicketTester t = new WicketTester(CitiesPage.class);
  t.startPage(CitiesPage.class);
  t.assertComponent("states", ListView.class);
  t.assertListView("states", getStates());
  t.assertComponent("states:0:name", "name-of-first-state");
}
@Test
public void testCityBinding() {
  // new tester, start, assertList...
  t.assertBookmarkablePageLink("states:0:cities:0:link",
                               NextPage.class, "city=0");
}

I put all tests here for simplicity, but, in TDD, I create only the first test, then correct, go for the second and so on. This will fail. I solve by creating the stub page and the bindings:

<div wicket:id="states">
  List of State <span wicket:id="name">Lorem ipsum</span>
  <div wicket:id="cities">
    <span wicket:id="name">City's name</span>
    <a href="#" wicket:id="link">Go for it</a>
  </div>
</div>

And, the Wicket's WebPage constructor:

public MyPage() {
  add(new ListView("states", getStates()) {
    protected void populateItem(ListItem item) {
    }
  });
}

And, the best part of TDD is I can make my code step-by-step: I implement the list binding, then I implement the list iteration, then, the cities binding inside "populateItem", until I finish the page. Much easier than implementing then testing. And it's a lot error-proof, too. When we make the code before the test, it's common to make a test that "just works" and having a lot of small glitches. Thinking about the test first, we develop code that needs to pass the requirements.

I finally fulfilled my dream of doing real TDD, from UI to persistence! Thanks wicket team! Now, I only hope it does not have any performance (or security) problems.

The last step (that usually takes huge time from me) is making a design that fits the code. But this part will be darn easy: I make a free (as in "freedom") design with a WYSIWYG editor and add the wicket markups, testing it direct on Safari/Firefox/IE/Opera, without the need of a slow "edit-deploy-run-test" cycle (mandatory for JSF or similar).

Filed under: Java EE No Comments
7Jul/110

vHost capabilities with Wicket

I started using Wicket because I liked how easy is to create SEO URLs and design-centric applications. One crazy feature I got working was customizing an application based on the vhost. The example I asked on StackOverflow used a fictional "abc.com" hosting with "abc.com.br" and "abc.com.pl" aliases.

The example works as this: all three vhosts are on the same codebase (i.e. the same "host" on Tomcat, with three aliases) and shows the same data. The difference between ".com", ".com.br" and ".com.pl" relies on how the data is shown to the user. There are two major country-related materials:

  1. Page design - ".com" must see an American tribute (USA is #1, American Flag, etc); ".com.br" must show Brazilian stuff (Samba, Soccer teams, etc); ".com.pl" shows Polish material. This was really easy, using a ResourceStreamLocator in my Wicket Application:
    getResourceSettings().addResourceFolder("");
    getResourceSettings().setResourceStreamLocator(
        new ResourceStreamLocator() {
      @Override
      public IResourceStream locate(Class clz, String path) {
        String host = ((WebRequest) RequestCycle.get().getRequest())
            .getHttpServletRequest().getServerName();
        String basePath = trimFolders(path);
        IResourceStream res = super.locate(clz, host + "/" + basePath);
        return (res != null) ? res : super.locate(clz, basePath);
      }
      private String trimFolders(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
      }
    });
  2. Contextual URL mounting - this part was hard. Assume a page named "Page1". It must be accessed using "abc.com/page1", "abc.com.br/pagina1" and "abc.com.pl/strona1", but not with combinations (e.g. "abc.com/strona1"). After asking in SO, I got an answer from "biziclop" that lead me to the solution I wanted. It's a servlet filter mapping vhost to WicketFilter instances, isolating Wicket application instances (as biziclop sugested):
    @WebFilter(urlPatterns = "/*")
    public static class Filter implements IWebApplicationFactory,
                                          javax.servlet.Filter {
      private final Map filters = new HashMap();
      private final ThreadLocal hosts = new ThreadLocal();
      private FilterConfig config;
     
      @Override
      public void init(FilterConfig filterConfig) {
        this.config = filterConfig;
      }
      @Override
      public void destroy() {
        for (WicketFilter w : filters.values()) {
          w.destroy();
        }
        filters.clear();
      }
      @Override
      public void doFilter(ServletRequest request,
          ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
        hosts.set(request.getServerName());
        try {
          getFilterForHost().doFilter(request, response, chain);
        } finally {
          hosts.remove();
        }
      }
      private WicketFilter getFilterForHost() throws ServletException {
        String host = hosts.get();
        if (!filters.containsKey(host)) {
          WicketFilter w = new WicketFilter() {
            @Override
            protected IWebApplicationFactory getApplicationFactory() {
              return Filter.this;
            }
          };
          w.init(config);
          filters.put(host, w);
        }
        return filters.get(host);
      }
      @Override
      public WebApplication createApplication(WicketFilter wf) {
        return new WicketApp(hosts.get());
      }
    }
Filed under: Java EE No Comments