Tutorial Requirement
Servlets spec version: 2.5 and above
Brief
Readers familiar with the JAX-RS specification will understand the problem with the Servlets URL mapping issues. For example, FileIt.in has the following URLs:
http://fileit.in/view/1/posts
And:
http://fileit.in/view/all/posts
The first URL displays all my posts, and the second everyones posts.
In JAX-RS, this can be easily mapped by parameterizing the dynamic part:
/view/{id}/posts
To design similar pattern matching in Servlets, it is bit more effort. For example, the only URL-match you can map to a servlet would be like:
<url-pattern>/view/*</url-pattern>
The part after /view/ (in our example /1/posts and /all/posts) can be fetched using the API call:
final String pathInfo = request.getPathInfo();
Finally, parsing the pathInfo string, you have to route the application flow to appropriate logic units.
O
/|\ ==> request ==> routing servlet ==> appropriate logic unit
/ \
Logic Units
Now, to map to each URL to logic units, I had the following options:
- Make private methods inside the routing servlet. Bad because the source code complexity of the routing class will increase.
- Make each of these logic units available in a separate POJO classes. Bad because I will be needing Servlet environment objects like Session and ServletContext in side these classes, and I might want to operate directly on response objects. This would mean a lot of parameter passing and return value checking. Also I would have to manage the lifecycle of the POJO objects.
So what is the option?
The option I choose was to write each of these logic units as separate Servlets. And make the routing servlet call the appropriate servlet.
Taking the example of our URLs:
http://fileit.in/view/1/posts
I wrote a corresponding Servlet:
public class ServletUserPosts extends HttpServlet{
...
}
And for the URL:
http://fileit.in/view/all/posts
this servlet:
public class ServletAllPosts extends HttpServlet{
...
}
These servlets were mapped in the web.xml thus:
<servlet>
<servlet-name>ServletUserPosts</servlet-name>
<servlet-class>package.ServletUserPosts</servlet-class>
</servlet>
<servlet>
<servlet-name>ServletAllPosts</servlet-name>
<servlet-class>package.ServletAllPosts</servlet-class>
</servlet>
Note: There is no <servlet-mapping> for either of the definitions. Both these servlets are not accessible by URL.
The routing servlet code:
public class ServletViewRouter extends HttpServlet{
public void doGet(...) throws ...{
final String pathInfo = req.getPathInfo();
...
Pattern USER_POST_URL = Pattern.compile("/([0-9]+)/posts");
Pattern ALL_POST_URL = Pattern.compile("/all/posts");
Matcher m = null;
// User Url logic:
m = USER_POST_URL.matcher(pathInfo);
if(m.matches()){
final long userId = Long.parseLong(m.group(1));
requset.setAttribute("userId", userId);
// Forward the request:
getServletContext()
.getNamedDispatcher("ServletUserPosts")
.forward(request, response);
return;
}
// All Url logic:
m = ALL_POST_URL.matcher("ServletAllPosts");
if(m.matches()){
getServletContext()
.getNamedDispatcher("ServletAllPosts")
.forward(request, response);
return;
}
}
}
Explanation
The magic of forwarding the request to the appropriate servlet is done:
getServletContext()
.getNamedDispatcher("ServletUserPosts")
.forward(request, response);
And:
getServletContext()
.getNamedDispatcher("ServletAllPosts")
.forward(request, response);
Using the getNamedDispatcher() (parameter of which is the <servlet-name> defined in the web.xml) method of the ServletContext, we are able to get the container managed instance of the Servlet wrapped as a RequestDispatcher object to which we can forward() or include() the request.
For passing the parameters from the routing servlet to the logic servlets, we use the request.setAttribute() (in the first example we are passing the userId) (which can be subsequently be taken from the logic servlets using request.getAttribute() method).
|