Currently I'm preparing for Java EE Web Component Developer exam.
In both exam study guide and Servlet API Java docs I found that method
ServletRequest.getRequestDispatcher()
returns
null if the servlet container cannot return a RequestDispatcher.
When I tried it with non-existent static file I actually got non-null value. And forward resulted in 404 sent to client. Same effect for non-existent servlet. I use Apache Tomcat 7.0.
Does it mean that this behaviour isn't defined and is left to implementer's choice? What do they (Java docs authors) mean by "servlet container cannot return a RequestDispatcher"? I tried to look for it in JSR spec but wasn't able to find answer.
This is what the Servlet 3.0 specification has to say about dispatcher behavior:
The getRequestDispatcher method takes a String argument describing a
path within the scope of the ServletContext. This path must be
relative to the root of the ServletContext and begin with a ‘/’, or
be empty. The method uses the path to look up a servlet, using the
servlet path matching rules in Chapter 12, “Mapping Requests to
Servlets”, wraps it with a RequestDispatcher object, and returns the
resulting object. If no servlet can be resolved based on the given
path, a RequestDispatcher is provided that returns the content for
that path.
Nowhere does it mentioning returning null if a matching servlet cannot be found for the dispatch path. Instead it states that the 'content' found at the dispatch path will be found, which I translate as meaning an implementation can simply allow the container to absolutely resolve the path (which in your scenario yields a 404).
As far as the Javadoc though, it seems documented with the broadest use case possible, aka, if the container cannot create a dispatcher for any reason then it will return a null object (as opposed to throwing an exception). This may be due to a technical problem in the implementation, or may be actually valid (e.g JAX-RS implementations can access some level of the Servlet infrastructure, but cannot use the servlet context or dispatcher).
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 am working on a Java Portlet (extending GenericPortlet), using JBoss 7.02 and LifeRay Portal 6.1.0 GA1. This is one of the bundles that can be downloaded from LifeRay's release archive.
During deployment, when the init() method is called, getRequestDispatcher() returns null. Below is the exact error message:
09:22:15,972 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/my-portlet-name]] (MSC service thread 1-15) Error during mapping: java.lang.NullPointerException
Below is a snippet from my init() method:
PortletConfig config = getPortletConfig();
PortletContext context = getPortletContext();
PortletRequestDispatcher normalView = context.getRequestDispatcher("/portlet.jsp");
As a temporary workaround, I have moved all getRequestDispatcher() calls to doView() where it executes without problem. I do not understand why getRequestDispatcher() can locate portlet.jsp when called during doView, but not when its called during init()
Am I missing a preceding call of some other method that would resolve this? Is this a known issue?
Thanks for any help.
Getting the request dispatcher in the doView is the only place I've seen it done. I would imagine that it returns null during init because there is no actual request to dispatch.
Typically the init method is used for time-expensive operations that you don't want to incur for each request. This might be something like reading data from a file, or creating a reusable SQL connection.
You should also keep in mind that you should keep any portlet state thread safe. Don't create class or object variables that can only be used for one request at a time. The portlet methods are not inhererently thread safe, so you need to make sure that whatever variables a request is interacting with won't be manipulated by another request that is executing concurrently.
I'm not familiar with Portlets, but the answer should be the same as for Servlets.
The init() method is called exactly once, when your application is initially deployed. There is no active request (no one is asking for anything) or response (no one is going to read what the output is). Therefore, it is very reasonable forgetRequestDispatcher() to return null. In doView(), when you're handling a request and response, it makes sense to ask another resource to generate part (or all) of the response.
To address your question directly, getRequestDispatcher() has no problem locating portlet.jsp from init(); it's the request that's missing. (Where do you expect to see the result of portlet.jsp, anyway?)
If you do want to print some output during initialization, you can try logging it to a file, if your application is set up for that. Or, you can display data on System.out, if you know where the container's console is. (I use this second option quite often with servlets.)
HI,
We are declaring the FacesServlet and its URL mapping in the Web.xml. From my understanding,
FacesServlet loaded only once at the server startup.
URL mapping is used only when first time JSP application accessed from the external context.
One of the new learner for JSF has asked me the questions, these two things are used only once by the application. Is it true? Also is there any other way by not including in the web.xml?
What I should answer?
Updated
For example, I am accessing the application using the URL http://localhost:8080/webapp/index.jsf. When we are accessing this URL, FacesServlet invoked and view is rendered. The following is my question:
In JSF, we never seen changing the URL in the address bar. In that case, how it is handling the new request with the same URL?
In faces-config.xml we are giving the navigation cases as follows:
to-view-id>failure.jsp /to-view-id>
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
FacesServlet loaded only once at the server startup.
Correct.
URL mapping is used only when first time JSP application accessed from the external context.
Incorrect. It's been tested on every incoming HttpServletRequest. How else should the container know which servlet to invoke?
Also is there any other way by not including in the web.xml?
If you're using a servletcontainer which supports Servlet 3.0, you can also do this by #WebServlet annotation. JSF 2.0, however, is designed to be backwards compatible with Servlet 2.5, so it doesn't ship with that annotation and you need to explicitly declare it in web.xml.
See also:
Servlets tag info page
Lifecycle of a JSP/Servlet webapplication
How servlets are initialized and used
Update as per the new series of questions (which should each belong in its own question, but ala)
In JSF, we never seen changing the URL in the address bar. In that case, how it is handling the new request with the same URL?
This happens only if under the covers a forward by RequestDispatcher#forward() takes place. In a forward, the servletcontainer basically reuses the same HTTP request/response for a view (JSP/XHTML page). It does not force/instruct the webbrowser to send a brand new request. On the other hand, the HttpServletResponse#sendRedirect() will instruct the client (the webbrowser) to fire a new GET request and thus the URL will change. You can force this in the JSF sice by adding <redirect/> to the <navigation-case>. Note that since this causes a new request, all request scoped beans of the initial request will be lost.
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
The FacesServlet knows its own url-pattern.
Yes. while loading your application container will load web.xml and will extract the data for
particular URL pattern to servlet . when request comes it checks from memory that for this pattern which servlet to invoke and then if servlet is already loaded it will take it from memory otherwise it will create an instance of servlet and it will invoke doGet() or doPost() depending on the request type.
and there is another way to delcare URL mapping as from JAVAEE-6 by annotation
something like
import javax.servlet.annotation.InitParam;
import javax.servlet.annotation.WebServlet;
#WebServlet(
name = "SimpleServlet",
urlPatterns = {"/login"},
initParams = {
#InitParam(name = "param1", value = "value1"),
#InitParam(name = "param2", value = "value2")}
)
public class SimpleServlet {
}
In faces-config.xml we are giving the navigation cases as follows:
<to-view-id>failure.jsp </to-view-id>
Why we need not give the view name as failure.jsf? We are just giving the *.jsp in the faces-config.xml. How it is handled internally?
it is view identifier not the URL FacesServlet will load that view upon invocation of that navigation case.
I am just getting started on web app development. I have an index.jsp that has just ONE line.
< jsp:forward page="landing.do?"/>
What does
the above line do?
page="landing.do?" actually refer to?
what does the question mark "?" next to "landing.do?" signify?
As Bozho rightly pointed out, a servlet called "action" is mapped to handle "*.do" in my web.xml (as shown below).
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Now
How do I find out what the servlet "action" corresponding to "landing.do" actually does?
Thanks.
The <jsp:forward> forwards a client request to the url declared on the page attribute.
I also need to mention that in your example, you should have a / as first character inside your page declaration, if you want to specify a relative URL, i.e.:
This, in effect, is translated as a redirection to (if localhost)
http://localhost:8080/MyAPP/landing.do? (yours would have been translated to http://localhost:8080/MyAPPLanding.do?)
The ? allows you to append application/x-www-form-urlencoded parameters into your declaration.
More info here.
To know what landing.do does, do the following:
Go to your struts-config.xml (found in WEB-INF folder in your project) file and find any action (<action>) that a path="/landing") attribute.
Once you find your action, there's an attribute called type (inside that action). The type is a the class name of the action class that Struts calls to execute the action. The class name is fully qualified name.
Open the java file of the class (if it exists) and depending on the action (Action, DispatchAction, LookupDispatchAction), you will have to find its mappings and see what method Struts invokes.
In your example, my assumption will be based that your landing.do is of type Action. Therefore, read what the execute() method does. All actions actually, is execute() by Struts. The other actions are just Template Method patterns that knows what method to call by some mapping.
you probably have a servlet mapped to handle *.do in your web.xml
the ? means nothing here - generally it marks the start of get parameters (like ?param=value)
forward changes the current page with the specified, without the client knowing the change has happened.
This line will forward user to another page of the site, in particular to landing.do
page="landing.do?" actually refer to some page of the site landing.do. I believe this page is written with Struts framework. (But can be other)
what does the question mark "?" next to "landing.do?" mean nothing in this case. Generally after "?" there should be a list of request parameters. In this cases there will just be no parameters.
Update:
You should find servlet class which is mapped to that servlet name. After that you will be able to try to understand what that servlet class does. Also, look at the Struts tutorials or specification to get understanding of Struts framework workflows.