Sunday, November 30. 2014
Compiling Servlet (part III)
This is the third and the last entry of the in-line compilation PoC series. The first entry started the development of a compiling servlet that loads, compiles and executes another servlet source file. The second post extended the previous servlet adding JSP management. Both entries were based on the idea of having my own parser and compiler libraries (the ECJ Java compiler is used for compilation and the Jasper 2 engine for parsing JSP). This third entry is dedicated to another JavaEE technology that can be integrated inside the same idea (template hot replacement): Java Server Faces (JSF). In this case we will see that no compilation is needed, not even a servlet is necessary but, nevertheless, I decided to not change the title of the series.
JSF was born like a component driven user interface technology which used internally JSP to render HTML pages. The technology offers a lot of JSP components to construct the pages and, besides, it gives an easy access to Java code though JSF beans (little java objects that links presentation and logic layers). JSF technology is based on the Model View Controller (MVC) architecture for separating logic from presentation. Since JSF 2.0 the JSP dependency was optional by using Facelets instead. Facelets is a new template technology which is much simpler and dynamic and, in my opinion, much better suited for HTML generation. Finally it is important to consider that around JSF there are a lot of different technologies which are more or less related: CDI (Context Dependency Injection), EL (Expression Language), AJAX,...
Going back to the in-line compiling issue, JSF (as I commented in the previous paragraph) gives us two different approaches: JSP or Facelets. In the case of JSP the solution would have been more or less similar to the previous post (at the end JSP technology is there and it follows the same rules). But with Facelets the story is very different, because Facelets integrates seamlessly in the HTML templating system without any compilation. So in this entry the idea is loading the XHTML facelet templates dynamically without re-deploying the JavaEE application.
With JSF / Facelets the standard itself provides ways of extending the solution in order to create your own resource resolvers. The current JavaEE standard recommends to use a ResourceHandlerWrapper in order to provide your own resource handler implementation (a resource handler is a class to inject into the Facelets system your own templates or resources). Any JSF resource handler is intimate related with the URL class because they return resources as an URL. So, in order to implement your own resource handler you usually have to create in turn your own URL connection. For that purpose I followed this documentation and invented my memory protocol (syntax mem:/some/resource.xhtml). This memory protocol is very simple, the URL path will be read (as in the previous entries) from the application context as a resource (remember that this is a PoC, this protocol in a real scenario should read the templates from some external repository). The classes needed to create your own URL protocol are the following (see the previous link for further details):
sun.net.www.protocol.mem.Handler: The handler that manages my memory URL, it should extend the URLStreamHandler and, in this case, only overrides the openConnection method. That method locates the resource from the context of the application. The package and name of the class are imposed by the JavaSE standard.
es.rickyepoderi.jsf.MemoryURLConnection: The previous class returns a MemoryURLConnection, this class extends the URLConnection class and is extremely simple for this PoC. It is just a wrapper of the URL and the resource located and stored as an InputStream.
es.rickyepoderi.jsf.MemoryStreamHandlerFactory: This factory is used to manage memory URLs and should extend the URLStreamHandlerFactory class. The factory must be added to the JVM someway. In common JavaSE a user factory is added using URL.setURLStreamHandlerFactory but Tomcat already uses some custom factory so the container gives the developer the possibility of adding more factories using the method TomcatURLStreamHandlerFactory.addUserFactory.
With the memory URL implemented (a fake URL which just loads a resource from the context of the application), two new classes were developed in order to have my own JSF resource handler:
es.rickyepoderi.jsf.MemoryResourceHandler: The wrapper resource handler that locates the facelet templates from the context. JavaEE gives us a ResourceHandlerWrapper which wraps the default JSF handler and let us extend its functionality. In the PoC the handler just overrides the method createViewResource (the method for loading XHTML templates). The method first tries to locate the template inside the wrapped / default JSF handler but, if null is returned, a MemoryViewResource is created.
es.rickyepoderi.jsf.MemoryViewResource: The memory view resource is an extension of the ViewResource class that acts as a link between the Facelets system and the memory URL.
And that is all. The process is really cumbersome but the idea behind it is really simple. A new resource handler is implemented which has the mission of locating resources for Facelets templating system. In my example only the view resources are managed (the XHTML templates), but any other resource (images, CSS, JS files,...) can be retrieved overriding the other methods of the wrapper. As resource handlers return resources as URL objects, a new memory protocol was also created to represent my resources. Using a new URL protocol in Java is a bit crazy because you have to implement several classes and integrate the new protocol via the URL factory.
Adding the handler to the faces-config.xml file is the last step to activate it inside the application:
<application> <resource-handler>es.rickyepoderi.jsf.MemoryResourceHandler</resource-handler> </application>
With all the previous classes and the handler correctly specified in the JSF configuration file a JSF page can be requested and it is being server without having it in the common directory. The PoC handler tries to locate the page inside the context if it is not found by the default JSF handler. I am going to say it another time, the final target would be retrieving the XHTML template from an external repository, the PoC just retrieves it from another non common directory as an example.
Here it is a little video that shows the three scenarios presented in the series. First the Compiler Servlet is accessed and another one (CompiledServlet) is compiled and executed. Then two different JSP files are requested, both are located inside the application bundle, parsed, compiled and executed. And finally two facelets pages are rendered, the subject of this entry, using the customized handler the XHTML files are recovered from a non standard directory. The final Netbeans project CompileTest (without libs) is uploaded here, it contains all the classes presented throughout the series (it is attached in this last post because now you have understood all the scenarios). Remember that the application only works in tomcat 8 and JSP processing needs some weird steps (check the previous entry).
This last entry is much more interesting than the previous two. JSF / Facelets technology already takes into account the fact of implementing your own resource handler. Therefore the final solution is not as weird as the previous ones. With JSF and Facelets the XHTML templates can be obtained from another place easily, opening the possibilities of storing them inside any external repository. Using Facelets no compiling is necessary (a JSP needs to be compiled, this fact complicated the solution a lot). I think that JSF / Facelets is the best suited javaEE technology for the hot-replacement feature I was investigating. As I commented at the beginning of this series I wanted to explore a hot-replacement technique for a new idea I have in mind. JSF / Facelets is the winner, the template is dynamically loaded in a standard way and perfectly integrated inside the framework.
Best Regards!
Comments