Handler method mappings with Spring 3.2.6 - spring-mvc

I'm trying to learn web app development with Spring MVC framework and I'm not completely clear about how controller mappings work. It will be better if I show my config and code first and then explain what I don't understand.
Here's mapping in my web.xml:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
mapping in my controller class:
#Controller
#RequestMapping("/main/courses")
public class CourseController {
...
and mapping of the method in question in this controller:
#RequestMapping(value="", method = RequestMethod.GET)
public String getCourses(Model model) {
...
As you can see, value attribute is set to empty quotes and this is the only way this mapping works. If I change value attribute to "/", I get 404 error. I have other mappings in this controller that work fine, for example value="/add". The only problem is when I try to map to the root of this controller.
Is value="" a valid value? Why value="/" doesn't work?

The #RequestMapping used at the class level sets the base path mapping that all the handlers methods will resolved against.
In your example, that's
#RequestMapping("/main/courses")
When you specify another #RequestMapping on a method like
#RequestMapping("/add")
that is added as a path element. So the request would need to be to
/main/courses/add
If you have another like so
#RequestMapping("/")
then you add a new path element but the element itself is empty. So the request needs to be to
/main/courses/
With a mapping like
#RequestMapping("")
there is no new path element, it's just the empty String. So the request would need to be to
/main/courses

Related

web.xml localhost servlet mapping [duplicate]

I have manually configured web.xml for my application. Now, I'm facing issues while running my application. I'm trying to access my servlet from my jsp page. But, it is throwing error as page not found.
The servlets are placed under below folder location
<application folder>/WEB-INF/classes/<package>
So, what should be the entries for servlets in url-pattern and servlet-mapping. So that, servlet can be accessible through URL.
url-pattern is used in web.xml to map your servlet to specific URL. Please see below xml code, similar code you may find in your web.xml configuration file.
<servlet>
<servlet-name>AddPhotoServlet</servlet-name> //servlet name
<servlet-class>upload.AddPhotoServlet</servlet-class> //servlet class
</servlet>
<servlet-mapping>
<servlet-name>AddPhotoServlet</servlet-name> //servlet name
<url-pattern>/AddPhotoServlet</url-pattern> //how it should appear
</servlet-mapping>
If you change url-pattern of AddPhotoServlet from /AddPhotoServlet to /MyUrl. Then, AddPhotoServlet servlet can be accessible by using /MyUrl. Good for the security reason, where you want to hide your actual page URL.
Java Servlet url-pattern Specification:
A string beginning with a '/' character and ending with a '/*'
suffix is used for path mapping.
A string beginning with a '*.'
prefix is used as an extension mapping.
A string containing only the '/' character indicates the "default" servlet of the application. In this case the servlet path
is the request URI minus the context path and the path info is
null.
All other strings are used for exact matches only.
Reference : Java Servlet Specification
You may also read this Basics of Java Servlet
Servlet-mapping has two child tags, url-pattern and servlet-name. url-pattern specifies the type of urls for which, the servlet given in servlet-name should be called. Be aware that, the container will use case-sensitive for string comparisons for servlet matching.
First specification of url-pattern a web.xml file for the server context on the servlet container at server .com matches the pattern in <url-pattern>/status/*</url-pattern> as follows:
http://server.com/server/status/synopsis = Matches
http://server.com/server/status/complete?date=today = Matches
http://server.com/server/status = Matches
http://server.com/server/server1/status = Does not match
Second specification of url-pattern A context located at the path /examples on the Agent at example.com matches the pattern in <url-pattern>*.map</url-pattern> as follows:
http://server.com/server/US/Oregon/Portland.map = Matches
http://server.com/server/US/server/Seattle.map = Matches
http://server.com/server/Paris.France.map = Matches
http://server.com/server/US/Oregon/Portland.MAP = Does not match, the extension is uppercase
http://example.com/examples/interface/description/mail.mapi =Does not match, the extension is mapi rather than map`
Third specification of url-mapping,A mapping that contains the pattern <url-pattern>/</url-pattern> matches a request if no other pattern matches. This is the default mapping. The servlet mapped to this pattern is called the default servlet.
The default mapping is often directed to the first page of an application. Explicitly providing a default mapping also ensures that malformed URL requests into the application return are handled by the application rather than returning an error.
The servlet-mapping element below maps the server servlet instance to the default mapping.
<servlet-mapping>
<servlet-name>server</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
For the context that contains this element, any request that is not handled by another mapping is forwarded to the server servlet.
And Most importantly we should Know about Rule for URL path mapping
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory at a time, using the ’/’ character as a path separator. The longest match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles requests for the extension. An extension is defined as the part of the last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the resource requested. If a “default” servlet is defined for the application, it will be used.
Reference URL Pattern

Filter Liferay HttpRequest

I would like to filter (modify) HttpRequest, which is coming to target Liferay Servlet, intercept it and add some attributes. (for example User) and then redirect (forward) the request to the target servlet. I create new servlet that should work like interceptor, add the attribute (request.setAttribute("user", User)) and then either dispatcher.forward or response.redirect... This is only for testing purposes to be able to call some liferay functionality from tests.
The problem is that Liferay is filtering the request and add there its 7 attributes. I tried also create SimpleFilter, but its not called when target servlet is called, only when I create my own Servlet which extends HttpServlet.
So the question is, is it possible to tell liferay not to filter the requests or more important not to remove the attributes from the request? I have debugged the Liferay source code, catch the Filter Chain and on the 1. filter I see there original request, and at the end the requests contains those Liferay attributes.
Many thanks.
The target servlet is defined like this:
<servlet>
<servlet-name>targetServlet</servlet-name>
<servlet-class>com.liferay.portal.kernel.servlet.PortalDelegateServlet</servlet-class>
<init-param>
<param-name>servlet-class</param-name>
<param-value>org.springframework.web.context.support.HttpRequestHandlerServlet</param-value>
</init-param>
<init-param>
<param-name>sub-context</param-name>
<param-value>targetServlet</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

How to map servlet to /*, it fails with infinite loop and eventually StackOverflowError

I would like to map my servlet to /*, but it failed with an infinite loop.
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>my.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
The java code is:
public class HelloServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response){
request.getRequestDispatcher("/WEB-INF/jsps/hello.jsp").forward(request, response);
}
}
If I map to /hello, everything works fine.
As the HelloServlet is mapped to /*, it will also be invoked on RequestDispatcher#forward() and cause the infinite loop.
How is this caused and how can I solve it?
This is not possible. The JSP should actually invoke container's builtin JspServlet. But the /* mapping definied by the webapp has a higher precedence.
You need to map the servlet on a more specific URL pattern like /pages/* and create a servlet filter which forwards non-static requests to that servlet. Yes, non-static requests (image/CSS/JS files) are also covered by /*, but they should not be processed by the servlet at all.
Assuming that you've all static resources in /resources folder, the following should do:
<filter>
<filter-name>filter</filter-name>
<filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>/pages/*</url-pattern>
</servlet-mapping>
With the following in filter's doFilter():
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());
if (path.startsWith("/resources")) {
chain.doFilter(request, response); // Goes to container's own default servlet.
} else {
request.getRequestDispatcher("/pages" + uri).forward(request, response); // Goes to controller servlet.
}
This takes place fully transparently without any change to /pages in the URL. The forward to the JSP will not trigger the filter or the servlet. Filters do by default not kick in on forwards and the JSP forward path does not match the controller servlet's URL pattern any more.
Alternatively, if you have your own default servlet implementation, then you could map the servlet to / and let it delegate to the default servlet if the request isn't applicable as a front controller request. This is what Spring MVC is doing under the covers. Creating a default servlet is however not a trivial task as it should be capable of responding to conditional requests, caching requests, streaming requests, resume requests, directory listing requests, etecetera.
See also:
Difference between / and /* in servlet mapping url pattern
How to access static resources when mapping a global front controller servlet on /*
Design Patterns web based applications
This is possible duplicate to Servlet Filter going in infinite loop when FORWARD used in mapping in JSF
Otherwise should check what your JSP contains, it could be that it makes request for css or image files that can result in this behaviour. Also would recommend to try without a wildcard.

Spring MVC RequestMapping confusion

I'm having trouble with Spring MVC RequestMapping and I'm hoping someone can give me advice on how to proceed.
I'm working on a server that will be accessed by a legacy client. The client sends simple requests (see below) but cannot be changed in any way to accommodate the new server.
The client sends gets or posts of the form /dbintf?command=GETINFO,acctNum=111
The server is called dbintf and it returns a simple text response.
I would like to create a Spring controller for each command and annotate a single controller method with
a params mapping that is based on the command parameter. So the mapping in the GETINFOController would be:
#RequestMapping(params="command=GETINFO")
public String serviceRequest(....
However, I cannot get this to work. Here are some details of the service.
The servlet mapping in web.xml:
<servlet-mapping>
<servlet-name>dbintf</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
The annotation scanning directives in intf-servlet.xml
<context:component-scan base-package="com.intf.controller" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
I have been trying various combinations of things to try to get this scheme to work and I'll boil this
trial-and-error work down to two examples:
This does not work. The response is: "The requested resource () is not available."
#RequestMapping( params="command=GETINFO")
public class TESTController {
#RequestMapping( method = RequestMethod.GET)
public String serviceRequest
If I change the url to add another level: /dbintf/test?command=GETINFO,acctNum=111
it works if I add a URL pattern to the handler method annotation.
#RequestMapping( params="command=GETINFO")
public class TESTController {
#RequestMapping( value="/test",method = RequestMethod.GET)
public String serviceRequest
This makes me think that params mapping alone will not work. It appears that a URL
pattern must be part of any mapping scheme. For my case however, there is no
URL pattern available since the the client request URL is fixed as: dbintf?.....
My question: is it possible to use parameter based mapping alone and not
in conjunction with a pattern? If so, what am I missing?
Thanks in advance for any help or advice,
beeky
I don't see in your config where you are mapping your controllers to "/dbintf"? You've got your DispatcherServlet (assuming that "dbintf" servlet is your DispatcherServlet) mapped to just "/", not "/dbintf". Are you deploying your webapp so that it's available at "/dbintf" relative to your container's "/"? I guess what I'm trying to get at is that you may just need to add some config to your #RequestMapping's:
#RequestMapping(value="/dbintf", params="command=GETINFO")

How to use Servlet in Struts2

How to use servlets together with Struts2?
I assume you want to know how to use a servlet in conjunction with Struts2 when you have mapped everything to the Struts2 filter.
You can use the following in your struts.xml:
<constant name="struts.action.excludePattern" value="/YourServlet"/>
You can exclude multiple patterns by separating them with a comma, such as:
<constant name="struts.action.excludePattern" value="/YourServlet,/YourOtherServlet"/>
More Information
Filter mapping for everthing to Struts2 besides one servlet?
Filters not working in Struts2
There are three ways to resolve this problem:
add constant tag in struts.xml
<constant name="struts.action.excludePattern" value="/YourServlet,/YourOtherServlet"/>
add suffix in servlet configuration in web.xml
<servlet-mapping>
<servlet-name>Authcode</servlet-name>
<url-pattern>/authcode.servlet</url-pattern>
</servlet-mapping>
Because in struts 2, it will only intercept all the request end with .action, if this request do not have any suffix, it will automatically add it. When we make our servlet url-pattern have a suffix, then struts 2 will not intercept it anymore.
implement a user-defined filter
Servlets technology is more low level architectural layer than Struts2. Even more Struts2 is embedded to your project as a filter (that is part of servlet technology).
So to add one more servlet just add to web.xml registration:
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>class.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
If you need multi mapping servlet you can using:
<constant name="struts.action.excludePattern" value="/Servletname1, /Servletname2" />
But in struts, you should not using servlet url because it not unity. You can use ajax:
$.ajax({
url : "nameAction.action?param="+id,
type : "post",
data : {
'id' : id
},
success : function(data) {
// $('#result').html(data);
},
error : function(jqXHR, textStatus, errorThrown) {
$('#result').html("Error");
}
});

Resources