JS Ext

Wednesday, December 11, 2013

XSLT-based replacement for JSPs

Every time I start a new project, I like to throw in something new and unique.  I experiment with using new technology or mixing old and new technology.  Sometimes the experiment is a complete failure.  Sometimes the experiment leads to a pattern that I start using over and over again.

For this project, I decided to try using an alternative to JSPs.  I wanted to replicate some of the features that JSPs support, though.  The main three features that I wanted were support for some sort of tag library, support for reading information from some sort of Java bean and support for basic flow control like loops.  There were a few things that JSPs didn't support that I wanted as well.  I also wanted to support unit testing with mock support and support for reading all files out of the classpath as opposed to the filesystem.  I want the app to get deployed as a "bag of jars" instead of a war file that requires Tomcat to be setup.

The System

The system I came up with is merges multiple technologies together, but the main technologies are XML and XSLT.  To understand the system, lets take a single GET request and decompose the main steps that occur.

GET /html/index.html

Step 1 - Create the page-specific bean

Every page gets a bean class and bean factory.  For the index.html file, the bean class would be IndexBean.java and the factory would be IndexBeanFactory.java.  The rendering engine would use reflection to instantiate a new IndexBeanFactory class.  This factory would then create an instance of the IndexBean class using the request object.  The factory would make any midtier calls that are needed, then call setters on the bean.  The bean acts as a simple container for data.  In the JSF world, the bean mimics a request-scoped ManagedBean.

Step 2 - Serialize the page-specific bean to XML

I found a really great Java library for serialization to XML.  The library is called Simple.  It is really easy to use.  In our example, the render engine takes the IndexBean instance that was created by the factory and serializes it to XML.  Now we have an XML document that represents all the data that would be visible on the page.

Step 3 - Transform the Bean XML to HTML and Custom Tags (Intermediate XML)

This is where the "replacement" of the JSP file comes in.  In this example, the developer would write index.xsl instead of index.jsp.  This XSL file contains a mix of HTML and Custom tags.  You can use XPath and the <xsl:*> namespace tags to do logic, like loops.  As a devloper, you are transforming the page bean xml to HTML.  It isn't 100% HTML, though.  The goal is for the "syntax" of this "visual code file" to be easy to learn for JSP/JSTL/Facelets developers.  When you want an HTML tag, you just put an HTML tag.  If you have a "tag library", you can use those tags.  This requires developers to learn XSLT, but as I have pointed out previously, every developer lists XSLT regardless of their experience with it.

Step 4 - Transform the Intermediate XML to HTML

While the Intermediate XML has HTML tags, it also has Custom tags.  The final step in the process is to perform an XSL transformation to translate the Custom tags to HTML tags. This is where your Tag Library developers come in.  They maintain their own XSL file that contains all the custom tags.  You can even import multiple tag libraries together if you have multiple teams doing this.  This step is what allows UI developers to use reusable code in their pages.


In this example, you end up with an HTML file from an XSL file.  Although a bit awkward at first, it should be easy to pick up.  For developers, they are only using technology that they should already know!  Your tag library developers stay in XSL/HTML land.  No more Java code.  Now, lets talk about some of the goals again.


Tag Library

This system supports having a tag library.  Developers use the custom tags in Step 3 while the tags are implemented in Step 4.  My app makes use of JQueryMobile.  For every component that I use, I have created a custom tag for it.  This makes it fast for me to write pages, since I don't need to copy and paste the HTML for a ListView over and over again.  The tag library is even segregated, so if you had a special team that owned this file, they are not sharing the file with the developers.  These xsl files are read from the classpath, so they can even be bundled with different jar files.

Reading from Java Beans

In the JSP and Facelets world, developers use Expression Language (EL) to access various Java beans.  With this technology, we serialize a single bean (tree) into XML.  This allows us to use XPath to read from the "Java Bean".  Although we are technically reading from XML instead of the bean, it can conceptually be thought of as reading directly from the bean.  This is implemented in Step 2.

Flow Control

Flow control that simulates JSTL is implemented in Step 3.  Developers use standard XSLT tags like <xsl:forEach> to do flow control.  These contructs are not new.  Developers should be semi-familiar with it.  The learning curve should be pretty minimal.

Testing with Mock support

Every step in the pipeline can be unit tested.  These previous step in the pipeline can be mocked to perform a true unit test.  You can even integration test the entire pipeline by mocking out the midtier calls.  You can use XmlUnit to to perform an XPath assertion.  You use Test Driven Development on your UI Layer!  You can unit test your bean factory and xsl page seperately.  You can unit test without starting Tomcat!

Reading from the Classpath

Xsl files are read from the classpath, not the filesystem.  This means you put them in src/main/resources for Maven projects.  In Debug mode, you can modify the xsl files and hit refresh in the browser.  You can TDD the xsl files using JUnit.  You can split out different parts of your website into different jars (rcs repositories) so that different teams don't conflict with each other.

Android Support

All of this technology uses either simple 3rd party Java libraries or parts of the JRE.  Most of the 3rd party libraries advertise the fact that they work on Android.  While I am using a servlet engine, that servlet engine (TJWS) advertises the fact that it works on Android.  On top of that, there is nothing in the design that forces it to work with a servlet engine.  In my current implementation, the factory object gets an HttpServletRequest object passed in.  Switching that out with a generic "request" bean would completely decouple the system from a servlet engine.  Alternatively, you might be able to implement the HttpServletRequest interface in a way that wraps the native request object for your webserver.  Either way, you now have a UI technology that works on Android.

Ease of Development

Another feature I implemented that makes development easier is segragation of the pipeline.  You can request the outputs of steps 2 through 4 just by changing the url.  In the example above, we requested index.html.  This gives you the output of Step 4.  If you were to request index.xml, then you get the output of Step 2: the serialized XML of the Java Bean.  If you were to request index.phtml, then you get the Intermediate XML that contains a mix of HTML and Custom tags from Step 3.  A developer can hit Step 2 in the browser to see what the XML structure looks like so that they can write the XPath expressions correctly.  Tag developers can look at Step 3 to see the before and after of their Tag transfomations.

Internationalization

Step 4 also performs simple variable replacement out of a ResourceBundle.  This allows built in internationalization that follows Java's best practices.  Java and Android tend to use different internationalization systems, though.

Performance Considerations

One thing to keep in mind is the scalability of your system.  One thing that JSP has over this system is performance.  JSPs get translated into Java code which gets compiled to class files.  The tag libraries are implemented in Java, so they are pre-compiled into class files.  The class files are JIT-compiled into (hopefully) optimized assembly.  This means a JSP-based system will perform better than this system.  On the other hand, these performance implications are the same for Facelets, so if you are using Facelets instead of JSPs, you shouldn't see much of a performance impact due to the XSLT transformation.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.