RequestDispatcher.forward loop - java

I am using
<url-pattern>/*</url-pattern>
to map all the requests to one sevlet,where i do all the authentication work.
but I want to skip some static content (like css files).so I tried fowrding them from
that sevlet to where the resource file is
if(isResourceFile){
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("static/" + fileName);
dispatcher.forward(request, response);
}
but this will start a infinite loop because this will again call the same sevlet
is there any way to work around this without mapping all the resource(css) files in web.xml?

The url-pattern of /* is better to be used exclusively by Filters, not Servlets. Put all your static files in a certain folder (maybe covered by the url-pattern of a more effective FileServlet?) and let the Filter check it.
E.g. (pseudo)
public void doFilter(request, response, chain) {
if (requestURI.startsWith("/static")) {
chain.doFilter(request, response); // Just continue. Do nothing.
} else {
request.getRequestDispatcher("/pages").forward(request, response); // Forward to page controller.
}
}
Hope this helps.

Assuming that you're looking to authenticate just JSP files, you could change the URL:
/*.jsp
I think it's a better idea to handle your authentication using a filter rather than a servlet. And in a production environment, you should use a front-end webserver for static content.

In case you cannot use a filter and you have to use a wildcard '*' in the mapping without a extension like '.jsp' then then this could work for you:
if(isResourceFile){
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/static/" + fileName);
dispatcher.forward(request, response);
return;
}
Adding the '/' in the beginning of the static directory will give it root context.

Related

Java Web - Canonical Url unlimited redirection

At the moment I am trying to make my own canonical links in Java web.
I got this in my web.xml
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ControllerServlet will call a factory that will use the getPathInfo() to call the correct class to handle this. The method that will handle the requests looks like
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
try {
RequestDispatcher view = request.getRequestDispatcher("../Overview.jsp");
view.forward(request, response);
} catch (Exception ex) {
System.out.println("error");
}
}
Now when I want to visit my page I will surf to
http://localhost/firstfolder/overview
The pathinfo I get is /firstfolder/overview, this redirects correctly to /Overview.jsp but after that it keeps redirecting to /Overview.jsp and ultimately crash. Leaving the end user with an empty page. Any idea how I can stop this unlimited looping and just serve the page?

The url works in localhost but not in server

I use getServletContext().getRequestDispatcher("/message.jsp").forward(request, response); to forward from servlet to jsp, but this does not work in server. I used response.sendRedirect(request.getContextPath() + "/message.jsp"); but then jsp does not show the message which I send from servlet. How to solve it?
If you do a sendRedirect instead of a forward, you will create a new request, which will cause your request attributes to be gone. Some frameworks use a 'flash' scope (essentially 2 times a request) instead of the request or session scope to handle this. In your case, however, there is no flash scope since you are using plain Servlets/JSP.
Instead, you can do something like:
ServletContext context = getServletContext().getContext(request.getContextPath());
RequestDispatcher dispatcher = context.getRequestDispatcher("/message.jsp");
dispatcher.forward(request, response);
Which should forward the current request to the message.jsp.
you can do by this way
request.setAttribute("PARAM1", "VALUE1");
RequestDispatcher dispatcher = request.getRequestDispatcher("message.jsp");
dispatcher.forward(request, response);
and in your Message.jsp retrieve your set value.
request.getAttribute("PARAM1");

How to use a URL like a parameter

I want a simple servlet that can be used without parameters. Something like :
http://servername:8080/do/view/username/address
and use it like a parameter :
http://servername:8080/do?action=view&login=username&page=address
Both urls will have the same behaviour. I prefer don't use any framework, only servlets and filters.
How can I obtain the url name from the servlet? What's the best solution?
Response:
Based on the reply made by #BalusC i have created the following servlet that do all i want:
#WebServlet("/do/*")
public class ActionTestCasesServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
String pathInfo = request.getPathInfo();
String[] parts = pathInfo.substring(1).split("/");
RequestDispatcher destination = getServletContext()
.getRequestDispatcher("/" + parts[0] + ".jsp");
if (parts.length > 1) {
request.setAttribute("username", parts[1]);
}
if (parts.length > 2) {
request.setAttribute("page", parts[2]);
}
destination.forward(request, response);
}
}
This code call the "view.jsp" passing the attributes "username" and "page".
Just map the servlet on /do/* instead of /do.
#WebServlet("/do/*")
This way you can obtain the path information using HttpServletRequest#getPathInfo().
String pathInfo = request.getPathInfo(); // /view/username/address
You can use the usual String methods like String#split() to split it in fragments.
String[] parts = pathInfo.substring(1).split("/"); // view, username, address
See also:
Design Patterns web based applications - for the case you intend to homegrow MVC (note: I don't recommend this, it's fun if it's for learning/hobby purposes, but if it's for real job, rather pick an existing MVC framework so that you can ensure that every pitfall is covered)
You say you'd prefer not to use "any framework, only servlets and filters", but have you considered the tuckey.org UrlRewriteFilter? This is a single filter that you can register in web.xml and then declare rules such as
<rule>
<from>/do/(.+)/(.+)/(.+)</from>
<to>/do?action=$1&login=$2&page=$3</to>
</rule>
in an XML file. Then you just write your servlets to expect the query parameters as normal and let the filter deal with the "pretty" URLs.
Spring MVC do it very very good. But if you do not want to use third-party frameworks all you can is handle request.getRequestURI(), split this string and do what you want. For example you can use pattern /entity/action.
I think previous threads reply are the perfect solution. I played with web.xml file to see what I can do for and here is my results. I was able to change the web.xml to get a url similar to what you want "http://localhost:8080/do/myServlet.do". Here is web.xml file content which pertains to the servlet-name and url-pattern.
<servlet>
<servlet-name>do</servlet-name>
<servlet-class>ControlServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>do</servlet-name>
<url-pattern>/myServlet.do</url-pattern>
</servlet-mapping>
</web-app>

Handling URLs in a Java Serlvet

I am fairly new to servlets and the way I am handling repsonses from the client is using if uri matches a certain path such as:
public class EntryServlet extends HttpServlet
{
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws IOException
{
String uri = request.getRequestURI ();
if (uri.matches ("/1")) {
do something....
} else if (uri.matches ("/2")) {
do something else.....
} else {
throw error message...
}
}
}
This works only if my web service have a few pages. But say i have 50 xml documents and I want to map exmaple.com/1 to a xml ...to example.com/50 to another xml. Now it would be bad design to have 50 if else statements to handle each of the resource. What is a better way to implement this? I am trying to do this purely in java with no third party apps/plugins/frameworks
Update:
I am working with only one servlet with a background thread. Essentially a client request to process a file and the background thread handles the processing and stores xml docs in a db. And I want have each of the xml docs to be a resource on the web service.
You gave the answer "I want to map", so lets use a Map
you have 2 options (that i see now)
1: you put the "/1" as a key and put a String as value that map's to your xyz.xml
2: you put the "/1" as a key and put an object that implements an "Executor" interface...
in both cases you lookup the value for the key and then use the value object to return something useful.
Put this in the init of your Servlet and fill it with your objects:
Map<String,String> myActions = new HashMap<String,String>();
myActions.put("/1","one.xml");
.....
Now you can do this:
String uri = request.getRequestURI ();
if (myActions.containsKey(uri)) {
String value = myActions.get(uri);
do something with value
} else {
throw error message...
}
you can easily change this to option 2 by using object that implement this:
public interface Action {
String execute(HttpServletRequest req, HttpServletResponse res) throws ServletException,
IOException;
}
The answer may depend on what you want to do with the information in the URI. When writing a REST style application, the URI is one of the primary ways that information is passed to your application. So if you may want to re-consider your aversion to additional packages since the REST APIs are very popular for good reason.
Sticking with plain Servlets, lets say we have 50 xml documents that we want to serve based on which id number is passed through the URL.
We could store a table in a database that indicates the correspondence between id number and document. Or we could store this data into a CSV file. Either way, for such a relatively small table, I would just load the whole table into memory when the Servlet starts up. You can place code in an init() method for loading data into your application.
Then we have something like this:
static HashMap<Integer, String> fileMap;
public void init() {
fileMap = new HashMap<> ();
fileMap.put(1,"thing1.xml");
fileMap.put(2,"thing2.xml");
fileMap.put(3,"Otherthingy.xml");
}
Now you can write much cleaner code in your doGet method.
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws IOException {
String uri = request.getRequestURI();
int slash = uri.indexOf("/");
if(slash+1 == uri.length())
//throw an error
String idString = request.getRequestURI().substring(slash+1);
int id = Integer.parseInt(idString);
//should put this in a try catch block and throw an error if it is not a number.
String requestedFile = fileMap.get(id);
if(requestedFile == null)
//throw some error.
// Do whatever you need to do with the file.
}
You should
Move each URI specific logic into a different servlet.
Use the servlet mapping concept from Java Servlet Specification and define the following in your web.xml
<!- Define the servlets here, ofcourse you should use meaningful names -->
<servlet>
<servlet-name>servlet-1</servlet-name>
<servlet-class>com.vikdor.webapps.ServletForURI1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet-2</servlet-name>
<servlet-class>com.vikdor.webapps.ServletForURI2</servlet-class>
</servlet>
....
<!- Map the servlet to the URL pattern -->
<servlet-mapping>
<servlet-name>servlet-1</servlet-name>
<url-pattern>/1/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet-2</servlet-name>
<url-pattern>/2/</url-pattern>
</servlet-mapping>
....
This will push the routing logic to web.xml and each servlet does a specific job.
Use a framework. That's what they're for. I'm partial to Spring MVC but for a basic use case like this anything modern should be fine. It might feel like a pain to first get it setup but once it's configured development is much faster and much more maintainable.
Edit: I just noticed the original question says not to use any frameworks/libraries. I still stick by this answer though as the majority of the time that's a bad idea. If you really have to then you can create Maps as suggested by a few other people but in the long run it's not maintainable so avoid it while you can.
You can specify in your web.xml uri patterns to route to specific servlet classes. Forgive my bad servlet names.
<servlet>
<servlet-name>processUserXml</servlet-name>
<servlet-class>com.example.server.ProcessUserXml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>processUserXml</servlet-name>
<url-pattern>/processuser</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>processItemXml</servlet-name>
<servlet-class>com.example.server.ProcessItemXml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>processItemXml</servlet-name>
<url-pattern>/processitem</url-pattern>
</servlet-mapping>
So instead of accepting uri requests from a single gateway and routing them through if statements (as you're doing now), when a user calls a specific uri, it'll get routed to the appropriate servlet class for you. So calling uri /processuser will route the call to the ProcessUserXml servlet class doGet method (or doPost, if it's defined and that's how the user made the HTTP call).

Is it possible to map a servlet to /* without overriding JSP processing

Elaborating on this:
I map a servlet or filter to "/*"
Now, if I access a url like:
/test
Then this will be directed to the servlet (which is okay)
But if i access a url like:
/index.jsp
This will be directed also to the servlet, I dont want this behavior, what I want is for index.jsp to be processed as jsp.
How can this be done?
Map your controller servlet on a more specific url-pattern like /controller/* and create a Filter which is mapped on /* and does roughly like follows in doFilter() method.
String uri = ((HttpServletRequest) request).getRequestURI();
if (uri.endsWith(".jsp")) {
chain.doFilter(request, response); // Just let it go. The container's builtin JspServlet will pickup this.
} else {
request.getRequestDispatcher("/controller" + uri).forward(request, response); // Goes to controller servlet.
}

Categories