Which is best method for calling a required servlet - java

I have a desktop application from where a client can add a new user, can chat with other user etc... I have 2 options now:
To make a handler servlet which will get post request on
according to request the handler servlet will call required
servlet(e.g loginServlet, chatServlet , addUserServlet etc)
Directly call the required servlet from client e.g make direct
request to chatServlet for chat, login and addUser
Remember my client side is a desktop application. Which method is good for implementing and future enhancements.
Thank you

Well,I think you should go with the 1 st Approach by using Front Controller pattern.
It should consist of only a SINGLE SERVLET which provides a centralized entry point for all requests.This servlet will delegate all request to the required servlet.
You need to do only following thing to apply the front controller pattern in your application:
<servlet>
<servlet-name>////servlet name: FrontController</servlet-name>
<servlet-class>////Fully qualified servlet name e.g: org.chat.controller.FrontController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FrontController<servlet-name>
<url-pattern>/*</url-pattern>
<servlet-mapping>

I couldn't clearly understand your problem from the question. But I would suggest you would start with approach 2 and start implement.
If you face any specific problems then post here.

Related

About Welcome Page and Accessing Web Service Methods

I have a problem with configuration ( or basic understanding how things work at background). I create a JAVAEE project by checking Web application and ReSt api checkbox ( in intellij with glassfish 5.0). I have sample code below which web methods work but welcome page does not work. My web.xml and sample web service methods are below.
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>test</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
**<url-pattern>/ *</url-pattern>**
</servlet-mapping>
#Path("/RestTest")
public class TestString {
#Context
ServletContext context;
#GET
#Path("insertdb/{param1}/{param2}")
#Produces(MediaType.APPLICATION_JSON)
public Object writeToDb( #PathParam("param1") String param1
,#PathParam("param2") String param2){
try{
String password= context.getInitParameter("DbPassword");
Class.forName("org.mariadb.jdbc.Driver");
Connection dbCon = DriverManager.getConnection(
"jdbc:mariadb://xxx/testdb", "root", password);
PreparedStatement stmt=dbCon.prepareStatement(
"INSERT INTO TestTable VALUES(?,?)");
stmt.setString(1,param1);
stmt.setString(2,param2);
stmt.executeUpdate();
dbCon.close();
return "Success";
}catch(SQLException | ClassNotFoundException ex){
return ex.toString();
}
}
#GET
#Path("sum/{sum1}/{sum2}")
#Produces(MediaType.TEXT_HTML)
public String calculateSum(#PathParam("sum1") int param1
,#PathParam("sum2") int param2){
return ""+(param1 + param2);
}
If i change this line url-pattern "/*" to "/"
then welcome page is accessible but not methods.
Thus what i want is, having a welcome page which i will use for documentation for my web services(i dont want SOAP) and web methods must work by adding / to base url. How can i achieve that and what is difference between /* and /
See here for explanation of differences:
What is url-pattern in web.xml and how to configure servlet
Generally for a rest api it is best to use a path specific to all rest calls, for instance http://localhost/mywebapp/rest/...
Something like:
<servlet-mapping>
<servlet-name>jersey-servlet/servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You only want jersey serving particular URLs when it is bundled in a WAR that also includes html pages.
To answer your question, difference between "/" and "/*"
A mapping that contains the pattern "/" matches a request if no other pattern matches. This is the default mapping. The servlet mapped to this pattern is called the default servlet. The default mapping is often directed to the first page of an application. Example :
Both requests will display same contents from index.jsp
http://myhost.com/index.jsp
and
http://myhost.com/
Now, a mapping that contains "/*" overrides all other servlets, including all servlets provided by the servlet container 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.
Possible solution for your issue
Change the URL pattern to specific instead of default pattern.
<servlet>
<servlet-name>webservice</servlet-name> //servlet name
<servlet-class>com.rest.MyRestServlet</servlet-class> //servlet class
</servlet>
<servlet-mapping>
<servlet-name>webservice</servlet-name> //servlet name
<url-pattern>/RestTest/*</url-pattern> //all webservice request
</servlet-mapping>
All the web service request are accessible through
http://myhost.com/RestTest/
You may also be interested to look
What is URL-pattern in web.xml and how to configure servlet
Basics of Java Servlet
Servlet configuration and url-pattern
As you highlighted, your problem revolves around those four lines:
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
As Jim Weaver mentioned, it's a matter of url-pattern.
Solution(s)
You may consider three solutions (from the most-preferred to least-preferred):
dedicated REST URL: the easiest way is to have a dedicated url-pattern such as /rest/* for your web services. You can benefit some assets such as url hierarchy or you can easily implement a security framework over it.
URL rewriting may be an option and this answer suggests some library. I haven't tested those libraries myself
Page redirection can be an option to go around REST filtering but in the specific case of having the url-pattern at /*, I have to say I'm not sure if it's working for the reason I'll explain in next section
now a bit of explanation of what happened
Why setting the url-pattern at /* prevent from accessing the welcome page?
Actually, it's not only the welcome page that is not accessible: it's all the resources under the defined url-pattern. Whatever get in touch with REST stays with REST... Here is the schema taken from JSR 339 Appendix C:
With a GlassFish 5.0, I guess you're using JAX-RS 2.1 (JSR 370) but the sections I'm quoting have the same content
Without entering into detail, it is visible that only ContainerRequest Filters are executed in the process. Especially, it's worthy to notice that after Request Matching, requests are not forwarded to any servlet in a sense that standard resources are not reachable, unless specified by the REST method.
It's also worthy to highlight the fact the servlet filters are executed beforehand (leveraging this point is absolutely vital for managing security for example). I did not find back the source proving this point but I know it's somewhere on the web ^^.
Request matching is defined at section 3.7.2. In a nutshell, it is in three steps where the first one is the most important for your question, especially at step D:
Identify a set of candidate root resource classes matching the request
c. ...
d. If [the set of classes whose path matches the request URL] is empty then no matching resource can be found, the algorithm terminates and an implementation MUST generate a NotFoundException (404 status) and no entity
e. ...
highlights are mine.
The two last steps are
Obtain a set of candidate resource methods for the request
Identify the method that will handle the request
TL;DR
What happened when you set <url-pattern>/*<url-pattern> and tries to access to your welcome page (or any page actually):
Your server receives the GET request
If there are filters (such those from a security framework), there are executed
REST enters the scene
Pre Match filters are executed (none if your case)
Fetch your root resources classes (in your example, all classes in the test package)
Find if one of those class match the request URL
None are found, a 404 status is returned
To avoid unnecessary URL conflicts, the best options would be having a dedicated URL for your REST methods
If you mention in web xml like following all the request receive by 'Jersey Web Application' servlet. so request to index.jsp also redirect to 'Jersey Web Application' servlet
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
To avoid this add some prefix to the url to separate rest request like following
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>rs/*</url-pattern>
</servlet-mapping>

How to make a servlet handle all URLs except JSPs [duplicate]

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.

Servlet - Dispatching request when the url-patern is like

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.

Interpreting request as JSP instead of through servlet

This should be relatively simple to do but I've yet to find a description of how to do it.
My setup is a simple web app that processes every request through a servlet (I'll call it MyEverythingServlet for this question). Here's a slightly modified version of my web.xml:
<servlet>
<servlet-name>MyEverythingServlet</servlet-name>
<servlet-class>blah.blah.blah.MyEverythingServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyEverythingServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Right now, the servlet is pretty simple and either does some work (when work.do is part of the path) and if a .txt file is specified in the path, we'll do some validation and then load the file and send the text as the response:
response.getOutputStream().print( content );
What I'd like to do is either:
Inside the servlet, if the request is a URL to a .jsp file, I'd like to be able to have the container interpret the JSP scriptlet parts/taglib stuff before I write the String to the response.
Change my web.xml to have it process .jsp files outside of MyEverythingServlet.
Inside the servlet, if the request is a URL to a .jsp file, I'd like to be able to have the container interpret the JSP scriptlet parts/taglib stuff before I write the String to the response.
There's no direct API available which processes JSP files programmatically like that. In theory, you'd like to have the JSP in public webcontent and forward the request to the JSP. You can do this with RequestDispatcher#forward().
if (request.getServletPath().endsWith(".jsp")) {
request.getRequestDispatcher(request.getServletPath()).forward(request, response);
} else {
// Write "plain" to response as you did.
}
You may only want to do some checks on the correctness of the paths, but this should at least give you the generic idea. There is however a little problem: the servlet will be invoked recursively since it's mapped on /*. You'd rather replace this MyEverythingServlet by a Filter which just delegates the remnant of the job to the appserver's builtin default servlet. Having a Servlet to listen on /* is already a design-smell indication that it should have been a Filter from the beginning on ;)
Change my web.xml to have it process .jsp files outside of MyEverythingServlet.
You can't have a "negative" url-pattern in web.xml. Best what you can do is to let the servlet listen on a more specific url-pattern like *.txt or /static/* and keep the JSP files there outside.

Using SiteMesh with RequestDispatcher's forward()

I'm attempting to integrate SiteMesh into a legacy application using Tomcat 5 as my a container. I have a main.jsp that I'm decorating with a simple decorator.
In decorators.xml, I've just got one decorator defined:
<decorators defaultdir="/decorators">
<decorator name="layout-main" page="layout-main.jsp">
<pattern>/jsp/main.jsp</pattern>
</decorator>
</decorators>
This decorator works if I manually go to http://example.com/my-webapp/jsp/main.jsp. However, there are a few places where a servlet, instead of doing a redirect to a jsp, does a forward:
getServletContext().getRequestDispatcher("/jsp/main.jsp").forward(request, response);
This means that the URL remains at something like http://example.com/my-webapp/servlet/MyServlet instead of the jsp file and is therefore not being decorated, I presume since it doesn't match the pattern in decorators.xml.
I can't do a <pattern>/*</pattern> because there are other jsps that do not need to be decorated by layout-main.jsp. I can't do a <pattern>/servlet/MyServlet*</pattern> because MyServlet may forward to main.jsp sometimes and perhaps error.jsp at other times.
Is there a way to work around this without expansive changes to how the servlets work? Since it's a legacy app I don't have as much freedom to change things, so I'm hoping for something configuration-wise that will fix this.
SiteMesh's documentation really isn't that great. I've been working mostly off the example application that comes with the distribution. I really like SiteMesh, and am hoping I can get it to work in this case.
My understanding is that SiteMesh is integrated into the application as a Servlet filter. By default, servlet filters are only invoked against the original incoming request (in your case, the request to the servlet). Subsequent forward or include requests are not passed throuh the filter, and therefore will not be passed through sitemesh.
You can, however, instruct the filter to be invoked on forwards, using something like this:
<filter-mapping>
<filter-name>sitemesh</filter-name>
<servlet-name>MyServlet</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Which instructs the container to only operate on FORWARD requests. The other options are INCLUDE and REQUEST, you can have several elements.
So your options are to either change your filter config to specify FORWARD, or to change your filter-mapping to match the servlet path, rather than the JSP path. Either one should work.

Categories