Clean and usable URLs in Stripes

Cool URIs don’t change wrote Tim Berners-Lee more than a decade ago, URLs should be clean, usable and persistent. However, the biggest percentage of URLs around the web still are not clean and usable and even less persistent. There are lots of reasons that URLs are not persistent and most of the times it is not in the hands of the developer or webmasters, under certain circumstances some domains may not continue to be alive or they may change hands, anyway there isn’t much a framework can do about that. But for the URLs to be clean and usable is totally in the hands of the developers and webmasters. Unfortunately most web frameworks makes things even worse by not providing the mechanism to handle URLs or by making it really difficult to change the default behavior.

Stripes is not one of those frameworks and it really makes it easy to create clean URLs and provides a very flexible mechanism for handling them.

The simplest way to do it is using the @UrlBinding annotation. Let’s assume we have the following action bean:


package com.project.action;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;

/**
 * @author omer haderi
 * @version 1.0
 */
public class SimpleMathActionBean implements ActionBean {
	private static final String view = "/WEB-INF/jsp/index.jsp";

	private ActionBeanContext ctx;

	private double number;

	public ActionBeanContext getContext() {
		return ctx;
	}

	public void setContext(ActionBeanContext ctx) {
		this.ctx = ctx;
	}

	@DefaultHandler
	public Resolution view() {
		return new ForwardResolution(view);
	}

	public Resolution sqrt() {
		number = Math.sqrt(getNumber());
		return new ForwardResolution(view);
	}

	public double getNumber() {
		return number;
	}

	public void setNumber(double number) {
		this.number = number;
	}
}

The URL for this action bean to calculate the square root (assuming the default behavior) will be:
http://www.domain.com/SimpleMath.action?sqrt=&number=10

To simplify this we just need to mark the bean with the following annotation:
@UrlBinding(“/public/simple_math”)
after this the URL will be:
http://www.domain.com/public/simple_math?sqrt=&number=10

Of course the point is to remove the question mark, the ampersand and the equals sign. In the previews URL the [sqrt] is an event of the action bean as you can see from the action bean class. There is a special notation to identify the event in the UrlBinding which is {$event}, so we need to modify the annotation as follows:
@UrlBinding(“/public/simple_math/{$event}”)
after this the URL will be:
http://www.domain.com/public/simple_math/sqrt=&number=10

Let’s move on and remove the rest of symbols. In our URL the [number] is a parameter and just like the events it can be embedded in the UrlBinding as follows:
@UrlBinding(“/public/simple_math/{$event}/{number}”) and it can even have a default value
@UrlBinding(“/public/simple_math/{$event}/{number=5}”)
after this the URL will be:
http://www.domain.com/public/simple_math/sqrt/10/

That’s it just one annotation and the URL is simple, more readable and makes more sense.

This is how will the action bean class look like after marking it with the annotation that we just constructed:


/**
 * @author omer haderi
 * @version 1.0
 */
@UrlBinding("/public/simple_math/{$event}/{number=5}")
public class SimpleMathActionBean implements ActionBean {
...
}

Here are the two URLs:
before: http://www.domain.com/public/simple_math?sqrt=&number=10
after   : http://www.domain.com/public/simple_math/sqrt/10/
as you can see the later is much better.

You may be wondering what’s that /public over there!?
That’s the url-mapping in the servlet-mapping of the DispatcherServlet in the web.xml which can be anything that makes sence for your project:

<servlet-mapping>
	<servlet-name>DispatcherServlet</servlet-name>
	<url-pattern>/public/*</url-pattern>
</servlet-mapping>

When using UrlBinding annotation only the URL of those action beans that are marked by the annotation will change, the rest of the actions will still have the default URL binding as explained in the “URL Binding in Stripes“.

To change the default behavior to all Action Beans we would need to create a subclass of NameBaseActionResolver and override its methods:


package com.project.extensions;
/**
 * @author omer haderi
 * @version 1.0
 */
public class CustomActionResolver extends NameBasedActionResolver {
        // this is the url-pattern from the DispatcherServlet servlet-mapping in the web.xml
	private static final String URL_PATTERN = "/public";

	@Override
	protected String getBindingSuffix() {
		return "";
	}

	@Override
	protected String getUrlBinding(String actionBeanName) {
		String result = super.getUrlBinding(actionBeanName);
		result = separeteCamelCaseWithUnderscore(result).toLowerCase();

		return URL_PATTERN + result;
	}

	private static String separeteCamelCaseWithUnderscore(String s) {
		String pattern = "(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])";
		return s.replaceAll(pattern, "_");
	}
}

You need to add the extension package in the filter parameters in the web.xml see “URL Binding in Stripes” for more information.

From now on all the action beans will have this as default URL binding.

Advertisements