I am facing a task to add a dependent jar application to an existing one. The existing one does not use to much of locale benefits and so on, but my new one should.
So like I have now: localhost:8080/old-app
I want to have also: localhost:8080/[en|fr|...]/new-module
Could anyone point me the direction, because even if I think I get the idea of filters, filter-mapping, I cannot manage to solve it.
I would like to keep the old one and also have access to the new one.
Deploy new-module as ROOT.war (or set path in /META-INF/context.xml to /). Use Tuckey's URLRewriteFilter to rewrite specific URL's and transform the language part to a request parameter so that it's available by request.getParameter(). It's much similar to Apache HTTPD's mod_rewrite.
An alternative to URLRewriteFilter is to homegrow a custom filter which does like the following in doFilter() method.
String uri = request.getRequestURI();
if (uri.matches("^/\\w{2}(/.*)?$")) {
request.setAttribute("language", uri.substring(1, 3));
request.getRequestDispatcher(uri.substring(3)).forward(request, response);
} else {
chain.doFilter(request, response);
}
Map this on an url-pattern of /*. The language will be available by request.getAttribute("language") on the forwarded resource.
If you dont want the applications name as context root e.g. localhost:8080/appname but under / directly you have to put it into the tomcat/webapps/ROOT folder. To get more sophisticated URL mappings working have a look at http://ocpsoft.com/prettyfaces/
Related
I am running a Jetty web server and I have a generic servlet filter to handle exceptions. As part of this, I want to log aggregated metrics about which servlets are causing the exceptions. Is there a way I can figure out which servlets defined in the web.xml match the servlet path of the current request?
I have a generic servlet filter to handle exceptions
I take you to mean that your filter extends javax.servlet.GenericFilter directly.
Is there a way I can figure out which servlets defined in the web.xml match the servlet path of the current request?
In any given servlet context there should not be more than one servlet mapped to a given servlet path, so in any given case you're looking for just one servlet, not multiple.
As far as I know or can tell, GenericFilter and the generic ServletRequests and ServletResponses with which it works do not provide a good way to access the information you are after. If you cast the request to HttpServletRequest, however, then you can obtain its HttpServletMapping (since Servlet 4.0 / Java EE 8)
, which contains the servlet name as declared in the webapp's deployment descriptor, among other information. Or the HttpServletRequest can directly provide the servlet path, too.
But if you are contemplating casting the requests, then you would probably be better off having your filter extend javax.servlet.http.HttpFilter, which would give you type-safe access. This is itself a subclass of GenericFilter, so it probably wouldn't be difficult to perform such a refactoring.
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.
I have seen this answer
but it does not help my case.
I have a class that implements an HttpServlet. Now I want to place a URL inside it so that it has the following pattern: resource/identifier/resource.
For example, I want to make this REST call: http://example.com/owners/1234/dogs
I tried to place a URL like this in the servlet: http://example.com/owners/*/dogs, but the call never reached the servlet and was not handled.
If I understood well you want your servlet to be mapped to something like /owners/*/dogs.
Well, unfortunately Servlets can only use wildcards at the beginning or end of the mapping. So you would have to map it to /owners/* and then using request.getPathInfo() parse the rest of the url to extract the path info.
Your best options are to use the standard JAXRS or Spring MVC, both of which support path variables.
I am using the ServletTester class provided by Jetty to test one of my servlets.
The servlet reads the the body of the request using InputStream.read() to construct a byte[] which is the decoded and acted on by the servlet.
The ServletTest class provides a method getResponses(ByteArrayBuffer) but I'm unsure how to create one of these in the correct format since it would also need to contain things like headers (e.g. "Content-Type: application/octet-stream).
Can anyone show me an easy way to construct this, preferably using an existing library so that I can use it in a similar way to the HttpTester class.
If there is a "better" way to test servlets (ideally using a local connector rather than via the tcp stack) I'd like to hear that also.
Many thanks,
Why use a mock at all? Why not test the servlet by running it in jetty?
Servlet servlet = new MyServlet();
String mapping = "/foo";
Server server = new Server(0);
Context servletContext = new Context(server, contextPath, Context.SESSIONS);
servletContext.addServlet(new ServletHolder(servlet), mapping);
server.start();
URL url = new URL("http", "localhost", server.getConnectors()[0].getLocalPort(), "/foo?bar");
//get the url...assert what you want
//finally server.stop();
Edit: Just wanting to reassure people that this is very fast. Its also a very reliable indicator of what your code will actually do, because it is in fact doing it.
Spring MVC provides a small set of "mock" classes for the various javax.servlet interfaces, such as HttpServletRequest, HttpSession, and so on. This makes it easy to unit test the likes of a servlet, you just inject mocks into the e.g. doGet() method.
Even if you don't use Spring itself on the server, you can still use the mock from the library, just for your tests.
You can use HttpClient to simplify testing somewhat. Take a look at the following article:
http://roberthanson.blogspot.com/2007/12/testing-servlets-with-junit.html
That in combination with servlet tester should give you what you want unit test wise.
I have some questions about dispatching a request in a servlet.
To sum it up, I deployed a website on a public server which forces me to have a url-pattern like /servlet/* for all my servlets (I heard that it was a default configuration anyway).
The problem is that while developping the application I didn't have such restrictions and therefore didn't built it to support such patterns....now, my application just doesn't work because of the urls. Let's say me servlet is declared and mapped like this :
<servlet>
<servlet-name>MainController</servlet-name>
<servlet-class>controller.controllers.MainController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MainController</servlet-name>
<url-pattern>/servlet/MainController</url-pattern>
</servlet-mapping>
The problem is that I use this code in my Servlet :
this.getServletContext()
.getRequestDispatcher(destination).forward(request, response);
The destination parameter is always a *.jsp at the root of my webapp juste like "/index.jsp", "home.jsp", etc.
When I was using my application on localhost my servlet had this url pattern :
<url-pattern>/MainController</url-pattern>
and everything was working fine because the request dispatcher was always searching the .jsp at the root of the webapp. But now with my new url-pattern, it tries to serch all my .jsp at servlet/* just like servlet/index.jsp and, of corse throw me a
HTTP Status 404 - /servlet/index.jsp
I perfectly understand why it's acting like that as, if I recall well, Servlets cannot extend outside their current context.
But my question is, am I doomed ? Isn't there a way to tell to the request dispatcher to go to the .jsp I'm asking without taking care of the "/servlet/*" pattern ?
I absolutely need the request's object because I work with it before forwarding it.
I do really don't know how to get through this, so I'm seeking some help here hoping that someone had already faced this situation or at least have a clearer vision of the situation than me.
Thank's for taking the time to read this and for helping me.
Best regards,
Sampawende.
So, destination doesn't start with / which makes its location to be dependent on the path of the calling servlet?
Fix it accordingly:
request.getRequestDispatcher("/" + destination).forward(request, response);
By the way, if you'd like to prevent direct access to JSP as well (enduser could change the URL to point to the JSP without calling the controller first), then consider placing the JSPs in /WEB-INF folder. Don't forget to change the RequestDispatcher path accordingly: "/WEB-INF/" + destination.