Facelets and legacy JSP

28 april 2010, door development

It’s well known that Facelets is the far superior technology when it comes to authoring pages using JSF. By default, Facelets has no provisions to include content from JSP pages, or Servlets for that matter. Normally this really isn’t needed. Facelets provides a better and clearer templating mechanism than what JSP has ever offered.

Facelets & JSP in one app

However, when migrating a large application you might have to run a while in mixed-mode, that is running with both Facelets and JSP pages in a single application. It ain’t pretty, but it is explicitly supported. Ever wondered why you are forced to use prefix mapping for this? Well, the Facelets view handler delegates to the default view handler for createView, which transforms a view ID suffix like .jsf or .xhtml to a default one (e.g. .jsp), but leaves the suffix alone when using a prefix mapping like /faces/*. The Facelets viewhandler later decides to handle a request itself or delegate again to the default view handler, based on the suffix. When using suffix mapping, the first delegation had just caused this suffix to be replaced with the default suffix and the Facelets view handler thus can’t make any distinction between a Facelets request and a JSP request anymore.

The above is thus why you have to use prefix mapping for Facelets pages if you want to use both Facelets and JSP in a single application.

JSP includes

Having Facelets and JSP pages in 1 application might be only half of the story. It’s not unlikely that the existing JSP pages make use of included resources that are also JSP based, or maybe Servlet based. Rewriting these to full-fledged Facelets artifacts is technically the best option, but if you have to run in mixed-mode for a while, this means you have to maintain two versions of these includes. Obviously maintaining two separate versions is not ideal too.

One alternative, that I like to present here, is building a bridge component that executes the JSP or Servlet and writes its output to the standard JSF response writer. Thanks to Facelets and JSF in general, such a component is actually not that hard to build:

The component’s source code:



public class JSPIncludeComponent extends UIComponentBase {

public String getFamily() {
   return "components.jsp.include";
}

public void encodeBegin(FacesContext context) throws IOException {
   try {
      ExternalContext externalContext = context.getExternalContext();
      HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
      HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();

      // Create dispatcher for the resource given by the componen's page attribute.
      RequestDispatcher requestDispatcher = request.getRequestDispatcher((String) getAttributes().get("page"));

      // Catch the resource's output.
      CharResponseWrapper responseWrapper = new CharResponseWrapper(response);
      requestDispatcher.include(request, responseWrapper);

      // Write the output from the resource to the JSF response writer.
      context.getResponseWriter().write(responseWrapper.toString());
   }
   catch (ServletException e) {
      throw new IOException();
   }
}
}

The component here gets its content from the resource path denoted by the attribute “page”. To JSF, there is no difference between content that is generated locally in the component and content that the component obtained from somewhere else.

The component makes use of a HttpServletResponseWrapper, which is a fairly common artifact in many legacy Servlet/JSP applications. For completeness, here is the source of the particular implementation I used:



public class CharResponseWrapper extends HttpServletResponseWrapper {

   private CharArrayWriter output;

   @Override
   public String toString() {
      return output.toString();
   }

   public CharResponseWrapper(HttpServletResponse response) {
      super(response);
      output = new CharArrayWriter();
   }

   public CharArrayWriter getCharWriter() {
      return output;
   }

   @Override
   public PrintWriter getWriter() {
       return new PrintWriter(output);
  }

   @Override
   public ServletOutputStream getOutputStream() {
      return new CharOutputStream(output);
   }

   public InputStream getInputStream() {
      return new ByteArrayInputStream( toString().getBytes() );
   }
}

class CharOutputStream extends ServletOutputStream {

   private Writer output;

   public CharOutputStream( Writer writer ) {
      output = writer;
   }

   @Override
   public void write(int b) throws IOException {
      output.write(b);
   }
}

In JSF 1.2 there’s a minor inconvenience where you have to register the component you just created in an .XML file. JSF 2.0 provides a convenient annotation for this, but in JSF 1.2 it has to happen via XML:


<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2"
>
	<component>
		<component-type>com.example.component.JSPIncludeComponent</component-type>
		<component-class>com.example.component.JSPIncludeComponent</component-class>
	</component>

</faces-config>

This can either be put in your regular faces-config.xml, or in a separate faces-config.xml in a META-INF directory on your class-path. The latter option is typically when packaging in a jar, but also works for any regular Java source dir of your web module.

To make the component usable on a Facelets page, we only have to assign it a name space and a tag name. This has to happen in a file with a suffix .taglib.xml that’s also in a META-INF directory on your class-path. Even in JSF 2.0, there are unfortunately no annotations for this. For this example I called the file foo-include.xml:


<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib>
	<namespace>http://foo.com/jsf</namespace> 

	<tag>
	  <tag-name>include</tag-name>
	  <component>
	    <component-type>com.example.component.JSPIncludeComponent</component-type>
	  </component>
	</tag>

</facelet-taglib>

Note that this example uses the older DTD for the standalone Facelets implementation for JSF 1.2. JSF 2.0 uses a more modern xsd, for which an example can be seen here. Also note that we don’t have to define any attributes nor did we had to define any getters and setters for those attributes on the UI component implementation. This convenience does come at a cost though, as your IDE now can’t provide you with any content assist for the component’s attributes.

Finally an example of how it all comes together on an actual Facelets page:


<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:foo="http://foo.com/jsf"
   version="2.1"
>
	<ui:composition>
		<html xmlns="http://www.w3.org/1999/xhtml">

			<head>
				<title>JSP include example</title>
				<meta http-equiv="pragma" content="no-cache"/>
				<meta http-equiv="cache-control" content="no-cache"/>
				<meta http-equiv="expires" content="0"/>
			</head>  

			<body>
				JSP: <br/>
				<foo:include page="/test.jsp"/><br/><br/>

				SERVLET: <br/>
				<foo:include page="/TestServlet"/><br/><br/>
			</body>
		</html>

	</ui:composition> 

</jsp:root>

I wouldn’t recommend using this as a lasting solution, but it might ease a migration from legacy JSP with smelly scriptlets and all on them to a more sane and modern Facelets application.

Arjan Tijms

7 Reacties op “Facelets and legacy JSP”

  1. john draper Zegt:

    Apart from the actual purpose of the component, this also shows how relative easy it is to define a java based custom component in jsf.

    It would be even better if the facelet tag could be generated too from an annotation. The tag attributes could be deduced from annotated getters/setters.

  2. Tom Zegt:

    I’ve worked with jsp and facelets and JSF and Seam and I can tell you, I’d take a dynamically typed language like Ruby, Python or Groovy + jQuery anyday.

    I’m not bringing this up to be negative I’m just kinda curious why in 2010 people are still using JSF when easier to use technologies like Groovy + jQuery are available.

  3. development Zegt:

    Tom, to each his own. We really like JSF and are very productive with it. JSF 2.0 that we started to use in small test projects, works like a charm. Next to that there are tons of high quality components available for it.

    We also like Python a lot. One of our developers builds various plug-ins for Edgewall’s Trac.

    Groovy is very interesting as well. I don’t really understand why you compare JSF with Groovy though. JSF is a web framework and Groovy is a language. You can easily build JSF backing beans with Groovy. As a matter of fact, I’m currently experimenting with just that ;)

  4. Tom Zegt:

    I compare Groovy/JQuery with Java/JSF because they are both technologies used to build similar things – Web apps. Plus, there are “components” that I can build using jQuery in a gsp page that IMO are just as powerful as anything I’ve seen in JSF.

    But yea to each his own. I guess I’m still a bit irrationally hostile at the J2EE world that made things too harder to do ;)

  5. Tom Zegt:

    As an example, every day I find a new awesome widget in jQuery. Check out this “bubble” menu:

    http://tympanus.net/Tutorials/BubbleNavigation/#

  6. development Zegt:

    The bubble widget surely is cool Tom, thanks for the pointer ;)

    The point of JSF is not so much that it somehow ‘creates’ widgets like that on the server, but that it provides a server side component interface for exactly those kind of cool widgets that jQuery provides.

    With JSF, I could very easily attach a Java or Groovy backing bean that reacts to events of that bubble widget or the other way around, feed the widget with data coming from the backing bean. So, JSF doesn’t aim to compete with jQuery or other powerful javascript libraries at all. On the contrary, it embraces these technologies fully and ‘only’ aims to provide a server side component interface for them and offers the programmer an easy way to compose complicated pages via parts (templating aka composition or tiling).

    See for example this component: http://www.primefaces.org:8080/prime-showcase/ui/terminalHome.jsf or any of the other examples on that page. Those are Javascript widgets with an easy server side component ‘interface’.

    I understand some hostility at J2EE. At Jdevelopment this is what we started out with. JSP, JSTL , early JNDI, EJB2 and the needlessly verbose XML were all a pain to work with.

    But Java EE is almost like a completely different platform; very light weight and really friendly towards open source and projects from the community. As said, with JSF we embrace all the best that’s available in Javascript libraries and it’s trivial to write the backing bean in Groovy, or if you don’t need the extra abstraction layer skip the backing bean entirely and have your UI components bind directly to your business classes.

  7. Insert page jsp in facelets « Raphael Rodrigues – Java Technical Blog Zegt:

    [...] i found this solution . I really thought  a pretty solution, but unfortunatelly this solution was not enough for me. This [...]

Plaats nu je reactie:

best counter