JSP and Wicket, sitting in a tree…

Most programmers like writing things from scratch, but throwing it all away and starting again often isn’t feasible, especially with web applications. I’ve recently been migrating a JSP/servlet based app to Wicket and I suspect there are many people who are going down this path.

There aren’t enough man hours available to rewrite everything at once, so most people need some way to run their new Wicket code alongside their existing JSPs. This can prove tricky. We started with the two halves completely separate. This posed a few problems:

  • Navigation and page border type components need to be duplicated in the JSP and Wicket code. The functionality and look & feel must be identical, and it’s hard keeping this all in sync.
  • You probably need to tie two different security mechanisms together (maybe wicket-auth-roles and the ACEGI JSP taglib).
  • It’s hard to link pages together: you can’t form links to JSPs easily from Wicket components without taking into account relative paths on mounted pages; it’s also sometimes difficult going the other way.

So, what would the ideal be? Well, Wicket is better than straight JSPs, right? So let’s assume you’d like to have your navigation and things in Wicket, and that you’d like to embed your existing JSPs somewhere into the body of a Wicket page.

I’d guess most people structure their JSPs something like this:

[-]Download index.jsp
<jsp:include page="include/top.jsp" flush="false">
   <jsp:param name="title" value="My Page"/>
</jsp:include>
<!-- Body HTML, taglibs, etc. for the page go here. -->
<jsp:include page="include/bottom.jsp" flush="false" />

What we’d probably like to do instead is replace the include stuff with the surroundings for a Wicket page and somehow have the body HTML, etc. in the middle magically stay in place.

A way of structuring your Wicket pages is to have each one extend some custom abstract MyWebPage class. This will itself extend WebPage and will add the navigation components and so on. You’ll then use the <wicket:child> and <wicket:extend> tags on your actual pages to add in the real body content. That’s how I generally do it, anyway. I’m going to strip that sort of thing out of my example code to keep it simpler, but you can imagine how it would work with it included. The key thing is that we’re going to end up with a Wicket page with some JSP embedded in it.

It’d also be really nice if we could get the page to continue to be rendered at its existing URL. Your customer might have bookmarks to your existing JSPs, and more importantly, your JSPs may do internal redirects and things that would need auditing and fixing otherwise.

First off, we need to create a basic Wicket WebPage that will embed a JSP fragment inside it. The ServletContext provides a RequestDispatcher that lets you forward and include requests/responses, letting you do this. This is not the same as doing a 302 redirect - it all happens in a single request. We can use this to embed a JSP fragment into our Wicket page.

The HTML for the page is simple as can be:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.sourceforge.net/">
<body>
    <span wicket:id="content"></span>
</body>
</html>

Here is the Java:

public class EmbedJsp extends WebPage {
  public EmbedJsp(PageParameters params) {
    this.setVersioned(false);
    page = params.getString("page");
    if (page == null) {
      throw new WicketRuntimeException("Bad page param.");
    }
 
    add(new WebMarkupContainer("content") {
      private static final long serialVersionUID = 1L;
 
      @Override
      protected void onRender(MarkupStream markupStream) {
        markupStream.next();
        try {
          WebRequestCycle cycle = (WebRequestCycle)RequestCycle.get();
          ServletRequest request = cycle.getWebRequest().getHttpServletRequest();
          ServletResponse response = cycle.getWebResponse().getHttpServletResponse();
          ServletContext context = ((WebApplication)Application.get()).getServletContext();
          context.getRequestDispatcher("/" + page).include(request, response);
        }
        catch (Exception e) {
          throw new WicketRuntimeException(e);
        }
      }
    });
  }
}

Then you just strip the <jsp:include> (or <%@ include file=”foo” %> if you’re using that) elements out of your JSPs and things should work nicely if you go to something like
http://localhost:8080/?wicket:bookmarkablePage=:com.foo.EmbedJsp&page=test.jsp in your browser.

It’s important to note the following requirements and limitations with this approach:

  • You must configure your Wicket application with getRequestCycleSettings().setBufferResponse(false) otherwise your Wicket page will be flushed out after the JSP that’s included immediately.
  • You can’t change the response status code or set any headers in your JSPs.

Now, that’s nice, but we need to feed everything with these crazy URLs, and if someone goes to /test.jsp in a browser, they get the core JSP fragment, but with no surrounding mark-up/navigation components. We could use mod_proxy out the front end to rewrite these, but we really should be able to fix this in-container.

What follows is a bit complex and is currently built against a pre-release version of Wicket 1.3. You may need to use the source to make it work. If you’re a clever person, you should treat this as an approximate guide for fixing this problem and not a complete boxed solution. If you’re not a clever person, you should probably stop reading about now - I have no intention of supporting this properly and it’s a bit of a hack. Do ask questions in the comments if you like and I’ll do my best to help, however.

OK, so what we want to do is have the JSP notice that it’s not embedded in a Wicket page and forward the request on to Wicket so that it is. Specifically, we want to forward it our Wicket EmbedJsp WebPage, with the appropriate parameter tacked on the end so it renders the correct page. With the exception of coping with relative URLs in Wicket 1.3, this turns out to be fairly simple. At the top of each JSP, include something like this:

<%@ include file="embed.jsp" %>

And create an embed.jsp file that looks like this:

<%@page import="wicket.RequestCycle"%><%
// Check to see if we're in a Wicket render cycle.
if (RequestCycle.get() == null) {
  String path = request.getServletPath();
  String query = request.getQueryString();
  String jspBaseUrl = URLEncoder.encode(path.substring(1), "UTF-8") + (query != null ? "&" + query : "");
 
  String prefix = "/Home";
  for (int i = 1; i < path.length(); i++) {
    if (path.charAt(i) == '/') {
      prefix += "/j";
    }
  }
  String fwd = prefix + "?wicket:bookmarkablePage=:com.foo.EmbedJsp&page=" + jspBaseUrl;
  getServletContext().getRequestDispatcher(fwd).forward(request, response);
 
  // This return is slightly dubious.
  // Probably better to create a tag in a taglib that does nothing
  // and returns SKIP_PAGE on doStartTag()/doEndTag() instead.
  return;
}
%>

You may have to tweak the prefix to work properly with relative URLs in 1.3 (it’ll need your servlet/filter mapping prefix if that’s non-root). Have a play and see what works for you. You may also need to point it to a real mounted page (I’ve used “/Home”).

Obligatory list of further caveats and gotchas:

  • NB: If you’re using the WicketFilter on the 1.3 branch of Wicket, make sure you’ve configured it in your web.xml with FORWARD and REQUEST <dispatcher> elements in the <filter-mapping> in your web.xml.
  • This approach is only tested with Wicket 1.3 but it can be applied with tweaking to 1.2.x or 2.0 branches (strip out the prefix stuff in embed.jsp for 1.2.x and just point it at /?wicket:bookmarkablePage… instead).
  • WICKET-40 will probably bite you if you try to mount your EmbedJsp page and then pass it JSP page parameters that have slashes in.

Hopefully this should give you a good starting point for how this sort of stuff can work. I’ve even done some work in Wicket 1.3 to try to make this cope with multiple levels of JSP forwarding and includes, so that things like relative URLs for Wicket resources on error pages that are triggered from JSP embedded pages work properly (even though the URL in the included request for the error page won’t match the original URL that was requested).

If you have issues, please shout. If you make it work, such that this overly-long blog entry has not been in vain, please also shout. :-)

This entry was posted in Apache Wicket. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

8 Comments

  1. joed
    Posted March 1, 2007 at 7:38 pm | Permalink

    Great post, and very useful info.
    Thank you!

  2. Joril
    Posted July 9, 2007 at 9:16 am | Permalink

    What about embedding Wicket within a JSF page? Do you think this technique will work?
    Many thanks :)

  3. Posted July 9, 2007 at 9:28 am | Permalink

    Possibly. But ugh, why would you want to use JSF? :-)

  4. Joril
    Posted July 9, 2007 at 4:03 pm | Permalink

    That’s why I’m trying to migrate to Wicket :D But right now we can’t afford to “translate” our existing JSF modules to Wicket, so I was looking for a way to mix them with the new (Wicket) ones.. I managed to do it via two servlets (one for JSF and one for Wicket) and an iframe.. It’s not very pretty to see, but it’s straightforward..
    Thanks for your article and your attention :)

  5. Posted October 17, 2008 at 8:22 am | Permalink

    Hi,

    can you explain how to user your EmbedJsp page in context with wicket child and extend? When i tried to put it in a div container, it will always rentered outside the div. It where rendered above the rest of the page. The jsp only contains :
    and rendered was this:

    Apache Tomcat/5.0.25

    JSP-Page: [test.jsp] embedded in Wicket

    JSP-Page: [test.jsp] embedded in Wicket

    [JSP CONTENT]

  6. Posted December 10, 2008 at 12:38 am | Permalink

    We have a similar situation, and I am new to Wicket so Im wondering if you can tell me if the following is possible and approx. how-to?

    Overview: we have a wicket page that generates some html+javascript. We want to ‘call’ this from within the application. we currently use httpclient to make a http request back to our server and take the response and munge it. The overhead of the extra request is an unsatisfactory load on our servers.

    Instead of using wicket internally to make a “internal redirect” to a JSP servlet we want to make that call to another wicket page and take the ‘response’ and munge it.

    An additional constraint is to call this ‘internal redirect’ from anywhere in the code and not neccessarily within a http request (i.e., from a Quartz thread).

    Any ideas here are appreciated, thanks
    Mike

  7. Amit
    Posted December 11, 2008 at 4:25 am | Permalink

    This is a very nice article.
    We have existing reusable components that have been built using strut/JSP etc. which made me skeptical about combining it with Wicket. I’m definitely gonna give it a try now.

  8. Posted December 13, 2008 at 7:46 pm | Permalink

    Mike: You can, in theory, render Wicket pages without HTTP requests (the WicketTester code sort-of does this, for example). I seem to remember some threads on wicket-users about that, so I suggest you search the archives and post there if you have questions.

One Trackback

  1. [...] I found several nice tutorials on the net such as this one as well as the Wicket tutorials. Right now I’m looking at integrating old code (JSP), and Acegi for a possible migration path [...]

Post a Comment

Your email is never published nor shared.