I am building servlets which implement a RESTful API. I understand the Jersey is a framework for deciphering and using given URL's. How do I use it in conjunction with the HttpServlet class.
I don't understand how the two work with each other. I guess this is a very broadstrokes question but I have done a fair share of reading but am still stuck on this seemingly trivial concept. I have attempted to deploy apps with classes that extend the HttpServletclass AND use Jersey annotations.
#Path("/api")
public class API extends HttpServlet{
#GET
#Path("/{name}")
#Produces("text/hmtl")
public String doGetSayHello(#PathParam("name") String name){
return "Hello" + name;
}
#GET
#Path("/articles")
#Produces("text/json")
public String doGetArticles(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
JSONObject obj = new JSONObject();
obj.put("interns", interns);
obj.put("company", "Stack Overflow");
return obj.toString();
}
}
Any help or informative materials would be greatly appreciated!
Actually you are confused because you don't understand how jersey works. Jersey framework basically uses com.sun.jersey.spi.container.servlet.ServletContainer servlet to intercept all the incoming requests. As we configure in our projects web.xml, that all the incoming rest request should be handled by that servlet. There is an init-param that is configured with the jersey servlet to find your REST service classes. REST service classes are not Servlet and they need NOT to extend the HttpServlet as you did in your code. These REST service classes are simple POJOs annotated to tell the jersey framework about different properties such as path, consumes, produces etc. When you return from your service method, jersey takes care of marshalling those objects in the defined 'PRODUCES' responseType and write it on the client stream. Here is a sample of jersey config in web.xml
<servlet>
<servlet-name>REST</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>
com.rest.services;
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>REST</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Jersey uses a servlet to route URLs to the appropriate service. Your service itself does not need to extend a servlet.
At a high level, Jersey's ServletContainer class accepts the requests, and then based on your Jersey configuration, your web service will be invoked. You configure what url patterns are processed by Jersey. Check out section 5.3 http://www.vogella.com/articles/REST/.
Related
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>
I am writing spring mvc application.
In my application I have web pages as well as rest web services to handle ajax call.
I have done below entry in web.xml
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring_myapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Should I map my rest url with same servlet like
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Or should I make new servlet entry for rest.
I have done required entries in pom.xml for "org.codehaus.jackson" and also I have made required entries in my spring_myapp-servlet.xml.
For html page I am using below code in my controller
#RequestMapping(value = "/htmlUrl")
public ModelAndView ModifyValiodation(HttpServletRequest request) {
// my code
}
For rest service I am using
#RequestMapping(value = "/restUrl")
public #ResponseBody Map<String, String> restUrl(HttpServletRequest request) {
// my code
}
If I am using only one servlet for two url mapping, then total 4 url will be made.
myapp/htmlUrl.html
myapp/restUrl.html
myapp/rest/htmlUrl
myapp/rest/restUrl
If I am using two different servlet with individual dispacherServlet then will i have to make entry of every component and service of spring in both the servlet.xml?
Please point out the solution for exposing rest web service.
Thanks!
use
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
If you use two DispatcherServlet entries , it will load two ApplicationContext Objects in your application. Since you are using spring mvc to handle all the requests to your app, you should be fine with this configuration. Any request url that ends with .html or any urls that contains /rest/ will be handled by spring.
It is up to you to design the server side of the infrastructure.
Neither the RESTful specifications have any instructions for doing this nor the Servlet specifications enforce anything on this.
On the Applications design I think it is better idea to keep two different servlets to handle different URLs because over time the classes will become complex and long. These to may be used as front controllers and may have common logic class in the backend.
I am wondering if it is possible to dispatch a request from a servlet to a Jersey (JAX-RS implementation) resource class. I am trying to do it but it doesn't seem to work and according to my logging, the jersey resource is never reached.
Code examples are below. Is what I'm trying to do impossible for some reason?
Please note, the Jersey resource works correctly when I access it directly in a web browser via the address bar.
Also please note that 'RequestDispatcher.forward()' works as expected. It is just 'include' that doesn't.
The servlet
//The Jersey dispatcher url-filter is set to '/api/*'
String servletID = "/api/items";
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(servletID);
dispatcher.include(request, response);
The Jersey resource
#GET #Path("/items")
#Produces ({MediaType.TEXT_XML})
public JAXBElement<Items> getItems(#PathParam("project") String project) throws IOException, JAXBException {
log.debug("reached getItems");
//Omitted code that returns 'Items' object wrapped in JAXBElement
}
Relevant parts of web.xml
<servlet>
<servlet-name>jerseyDispatcher</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>uk.co.web.api.resource</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jerseyDispatcher</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
It is possible you forward the request.
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
RequestDispatcher requestDispatcher = null;
requestDispatcher = httpServletRequest.getRequestDispatcher("/items");
dispatcher.forward(request, response);
return;
However note if you receive a GET request and try to forward to a POST resource,
It will throw a 405 error.
Edit:
Let me understand what you are trying to achieve, if you need write content to response output stream you could use jersey Resource Filter.
public class YourResourceFilter implements ResourceFilter
{
public ContainerRequestFilter getRequestFilter()
{
return new ContainerRequestFilter()
{
#Override
public ContainerRequest filter(ContainerRequest containerRequest)
{
//Pre- editing the request
return containerRequest;
}
};
}
#Override
public ContainerResponseFilter getResponseFilter()
{
return new ContainerResponseFilter()
{
#Override
public ContainerResponse filter(ContainerRequest containerRequest, ContainerResponse containerResponse)
{
// after the request has been completed by your jersey resource
return containerResponse;
}
};
}
}
I got it to work, sort of (Jersey 2.13) by configuring Jersey as a filter, and not a servlet, in web.xml. Then, you can tell the container to apply the filter to included requests too:
<filter-mapping>
<filter-name>Jersey Web Application</filter-name>
<url-pattern>/api/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
RequestDispatcher.include will then work for request handled by Jersey, too. There's a caveat, though. Jersey calls response.getOutputStream, so all output must be performed through said output stream - this rules out JSP pages, that use response.getWriter instead. So, unless you figure out how to work around the problem, forget about including a Jersey resource in a JSP page or, vice versa, including the result of evaluating a JSP as part of a REST response.
I am trying to setup a simple web service (deploy on tomcat) which goes like this:
#Path("/api")
public interface Api {
#GET
#Path("categ")
public String getCateg();
}
and I have the following class implementing the interface:
public class RAPI implements API {
public String getCateg() { ... }
}
My web.xml looks as follows:
<servlet>
<servlet-name>API</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.api.resources</param-value> <!-- THIS IS CORRECT -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>API</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
But when I try to deploy on Tomcat I get the following (rather expected error):
com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Which (not copied the whole trace here) tells me that although it found the API interface it cannot instantiate it.
How can I declare which of the implementing classes will actually act as the REST web service?
Having an interface annotated with JAX-RS allows you to create remote proxy clients. We do this with Apache CXF, but I haven't tried it with Jersey.
EG in my Spring config I can have;
<jaxrs:client id="myClient" inheritHeaders="true"
address="http://myhost/rs"
serviceClass="com.mycorp.restful.MyServiceInterface">
<jaxrs:headers>
<entry key="Accept" value="application/xml"/>
</jaxrs:headers>
</jaxrs:client>
I can now use this spring bean by just calling the methods. I don't have to create a Client and I don't have to care about the relative paths of the different RS services it defines.
As for using Interface for REST Service it is a good idea IMHO. But one thing do not annotate Interface itself leave it for implementation. This way you may have more flexibility. For instance,
public Interface Readable {
#GET
#Path("/read/{id}")
public String read(#PathParam("id") Integer id);
}
#Service
#Path("/book")
public class Book implements Readable, ... {
...
public String read(Integer id){...}
}
As for Jersey proxy check this:
https://jersey.java.net/project-info/2.0/jersey/project/jersey-proxy-client/dependencies.html
Application configuration:
Web application using java first method of creating JAX-WS 2.0 Web Services with annotations.
WebLogic 10.3
My Requirements
The requirements I have are to deploy a single web service implementation class, but change logic based on the URL from which the service was accessed.
Question:
I'm assuming a good way to do this is to deploy different mappings in web.xml and initialize them with different parameters. Is there a better way?
What is the best way to switch logic off the URL from which the web service was accessed? Should I try to configure two servlet mappings in web.xml with initialization parameters (tried, but couldn't get it to work), or should I parse the URL in the service impl? Any other alternatives?
What I've Tried (but didn't work)
I have tried adding the <init-param> in the <servlet> element in web.xml. However, can't get to the ServletConfig object inside the web service to retrieve the param. The web service does not have all the functionality of a standard Servlet (even if I implement Servlet or ServletContextListener). I only have access to the WebServiceContext (it seems) and from there I can only get <context-param> elements--but I would need <init-param> elements instead.
In web.xml, I enter two <servlet> elements using the same Java class, but which map to two different URLs as follows. Notice how the "source" param is different in each Servlet mapping.
<servlet>
<servlet-name>Foo</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/Foo</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Bar</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Bar</servlet-name>
<url-pattern>/Bar</url-pattern>
</servlet-mapping>
You very well may have, but did you try using MessageContext at runtime to determine what the source is?
#WebService
public class CalculatorService implements Calculator
{
#Resource
private WebServiceContext context;
#WebMethod
public void getCounter()
{
MessageContext mc = wsContext.getMessageContext();
// you can grab the HttpSession
HttpSession session = (HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
// ...or maybe the path info is enough
String path = mc.get(MessageContext.PATH_INFO);
// the query itself should almost definitely be enough
String query = (String) mc.get(MessageContext.QUERY_STRING);
}
}
I got the idea from http://sirinsevinc.wordpress.com/category/jaxws/. Haven't tried it, though.