I've just started learning how to code REST web services, and I've been stuck with this for several days now. I'm coding an example application with header-based filtering, using Jersey 2 and deployed on Tomee-plus 1.7.2. No matter what I try, the ContainerRequestFilter's filter method is never called.
// TestRequestFilter.java
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
#Provider
#PreMatching
public class TestRequestFilter implements ContainerRequestFilter
{
private final static Logger log = Logger.getLogger(DemoRESTRequestFilter.class.getName());
#Override
public void filter(ContainerRequestContext requestCtx) throws IOException
{
System.out.println("FILTER-REQUEST");
}
}
My web.xml file is empty save for the required headers. The behavior right now is: the filter is recognised as #Provider and instantiated as normal, the test web service I have (just a GET returning an empty Response) can be called normally, but the filter method is never called.
Things I've tried and their effects:
Declare the filter in a class extending Application: Error on deployment.
Register the filter in a class extending ResourceConfig: Filter is instantiated twice, but filter method is still not called.
Use the classes from the com.sun.jersey.spi.container package: No effect.
Add an authentication annotation (#RolesAllowed, #PermitAll,...) to the WS method: No effect.
Add disabled=true to cfx-rs.properties in server configuration: Deployed service cannot be found at usual URL.
Add this to web.xml: No effect.
<servlet>
<servlet-name>CongressAppWS</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>org.glassfish.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.s4w.congressapp.auth.DemoRESTRequestFilter;com.s4w.congressapp.auth.DemoRESTResponseFilter</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.s4w.congressapp.auth;com.s4w.congressapp.resources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Using com.sun.jersey.spi.container prefix instead of org.glassfish.jersey.spi.container in previous code: No effect.
I'm honestly running out of options here. Every time I try something new, either there is no effect or everything stops working. Any help?
I found an alternative to ContainerRequestFilter that actually works! It's javax.servlet.Filter. Extending from this class, all I had to do was annotate it with the following code and the filtering mechanism works like a charm.
#WebFilter(filterName = "AuthenticationFilter", urlPatterns = { "/*" })
The reason for this is that ContainerRequestFilter is part JAX-RS 2.0, but TomEE 1.7.X comes with JAX-RS 1.1. You'll either have to upgrade TomEE to 7.0.0+ or use a different Server (e.g. Glassfish).
Related
Firstly I would like to say that I am new to development and have started working directly with REST. So I may be stupid to ask some silly questions. Please accept my apologies in advance!
Regarding the Problem - I am trying to learn how REST API WebServices can be developed so I started with a Mavan Project using the Archetype - "jersey-quickstart-webapp" and using Chrome POSTMAN as the Rest Client.
I am successful in writing GET methods but getting struck while writing PUT.
Every time I get only two things - a) Runtime Exception with 404 error or b) 415 Error with Media unsupported.
I have tried different combinations for #Consumes annotation and method parameters like #FormParam, #QueryParam but no luck. I am neither successful in reading the value sent via PUT to my application from POSTMAN nor I am able to send a simple response back to POSTMAN. Please suggest and drive me a path where I can overcome these small hurdles.
My code -
package tcs.suraj.learnwebservices;
import java.util.ArrayList;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import tcs.suraj.learnwebservices.domain.MovieBean;
#Path("/movies")
public class Movies {
static ArrayList<MovieBean> movieList = new ArrayList<MovieBean>() ;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getMovie(){
System.out.println("Under Construction");
return "Under Construction";
}
#PUT
#Consumes("application/x-www-form-urlencoded")
#Produces(MediaType.TEXT_PLAIN)
public String updateMovie(#FormParam("name") String name){
Response r ;
System.out.println(name +" updated!");
return name;
}
}
I am assuming you are using Tomcat for deployment, add this to your web.xml and then try again.
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Note: I have used org.apache.catalina.filters.CorsFilter in Tomcat 7.0.53. I am not sure what is the least version supported for CorsFilter.
I want to build a REST-API with Jax-Rs with cors enabled. So I googled how to do and found this:
http://www.developerscrappad.com/1781/java/java-ee/rest-jax-rs/java-ee-7-jax-rs-2-0-cors-on-rest-how-to-make-rest-apis-accessible-from-a-different-domain/
The solution is a ResponseFilter, that adds some header information to every response, so that the browser of the user knows, that cross domain accesses are allowed.
Because the ResponseFilter is not executed when I do any request (tried GET, POST and OPTIONS), I googled again and found this:
ContainerResponseFilter not working
#lefloh gave a good answer, that sounds logic (to remove the annotation #PreMatching). It did so, but nevertheless my filter is not invoked, when I do a Http-Request.
This is my code:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class RESTServiceResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext parContainerRequestContext, ContainerResponseContext parContainerResponseContext) throws IOException {
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Origin", "*" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Headers", "Content-Type" );
}
}
I kept on googling and found out, that I forgot to add the Filter to the web.xml. So I also did that:
<servlet>
<display-name>webinterface.api</display-name>
<servlet-name>JAX-RS REST Servlet</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>de.tsystems.lbus.apprestserver</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>de.apprestserver.filter.RESTServiceResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/TNR/*</url-pattern>
</servlet-mapping>
I have no more ideas now and want to ask you, if you maybe have a solution for me. Thanks in advance!
You're using Jersey 1 (can tell by the com.sun.jersey in your web.xml). The filter implementation you are using (or showing us) is Jersey 2. There's subtle difference, but it's a major difference. The latter will not work with Jersey 1.
And the fact that it even compiles (if that's the case) means that you need to get rid of some dependencies. The class you have is a JAX-RS 2 class (interface). Any JAX-RS/Jersey 2 dependency, you might have, get rid of them. They don't play well (maybe not cause of issue, but get rid of them to drop any confusion)
Jersey 1 == com.sun.jersey (keep)
Jersey 2 == org.glassfish.jersey (get rid of)
JAX-RS 2 api == javax.ws.rs-api (get rid of)
See here for Jersey 1 implementation and configuration
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/.
I'm trying to call a method from my server side whose signature is
public Integer method()
but when I redid all the steps used on the StockWatcher tutorial to call it, I'm getting a 404 error which says this is the URL
<p>RequestURI=/com.medtronic.empattendance.EmployeeAttendance/empQueries</p>
I'm not sure what the correct URL should be, but this is the incorrect URL.
my web.xml says this on servlets
<servlet>
<servlet-name>empQueryServerImpl</servlet-name>
<servlet-class>com.medtronic.empattendance.server.EmpQueryServerImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>empQueryServerImpl</servlet-name>
<url-pattern>/empattendance/empQueries</url-pattern>
</servlet-mapping>
Where am I going wrong?
There is an alternative: Use the #RemoteServiceRelativePath (javadoc) annotation on your RPC class (The interface extending RemoteService, not the Async one).
Assuming your GWT app is /empattendance:
#RemoteServiceRelativePath("empQueries")
public interface EmpQueryServer extends RemoteService {
// your methods
}
I have solved it:
I had <url-pattern>/empattendance/empQueries</url-pattern> which was based off the tutorial, but digging deeper I found out I needed to use the full package name.
<url-pattern>/com.myCompany.empattendance.EmployeeAttendance/empQueries</url-pattern>
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