Handling URLs in a Java Serlvet - java

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).

Related

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>

How to pass on parameters from pretty URL to JSP pages?

I trying to do two things at once and I don't seem to find answers that can do both.
I'm trying this:
I want URLs ending in /user/{parameter} to be mapped to a JSP page user.jsp where I can access the parameter.
I find ways to pass parameters from the web.xml file to the JSP using this method
<init-param>
<param-name>someParam</param-name>
<param-value>itsValue</param-value>
</init-param>
And I find ways to create URL filters and map them to Java Servlets.
What I need is a combination. Also, what I found on passing URL parameters to Servlets wasn't too detailed either, so a good reference on that would also be more than welcome!
I want URLs ending in /user/{parameter} to be mapped to a JSP page user.jsp where I can access the parameter.
Map a servlet on /user/* and use HttpServletRequest#getPathInfo() to extract the path info.
E.g.
#WebServlet("/user/*")
public class UserServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pathInfo = request.getPathInfo(); // "/{parameter}"
// ...
request.getRequestDispatcher("/WEB-INF/user.jsp").forward(request, response);
}
}
That's basically all. Use the usual String methods like substring() and/or split() to break down the path info in usable parts.
Use a base-url servlet to parse the URL and perform a conditional servlet forwarding to the appropriate JSP.
request.getRequestDispatcher().forward(JSPurl)
Let us say you have a URL branch /sales/. Under this URL branch, you would allow the following URLs to to be serviced by /implemented/usersales.jsp :
/sales/liquour/users/{region}
/sales/liquour/soft/users/{region}
/sales/toiletries/users/{type}
and the following URLs to be serviced by /implemented/products.jsp
- /sales/groceries/products/{section}
- /sales/groceries/meat/products/{region}
- /sales/groceries/vegetables/beans/products/{region}
You would have a web.xml servlet mapping for servlet class org.myorg.SalesHandler to be mapped to /sales/.
In the service method override in org.myorg.SalesHandler servlet class,
analyse the URL pattern (using regexp or otherwise) and then conditionally forward the request using
request.getRequestDispatcher().forward(JSPurl)
JAX-RS
But why would you do that when we have jax-rs?
How to forward from a JAX-RS service to JSP?.
JAX-RS may seem daunting at first, but it is actually very simple and intuitive to implement.

Spring and returning protected resources

I have a pretty standard Spring 3.0.7 web app
The structure is like this
WebContent/
resources/
myStaticConent/
WEB-INF/
views/
myProtectedContent/
I am using the <mvc:resources> configuration for the static content and my controllers get views using the InternalViewResolver from WEB-INF/views
Now I have a requirement to return non-JSP content ( JPGs,PNGs,HTML,etc ) from a protected directory in WEB-INF
So a user might enter a URL like http:myWebApp/myProtectedContent and hit my protected content controller.
#Controller
public class HelloWorldController {
#RequestMapping(value="/myProtectedContent")
public String index() {
return "myjpg.jpg";
}
}
Essentially I want to conditionally serve a file just like I would a view. Anyone know how this can be done ?
I looked at some of the other methods here, Streaming using Inputstream seems overkill for files that are essentially static. Can I register another "view" type ? I need this to appear l( from the web browser side ) like a standard http request response ( like the current view implementation).
I would really like to avoid inventing my own file handling methods unless there is some reason why using the file access methods are better then Springs "other" view resolvers like ResourceBundleResolver
So the requirement is
Conditionally respond to a http request with variable file type (jpg,png,html) from inside WEB-INF without wrapping in a jsp or having the file interpreted by the JSTL view. The names of the files are known and static. The controller will determine the file name based on its own business logic.
You can reproduce the behavior of the underlying implementation of <mvc:resources/> which is org.springframework.web.servlet.resource.ResourceHttpRequestHandler, which essentially streams out the content of the static files - You can like ResourceHttpRequestHandler, extend from org.springframework.web.servlet.support.WebContentGenerator which has extensive support for sending last-modified and caching related headers, and finally to stream the content also there is a utility that Spring provides:
org.springframework.util.FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
Updated:
#Controller
public class HelloWorldController implements ApplicationContextAware {
ApplicatonContext ctx = ...;
#RequestMapping(value="/myProtectedContent")
public void index(HttpServletRequest req, HttpServletResponse res) {
Resource resource = ctx.getResource("classpath:staticpath/myjpg.jpg");
FileCopyUtils.copy(resource.getInputStream(), response.getOutputStream());
}
}
Something you can do is to map a new servlet to the path you want to be protected and handle the request the way you want.
For example, in web.xml:
<servlet>
<servlet-name>protServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/protServlet-context.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>protServlet</servlet-name>
<url-pattern>/myProtectedContent</url-pattern>
</servlet-mapping>
This way, you map a new servlet (DispatcherServlet) for URLs that are protected content.
The load-on-startup value equals 2 is due if you already have a DispatcherServlet with this field value equals 1.

How to protect pdf file with Tomcat filters?

HI,
I am currently running a tomcat instance with struts 1 and I would like tomcat to detect when pdf files are requested in the URL (For example of a link: http://www.***.com/files/action=download&name=myreport.pdf).
At this point I want a java class to be instantiated, then using a pdf API I want to inject a password to a file. The main point here is that I do not want to have the password store in the original pdf file I am serving instead I want the password to be injected at runtime by Tomcat.
Please let me know if you have any ideas, I did a little of research and I came across tomcat filters but I am unsure if this will resolve this problem.
Please note the passwords are store in a database table.
Thanks
From the filter we invoke a Java class to do the actual "injecting of password".
The entry in the web.xml will redirect your call to a particular filter.
<!--web.xml call all calls to .pdf will invoke the particular filter.-->
<filter>
<filter-name>PDF Filter</filter-name>
<filter-class>PDFFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PDF Filter</filter-name>
<url-pattern>*.pdf</url-pattern>
</filter-mapping>
//This is the actual filter
public class PDFFilter implements Filter
{
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
PDFPasswordInjector pdfPassInject = new PDFPasswordInjector();
//use HttpServletRequestWrapper to get the pdf location/pdf name
pdfPassInject.injectPassword( "<pdf location>" );
chain.doFilter(request, response);
}
}
//Java class to inject the password
public class PDFPasswordInjector
{
public boolean injectPassword( String sPDFName )
{
// retrieve password from DB
// use API to inject password to PDF
}
}
Create a servlet
Set url-pattern to *.pdf
Whenever your pdf url is called, the servlet is executed.
Do whatever you want from the servlet before returning the PDF to the user in response.
It should be fairly straight forward to write a Filter to intercept all requests that return a PDF. Filter's doFilter() method has access to the request and response so you can modify it however you like.
Filters are not the way to solve this particular problem. Filters allow you to modify requests and cause them to be redirected or redispatched to different servlets. But they don't allow you to rewrite the response body. From what I understand, this is what you are trying to do.
You will have to do the PDF file modification in a Servlet, as described in #Aardvocate's answer.

Servlet non-lifecycle method call from Browser

I was wondering if it is possible to call a non-life-cycle method of a servlet directly from the Browser.
For example, just as a sample code if I have something like -
public CheckServlet extends HttpServlet {
public void foo(HttpServletRequest req, HttpServletResponse res) {
// do something
}
}
web.xml -
<servlet>
<display-name>CheckServlet</display-name>
<servlet-name>CheckServlet</servlet-name>
<servlet-class>defpkg.CheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CheckServlet</servlet-name>
<url-pattern>/CheckServlet</url-pattern>
</servlet-mapping>
The browser URL -
http://localhost:8080/MyApp/CheckServlet
Now if I have to make a call to foo() from the browser, is it possible and if yes, how?
Kind Regards.
No this is not possible! If it was possible it would be a great security hole as a user of your webapp may call any method!
But your servlet may call want it wants in it's lifecycle methods.
The servlet spec (2.5) only supports the following;
doGet
doPost
doPut
doDelete
doHead
doOptions
doTrace
No, but you can pass in some parameters through either a GET or a POST, The first one would be the name of the action you want to execute and the other ones would be the parameters to pass in. On the Servlet side you would map the different actions to different methods and simply pass in the request parameters.
Yes the browser can call in case the web-app is servlet 2.5, The new servlet spec supports custom methods, means the servlet can have doFoo() method, and the browser can call with foo method like any normal GET, POST Http method.

Categories