Migrating from PrettyFaces to Rewrite – Simplicity meets power
PrettyFaces is:
“The open-source /url/#{rewriting} solution for Servlet, JSF, and Java EE, including features such as: page-load actions, seamless integration with faces navigation, dynamic view-id assignment, managed parameter parsing, and configuration-free compatibility with other web frameworks.”
[[Rewrite]], on the other hand, is a URL-rewriting framework built for extendability, for use with any web-framework or pure Servlet itself, and is used for the core of PrettyFaces “4.0” – bringing the best of both worlds… so that sounds very cool thus far, but what do we want to achieve with it? Why would we use either of these frameworks?
Just compare these two URLs:
Very ugly one :
http://www.example.com/blog.html?author=w0mbat&post_id=23&year=2012
http://www.example.com/blog/w0mbat/2012/23
This is something that both PrettyFaces and Rewrite can accomplish for us, but what if, for example, we wanted to intercept all URLs and require a login? This is where PrettyFaces can no longer help us, but rewrite is ready to come to our aid!
Why migrate?
PrettyFaces itself is really great but the configuration is not runtime dynamic. There is a feature called “DynaView”, which can be used to determine the page to display for a given URL-mapping at runtime, but it is fairly limited and is difficult to use when things get hairy. To achieve some level of dynamism, one can implement what is called a “RewriteProcessor,” but it’s basically all manual coding; there are no dynamic rules that one would need to e.g. display a login page for every requested URL if the user is not logged in.
This is only one of many cool features that [[Rewrite]] offers in comparison to PrettyFaces.
Part 1 : Stock-taking
I created a small JEE 6 sample webapp with JBoss Forge which you can fork or clone from Github if you want. This application will show us several things:
- a small pom.xml with only a few dependencies
- an XHTML template to be DRY
- four pages: index, about, profile, login
- a WEB-INF/pretty-config.xml
We will start with our pretty-config.xml shown below:
<pretty-config xmlns="http://ocpsoft.com/prettyfaces/3.3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ocpsoft.com/prettyfaces/3.3.2 http://ocpsoft.com/xml/ns/prettyfaces/ocpsoft-pretty-faces-3.3.2.xsd"> <url-mapping id="home"> <pattern value="/" /> <view-id value="/index.xhtml" /> </url-mapping> <url-mapping id="about"> <pattern value="/About" /> <view-id value="/about.xhtml" /> </url-mapping> <url-mapping id="profile"> <pattern value="/Profile" /> <view-id value="/profile.xhtml" /> </url-mapping> </pretty-config>
This file just maps / to index.xhtml, /About to about.xhtml and /Profile to profile.xhtml. The pages just have links to each other, to provide some sort of interaction. Nothing too special 😉
Below you can see some content of the index.xhtml to get an idea of how the navigation with PrettyFaces works:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" template="/WEB-INF/templates/default.xhtml"> <ui:define name="links"> <h:link outcome="pretty:about" value="About"/> <h:outputText value=" " /> <h:link outcome="pretty:profile" value="Profile"/> </ui:define> </ui:composition>
All the other files(web.xml, faces-config.xml,..) are pretty straight forward and you can check them out via Github.
Part 2 : Migration
The easiest way to migrate to Rewrite, is simply to use the Rewrite PrettyFaces compatibility module. Remove any PrettyFaces dependencies that you may have in your project, then include the following dependencies. First we will have to change the pom to exclude PrettyFaces and include Rewrite:
<dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-servlet</artifactId> <version>2.0.0.Final</version> </dependency> <dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-config-prettyfaces</artifactId> <version>2.0.0.Final</version> </dependency>
That’s it! You’re done. You’d now using PrettyFaces with Rewrite core, but since you probably want to know a little bit more about how to use the power of [[Rewrite]], let’s continue and learn how to replace our PrettyFaces configuration with Rewrite completely. We’ll also learn a few tricks that Rewrite can help us with.
Migrating the configuration
First, delete your pretty-config.xml
, but keep it somewhere you can look at while you move the functionality over to Rewrite.
Now, we need to create several ConfigurationProvider classes. These classes have to extend org.ocpsoft.rewrite.servlet.config.HttpConfigurationProvider
(for servlet environments.) A ConfigurationProvider has a method which returns a priority and a method which returns the Configuration. Have a look at the AccessRewriteConfiguration
below:
package at.w0mb.prettyMigration.rewrite; import javax.servlet.ServletContext; import org.ocpsoft.rewrite.bind.El; import org.ocpsoft.rewrite.config.*; import org.ocpsoft.rewrite.servlet.config.*; public class AccessRewriteConfiguration extends HttpConfigurationProvider { @Override public Configuration getConfiguration(final ServletContext context) { return ConfigurationBuilder.begin() .addRule(Join.path("/").to("/index.xhtml")) .addRule(Join.path("/about").to("/about.xhtml")) .addRule(Join.path("/profile").to("/profile.xhtml")) .addRule(Join.path("/login").to("/login.xhtml")) // Authentication .defineRule() .when(Direction.isInbound().and(Path.matches("/logout"))) .perform(Invoke.binding(El.retrievalMethod("identity.logout")) .and(Redirect.temporary(context .getContextPath() + "/"))); } @Override public int priority() { return 10; } }
With this Configuration, we mainly implemented what we saw earlier in the pretty-config.xml
; but we have also defined a ‘virtual’ URL that doesn´t map to an *.xhtml
file, but rather, invokes a bean method that performs the logout and redirects to /
.
Question: What happens if we try to invoke the application now?!? No idea? Ok.
Answer: We will get error messages because JSF cannot find any mappings for our ourcomes e.g. pretty:about, pretty:profile, …
So what do we need to do now? We have to do the mapping ourselves in the faces-config.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_2_0.xsd" version="2.0"> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>pretty:home</from-outcome> <to-view-id>/index.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>pretty:about</from-outcome> <to-view-id>/about.xhtml</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>pretty:profile</from-outcome> <to-view-id>/profile.xhtml</to-view-id> </navigation-case> </navigation-rule> </faces-config>
The application would nearly work now as we have written it. But how about the login? We will define another ConfigurationProvider
as shown below.
First, though, we will need to add another dependency to our POM. This will enable us to @Inject
our Identity
object directly into the configuration. The rewrite-integration-cdi extension module, enriches our rewrite configuration objects with CDI support, and gives us access to the Unified Expression Language (not shown.)
<dependency> <groupId>org.ocpsoft.rewrite</groupId> <artifactId>rewrite-integration-cdi</artifactId> <version>2.0.0.Final</version> </dependency>
package at.w0mb.prettyMigration.rewrite; import javax.inject.Inject; import javax.servlet.ServletContext; import at.w0mb.prettyMigration.Identity; import org.ocpsoft.rewrite.config.*; import org.ocpsoft.rewrite.servlet.config.*; public class LoginInterceptor extends HttpConfigurationProvider { @Inject private Identity identity; @Override public Configuration getConfiguration(ServletContext arg0) { ConfigurationBuilder config = ConfigurationBuilder.begin(); if (!identity.isLoggedIn()) { return config .defineRule() .when(DispatchType.isRequest().and(Direction.isInbound()) .and(Resources.excluded())) .perform(Forward.to("/login")) .addRule(Join.path("/login").to("/login.xhtml")); } return config; } @Override public int priority() { return 0; } }
As a final step, we have to tell Rewrite which ConfigurationProvider
classes to use at runtime:
at.w0mb.prettyMigration.rewrite.LoginInterceptor at.w0mb.prettyMigration.rewrite.AccessRewriteConfiguration
Part 3 : Conclusion
We are now able to call our application under http://localhost:8080/prettyMigration and we will see our login page. This page gets displayed no matter which URL we will call. After clicking on ‘login’ we will be ‘logged in ‘ and redirected to the index page. Once we open http://localhost:8080/prettyMigration/logout, we are ‘logged out’ and we will again see the login page.
You can grab this project from Github. The project was build and tested on JBoss 7.1, in my opinion the fastest and most advanced application server on the market.
Posted in Java
Hi, don’t you think this is quite much work that writing so many classes and xmls instead of a single pretty-config.xml.
i used pretty before and find it very usefull to make urls beautiful and i think i should not be have to write so much code only for login / logout thing.
Will you make Rewrite more simple config in the near future ?
Well, I don´t think that this is too much configuration.
Basically it is just 1 class for the mapping that we have done in pretty-config.xml before, 1 class that does the login and some entries in the faces-config.xml to ensure, that our PrettyFaces outcomes are met. If you don´t need the outcomes you can omit them.
I think, Lincoln can tell you more details about the upcoming integration of Rewrite into PrettyFaces.
Thanks so far!
w0mbat
Sorry, I see a single very simple and easy to understand XML vs a more verbose XML + classes with really not easy to understand syntax and one more property to mantain.
These example navigation cases are very simple. What if the pages take parameters? Like this:
Hey Lars,
It looks like there was a problem with our website when you posted your comment, and your XML was lost. Could you please try again? Thanks. ~Lincoln
Hi,
what about the following use case:
1. client requires a protected page
2. rewrite redirects to the login page
3. user logs in
4. the user redirects to the protected page
How can rewrite manage the point #4?
Thanks
prettyfaces still resists in our projects, cos easy and simple to understand. Rewrite is to "hardcoded"
A bit frustrated about the lack of documentation for rewrite etc. Searches just tend to return source listings. I’m having trouble getting rewrite to work the way I want it to after having used prettyfaces relatively successfully for some time, but wanting to try out rewrite properly. This article, seemingly the most comprehensive, is out of date, there are API references that simply don’t exist any more. I’ve also never been able to get either prettyfaces or rewrite to function unless explicitly specifying /faces/… in all aspects of rules, which doesn’t make a great deal of sense to me, especially on the ‘pretty’ url. In addition, after having battled it around, I’ve gotten to a stage where JSessionId is being appended to every URL, in one of the most basic of scenarios, and this seems to be such a basic behaviour that I feel as though something fundamental is missing.
Hi Matthew, I’m really sorry. You’re right, we have a documentation problem. Unfortunately we’ve been unable to really spend as much time as we’d like to on the docs. We’d love any help! Can you tell us perhaps how you’d like to see the docs structured? Perhaps you could even help us? I know you need the docs, and I’m asking you to help, so I see the irony there, but it would be hero epic.
~Lincoln