May 4th, 2013 by Daniel Sachse

Migrating from PrettyFaces to Rewrite – Simplicity meets power

Hi Java web-developers. I hope you are already familiar with [[PrettyFaces]]. If not, I will give you a very short introduction taken from the project documentation:

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
A very pretty one :
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.

In this post we are going to migrate a PrettyFaces project which is NOT annotation based. We just use the pretty-config.xml to map everything.

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:

WEB-INF/pretty-config.xml
<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:

/src/main/webapp/index.xhtml
<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:

/pom.xml
<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:

at.w0mb.prettyMigration.rewrite.AccessRewriteConfiguration
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:

WEB-INF/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.)

Exhibit 5
<dependency>
	<groupId>org.ocpsoft.rewrite</groupId>
	<artifactId>rewrite-integration-cdi</artifactId>
	<version>2.0.0.Final</version>
</dependency>
at.w0mb.prettyMigration.rewrite.LoginInterceptor
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:

META-INF/services/org.ocpsoft.rewrite.config.ConfigurationProvider
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.

I hope you liked my first post on this blog and if you have any comments/questions/improvements, please just post a comment here or contact me!

Posted in Java

9 Comments

  1. hh says:

    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 ?

    1. 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

      1. Alberto Gori says:

        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.

  2. the airspeed indicator says:

    These example navigation cases are very simple. What if the pages take parameters? Like this:

    
    
    1. 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

  3. maxqua72 says:

    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

  4. sence says:

    prettyfaces still resists in our projects, cos easy and simple to understand. Rewrite is to "hardcoded"

  5. Matthew says:

    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.

    1. 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

Reply to Matthew




Please note: In order to submit code or special characters, wrap it in

[code lang="xml"][/code]
(for your language) - or your tags will be eaten.

Please note: Comment moderation is enabled and may delay your comment from appearing. There is no need to resubmit your comment.