A Java library for enabling your webapps to have clean, REST like urls. Rather than being a swiss army knife tool like others, it is optimized for this single role, and as such it is (hopefully) easier to use, less verbose and packed with convenience features (such as binding matched url parts directly to javabean properties).
Download the jar and add it to your application
library directory.
Create a subclass of
com.codegremlins.jurlmap.DispatchFilter
and override the configure method to add
your configuration.
Add a mapping of your filter to your
web.xml file.
<filter>
<filter-name>DispatchFilter</filter-name>
<filter-class>com.myapp.MyDispatchFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DispatchFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Lets start with some examples:
protected void configure() {
// Match given pattern, in which $ mean a string, bind to parameter
// `Username` and forward to profile.jsp
// Parameter will be accessible via request.getParameter()
forward("/profile.jsp", "/profile/$Username");
// Match given pattern in which % means integer and * mean until end of pattern,
// bind integer to parameter ArticleId and forward to article.jsp
forward("/article.jsp", "/article/%ArticleId/*");
// match pattern, bind parameter and redirect to `/servlets/profileservlet?Username=...`
redirect("/servlets/profileservlet", "/member/$Username");
// Same as above only send 301 permanent redirect
relocate("/servlets/profileservlet", "/member/$Username");
// Shows how to add http methods to patterns. First one will be run when pattern matches
// AND method is POST (and only POST). Second will run when method is GET.
// Default is GET and POST.
// Both patterns are the same (besides the http method that is).
forward("/edit_person.jsp", "/member/$Username;POST");
forward("/person.jsp", "/member/$Username");
// Shows use of deploy rule
deploy(LogoutPage.class, "/logout");
// Deploy is also possible without passing a pattern parameter, if LoginPage class is
// annotated with a @Deploy(path) annotation
deploy(LoginPage.class);
// Shows that multiple patterns are possible in single rule call.
// Also demonstrates `enum` pattern element where one of several given values is matched
deploy(ArticlesHomePage.class, "/", "/sort/[newest|oldest|hottest]SortOrder");
}
The above examples show 90% of what you need to know to effectively use jurlmap.
Rather than having external configuration files jurlmap is configured in plain old Java.
As can be seen from the example (the one way way
above...) there are currently four possible actions:
forward, redirect,
relocate and deploy. Each
takes as parameters a target, and one (or in the case
of deploy zero) or multiple patterns. On
invocation if the pattern is matched the action is
executed...obviously.
What do these action types mean exactly:
forward When matched it will make
parameters accesible via request.getParameter and
will forward to target
redirect When matched it will
append parameters as query string and will send
redirect to target
relocate It is similar to
redirect but sends 301 permanent
redirect.
deploy When matched it will create
a new instance of target class, will
bind parameters to new instance via setters or raw
fields (whichever it finds) and finally invoke the
service method.
The first two action types are useful when you are
working with an existing application, or an application
written in an existing framework for which you want to
enable clean URLs. The deploy action is
more interesting if you are developing a new
application and you are looking for an alternative to
the limited servlet mapping facilities of web.xml.
The available pattern syntax is very simple:
A pattern is separated between a ;
character. Anything before ; is the path
pattern. Anything after is the http methods such as
GET, POST, PUT, DELETE. You can specify multiple http
methods separated by | such as
GET|PUT.
Any given path is split at each /
character and each subelement can be one of the
following:
$ Matches any string.
% Matches a long/integer.
[] Matches one of several options
separated by | in the like:
[options1|option2|option3]
* Matches everything until the end
of the pattern, including subsequent
/.
Any other text is matched as is. it behaves same
as [text] with a single element, only in this case
it cannot be followed by ? or binding
options.
If a ? follows any of the above
elements (except the last one) then that element is
optional, that is the pattern matches whether the
element is present or not.
Any of the above elements can be followed by binding options that specify to which parameter to bind the matched result. example
/member/$Username will match a string
and bind it to parameter username. Parameters can also
be compound such as /member/%User.Id,
which will work as follows:
You can define subpatterns by using parenthesis. For
example /home(/room/$roomname) will expand
to both /home and
/home(/room/$roomname) patterns and will
match any results that resolves to either pattern.
For the forward and
redirect actions the matched parameter
will contain a dot and will be User.Id
exactly as give. For the deploy action,
the result will be more complicated, in that first the
object property User will be looked up, a
new object will be instantiated to the type of that
property, the property set to that object, and the
Id property or the new instance will be
set. Lets draw a picture to explain that:
Assuming the following:
class MyPage implements Page {
User user;
}
class User {
int id;
}
When binding User.Id this will
happen:
- result = new User()
- result.id = @@MatchedIntegerParameter@@
- myPage.user = result;
There is one case where the binding syntax is a
little different. It is possible to annotate methods of
a class with pattern annotations @Deploy
and then use them as targets in a pattern dispatch
like:
class MyClass {
private static final PathSet PATHS = new PathSet(MyClass.class);
public void doMe(String path) {
PATHS.dispatch(this, path);
}
@Deploy("/something/to/do")
public void runSomething() {
...
}
@Deploy("/person/$1/%2/%3.Id")
public void runSomething(String parameter1, int parameter2, User parameter3) {
...
}
}
In this case, as the examples show the parameter bindings are specified as numbers, corresponding to the method parameters as they appear in order.
One problem you will never have with jurlmap is the
issue of trailing /. In many frameworks
/path is different from
/path/ and sometimes you get one when you
want the other or you are confused about which is
which. With jurlmap all extra / characters
are ignored so /my/path is the same as
/my/path/ which is the same as
/my/////path//
Here is a list of the classes and interfaces that you can use as part of jurlmap:
public class DispatchFilter implements javax.servlet.Filter {
abstract protected void configure();
public final void forward(String target, String ...patterns);
public final void redirect(String target, String ...patterns);
public final void deploy(Class<?> clazz, String ...patterns);
public final void setDispatchServlet(String dispatchServlet);
public final void setDefaultHttpMethods(String methods)
}
public class DispatchServlet {
}
public interface Page {
public void service(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
}
abstract public class AbstractPage implements Page {
public void run() throws ServletException, IOException;
protected boolean dispatch();
protected boolean dispatch(String path);
protected HttpServletRequest getRequest();
protected HttpServletResponse getResponse();
protected ServletContext getServletContext();
protected void forward(String target) throws ServletException, IOException;
protected void redirect(String target) throws IOException;
protected boolean modified(long lastModified);
protected void serialize(Object object);
}
public class PathSet {
public PathSet(Class targetClass);
public Object dispatch(Object self, String path);
public Object dispatch(Object self, String path, HttpServletRequest request);
}
public @Retention(RetentionPolicy.RUNTIME)
@interface Deploy {
String[] value();
}
License is LGPL.
If you think there is something missing either from this manual or a feature that should be added to jurlmap let me know.