Using JRebel with Tiles
Tested with versions: embedded Tiles in Struts 1.1.x, Struts 1.2.x, Struts 1.3.x; Tiles 2.0.x, Tiles 2.1.x, Tiles 2.2.x..
Apache Tiles is a popular JEE framework that helps to build web UIs by assembling smaller page fragments (JSPs) together into complete web pages, thus promoting flexibility, abstraction and reuse. The biggest difference between Tiles1 and Tiles2 is that while Tiles1 is a subframework of the Struts1 web framework, Tiles2 was adjusted to be a completely independent framework. Ideologically they are the same, and also the client code for Tiles1 and Tiles2 is very similar.
JRebel itself just reloads the Java .class files. What Tiles is all about is bunch of fragment definitions connected with corresponding .jsp templates that can then be combined and reused to build up larger web pages. These definitions are by default held in the
tiles-defs.xml for Tiles1) configuration file in the WEB-INF directory (can be overridden by the user). Usually, these definitions are read once when the Tiles container starts up and cached; newer versions also support reloading. To make web development with Tiles smooth with JRebel and eliminate the need for redeploying the application, we also have to tell Tiles to reload these fragment definitions once they have changed.
The reloading is implemented by JRebel’s JRebel-Struts-Tiles and JRebel-Tiles2 plugins; for newer Tiles2 versions, Tiles is just configured to handle the reloading by itself. The plugins are bundled with
jrebel.jar since JRebel release 3.2 and are enabled by default; normally a JRebel user doesn’t need to know anything about them. To force the plugins to be disabled/enabled, add the JVM argument
true to enable).
Tiles 2.2 was used for the following example, but everything is very similar with other versions of Tiles. The assumption for this to work is, as always, that you have enabled JRebel in your app-server and correctly configured
rebel.xml to tell JRebel which locations it should monitor for updated resources. Refer to other documentation for information on how to do that.
We set up our Tiles2 container using the
SimpleTilesInitializerServlet. This shouldn’t really matter from JRebel’s point-of-view. Anyhow, here’s our
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>TilesServlet</servlet-name> <servlet-class>org.apache.tiles.web.startup.simple.SimpleTilesInitializerServlet</servlet-class> <load-on-startup>0</load-on-startup> </servlet> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
Let’s begin with a single Tiles page that includes another JSP (a completely static one with the classical “Hello World” message).
<%@ page session="false" %> <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <tiles:insertDefinition name="test.definition" />
Here is the initial contents of the Tiles definitions file:
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN" "http://tiles.apache.org/dtds/tiles-config_2_1.dtd"> <tiles-definitions> <definition name="test.definition" template="/page1.jsp" /> </tiles-definitions>
Let’s deploy this initial application. Not very surprisingly:
Now the more interesting part is whether we can add new things to the page without redeploying our application. We start by editing our
index.jsp to include another Tiles definition:
<%@ page session="false" %> <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <tiles:insertDefinition name="test.definition" /> <tiles:insertDefinition name="another.definition" />
The JSP pages are automatically recompiled by the appserver so we don’t need to worry about this here. But we do need to add this new definition to
... <definition name="test.definition" template="/page1.jsp" /> <definition name="another.definition" template="/another.jsp" > <put-attribute name="msg" value="A message from Tiles" type="string" /> </definition> ...
.. and we also need a new JSP template for it:
<%@ page session="false" %> <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> <p>Another definition</p> <tiles:insertAttribute name="msg" />
What will happen when we hit refresh in our browser?
A message from Tiles
First, the container will recompile the updated
index.jsp and the completely new
another.jsp (assuming that your IDE and
rebel.xml are configured correctly: the IDE needs to copy the jsp into the same directory that JRebel is monitoring for updates). JRebel plugin will force Tiles to check if its definitions have been updated – indeed they are, and Tiles will reload them. Had we had any Java class files, they would also have been reloaded by JRebel. From there on, the request is served as normally, and the updated page is displayed to the user. The result is that we can see all our changes without a redeploy.
Notice that we didn’t use a single Java class in this overview, so actually there wasn’t anything on what to demonstrate JRebel’s class reloading feature. This was a conscious decision: normally you would use Tiles together with a JSP-based web framework, but we didn’t want to complicate this article with any third-party technologies. Neither did we want to demonstrate Tiles directly on top of a custom servlet, as this is not the way a normal person would build a web-application with Tiles, and might lead to wrong conclusions. Just take our word for it: when you have a JEE application with Java classes initializing the dynamic content and Tiles organizing the JSP pages, you can develop it without redeploying due to ..
- JRebel reloading the classes
- the app-server recompiling the JSPs
- the JRebel-Tiles plugin forcing Tiles to reload its definitions
Warning! In Tiles 2.0.x, there are two TilesContainer implementations that you can choose between:
CachingTilesContainer. Definitions reloading is currently unsupported for the
BasicTilesContainer, so make sure you configure your application to use the other one. To do this, set the following context-param in your web.xml:
<context-param> <param-name>org.apache.tiles.factory.TilesContainerFactory.MUTABLE</param-name> <param-value>false</param-value> </context-param>
Tiles versions since 2.1.x are not known to be affected by this.