Filter is used as controller in Struts2 - servlets

In struts2 why is a Filter is used as a controller instead of ActionServlet?
What is the advantage of using a Filter over ActionServlet?

As per Struts2 Budi Karnival struts2 book, There is one distinct advantage of using a filter over a servlet as a controller. With a filter you can conveniently choose to serve all the resources in your application, including static ones.
With a servlet, your controller only handles access to the dynamic part of the application. Note that the url-pattern element in the web.xml file in the previous application is
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>...</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
With such a setting, requests for static resources are not handled by the servlet controller, but by the container. You wouldn't want to handle static resources in your servlet controller because that would mean extra work.
A filter is different. A filter can opt to let through requests for static contents. To pass on a request, call the filterChain.doFilter method in the filter's doFilter method.
Consequently, employing a filter as the controller allows you to block all requests to the application, including request for static contents. You will then have the following setting in your deployment descriptor:
<filter>
<filter-name>filterDispatcher</filter-name>
<filter-class>...</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDispatcher</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Advantage of this filter : One thing for sure, you can easily protect your static files from curious eyes.
The following code will send an error message if a user tries to view a JavaScript file:
public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String uri = req.getRequestURI();
if (uri.indexOf("/css/") != -1 && req.getHeader("referer") == null) {
res.sendError(HttpServletResponse.SC_FORBIDDEN);
} else {
// handle this request
}
}
It will not protect your code from the most determined people, but users can no longer type in the URL of your static file to view it. By the same token, you can protect your images so that no one can link to them at your expense.
Another advantage :
The introduction of Interceptors in Struts2 framework.It not just reduce our coding effort,but helps us write any code which we would have used filters for coding and necessary change in the web.xml as opposed to Struts1.So now any code that fits better in Filter can now moved to interceptors( which is more controllable than filters), all configuration can be controlled in struts.xml file, no need to touch the web.xml file

We generally Use a Filter when we want to filter and/or modify requests based on specific conditions.
For S2 to work it needs to perform certain reprocessing and modification work in order for a successful execution of your request while on other hands we use Servlet when we want to control, preprocess and/or post-process requests.
For controlling request S2 use Servlet under the hood but being hidden away to make the overall application structure more clean and easy to use.
This is what we have for Filters in The Java EE 6 Tutorial.
A filter is an object that can transform the header and content (or both) of a request or response. Filters differ from web components in that filters usually do not themselves create a response. Instead, a filter provides functionality that can be “attached” to any kind of web resource. Consequently, a filter should not have any dependencies on a web resource for which it is acting as a filter; this way, it can be composed with more than one type of web resource.

Related

Disable access to a Servlet

I'm developing a WebApp using JavaEE and I use a servlet to test some stuff.
Currently, my WebApp is set as when I go to my local url localhost:8080/myApp/test , I can run my test Servlet.
My plan is to deploy my project to a Web and I want to disable the Servlet, but not delete it. I mean, if in the future I visit my remote server via URL www.myNewWeb.com/test , I would like it throws an error od do nothing.
How could I do that?
There are many possible options here:
Option 1
Remove the mapping (annotation #WebServlet or url mapping entry in web.xml). In this case, any attempt to call this servlet will end with an error generated by the JEE container of your choice. It will try to map the servlet to URL, will obviously fail and throw an exception
The obvious drawback of this method is that you need to change the deployment configuration and if you'll want to run the same artifact in another envrironment where this servlet should work you won't be able to do so.
Option 2
Create some kind of configuration, load this configuration along with your application.
In the doGet (just for the sake of example) method do something like this:
public void doGet(request, response) {
if(config.isTestServletEnabled()) { // this is where the data gets read from configuration that I've talked about before
// do your regular processing here
}
else {
// this will happen when the servlet should not be activated
// throw an exception, return HTTP error code of your choice, etc
}
}
This way doesn't have a drawback of the first method that I've explained above, however involves some code to be written.

Spring MVC handle same request parameter in common place for different URIs

I've spent a few days researching this, but haven't found a suitable answer for my situation. I have a Spring 3.1 MVC application. Currently, some vendors log into the application via a web client in which case the user information is stored in the session. I want to expose some services to other vendors via RESTFul web services, but have the vendor pass their vendor id as a part of the URI or via PARAMS. Is there a way to handle the vendor id in a single place that then forwards to the respective controller for request processing? Should the vendor id be a part of the URI or should the vendor id be passed in the request body? I've looked into Interceptors, but how would I do this with multiple URIs or for every controller for the RESTFul webservice? Any suggestion would be greatly appreciated
Having a custom header is the most clean option but parameters also work equally well.
In the interceptors preHandle method you could lookup the vendor by either a header or a parameter and attach it to the request by adding the object to it's attributes.
request.addAttribute("vendor", myVendorInstance);
From that point on the vendor can be retrieved from the request like:
Vendor vendor = (Vendor) request.getAttribute("vendor");
Interceptors can be mapped to any URL you like using a mapping e.g.
<mvc:interceptor>
<mvc:mapping path="/vendors/**" />
<bean class="my.package.VendorLookupInterceptor" />
</mvc:interceptor>
Another way of making the vendor object available to controllers is to inject it. For instance, say that controllers interested in the object should implement this interface.
public interface VendorAware {
public void setVendor(Vendor vendor);
}
Controllers implementing this interface could be handled by the interceptor and get the vendor injected.
if (handler instanceof HandlerMethod) {
Object bean = ((HandlerMethod) handler).getBean();
if (bean instanceof VendorAware) {
Vendor vendor = getVendor();
((VendorAware) bean).setVendor(vendor);
}
}
Obviously the problem with adding the vendor id to the URI is that it affects all your URL's, so cannot easily make the controller generic.
Another way is to have the vendor id passed as a header to the controllers. You could use the X-User header.
Then you can write some kind of handler to check for this header, possibilities:
spring interceptor
servlet filter
spring security
aspectj

Spring MVC gives HTTP 406 for application/octet-stream

I am using Spring Web MVC and have written a controller that shall return binary data to a plugin in a web-page that show 3D content. This plugin uses STK files for it's models, so I have set-up my webapp to produce this kind of contents for *.stk URLs
The springDispatcherServlet is setup to handle *.stk requests in my web.xml
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.stk</url-pattern>
</servlet-mapping>
My controller looks like this:
#Controller
#RequestMapping(value="/3dobject", produces="application/octet-stream")
public class Object3DController {
#Autowired
private DesignerService designerService;
#RequestMapping(value="/{article}.stk", method=RequestMethod.GET, headers="Accept: application/octet-stream")
public void getObject3DForArticle(HttpServletResponse response, #PathVariable String article) throws IOException {
response.setContentType("application/octet-stream");
Object3D object = designerService.get3DModelForArticleNumber(article, LanguageCode.NORWEGIAN);
byte[] content = object.getContent();
response.setContentLength(content.length);
IOUtils.write(content, response.getOutputStream());
}
}
When I try to access the data through a browser, I get the following error:
HTTP Status 406 - The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
I have deployed my app to a tomcat server on localhost.
Any ideas of what I must do to make the browser download the binary data?
PS! I changed from *.3do (3d object) to *.stk. With 3do it worked as expected, but the plugin fails with an error (which is hard to interpret), so I have to experiment in order to verify wether or not the URL extension matters...
The RequestMapping.produces() narrows the request and therefore is
only mapped if the Accept matches one of these media types
So you need to set the corresponding Accept header in the request from the plugin. This cannot be done by simply making an HTTP GET Request by typing in a URL into a browser. One plugin I find useful for Chrome is the Advanced REST Client. There are plenty of others that offer similar functionality. Alternatively try using cURL to make the request with the correct header.

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.

Difference between / and /* in servlet mapping url pattern

The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.

Resources