I have created a simple restful web service based on some examples and built it into a .war file(project structure has web.xml under WEB-INF), deploy it on glassfish ang get a 404 not found error when i try to call it.
My class containing the service is :
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
/**
* Created by Nikos Kritikos on 10/22/2015.
*/
#Path("/decks")
public class HS_Services {
#Path("sayHello/{name}")
#GET
public String doSayHello(#PathParam("name") String name) {
return "Hello there "+name;
}
}
my web.xml is this :
<servlet>
<servlet-name>HSRestServices</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>HSRestServices</servlet-name>
<url-pattern>/hsrest/*</url-pattern>
</servlet-mapping>
i try to call it with http://localhost:8080/HSRestServices/hsrest/decks/sayHello/Nikos but i get 404 from glassfish..
Any help would be much appreciated, thank you.
You are missing the init-param where you specify which packages should be scanned for REST endpoint classes.
Change your web.xml to look like this:
<servlet>
<servlet-name>HSRestServices</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>insert.packagename.where.your.class.is.here</param-value>
</init-param>
</servlet>
Make sure to insert the name of the package which contains your class.
You don't need Spring for this.
There is also another way which works without web.xml. For details have a look at this question.
Related
My application
In my JAX-RS/Jersey application running in Tomcat, I need to run a filter class that implements ContainerRequestFilter before every resource method. The filter determines whether or not the service is available right now1. This is done rather simply if I register the filter in web.xml:
DataResource.java:
package thepackage;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
#Path("/data")
public class DataResource {
#GET
#Produces("application/json")
public String getData() {
return "{\"data\": [{\"id\": 1},{\"id\": 2}]}";
}
}
ServiceAvailabilityFilter.java:
package thepackage;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import java.util.Random;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class ServiceAvailabilityFilter implements ContainerRequestFilter {
private static final Random RANDOM = new Random();
private boolean isServiceAvailable(ContainerRequest request) {
// Actual logic goes here; for this sample, just flip a coin
return RANDOM.nextBoolean();
}
#Override
public ContainerRequest filter(ContainerRequest request) {
if (isServiceAvailable(request)) {
return request;
} else {
throw new WebApplicationException(
Response.serverError().type(MediaType.APPLICATION_JSON_TYPE)
.entity("{\"error\":\"Service not available\"}").build()
);
}
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>jersey</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>io.github.garysheppardjr.jersey1xfiltertest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>io.github.garysheppardjr.jersey1xfiltertest.ServiceAvailabilityFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My question
However, I don't want to declare the ServiceAvailabilityFilter in XML. I want to register it in Java code. How would I do that?
Things I would rather not do
Declare the filter in web.xml. Why not? If I deliver this application as on-premises software, I want to make sure someone can't disable my filter by editing web.xml.
Check for service availability in the DataResource class. Why not? I don't want to write and maintain new code in all of my resource classes. This is one thing that filters can avoid.
Change from Jersey 1.x to Jersey 2.x. Why not? I have dependencies and deadlines that will make moving to Jersey 2.x very difficult.
1 Why might the service be unavailable, you might ask? It doesn't matter. Use your imagination. That logic is not relevant to this question, so I have replaced it with Random.nextBoolean().
Remove your <servlet> element from web.xml (or even delete web.xml altogether if that's all you have in it and you're using at least Servlet API 3.0). To take the place of the <servlet> element, create a class that extends javax.ws.rs.core.Application. In the case of this question, it might be easiest to extend com.sun.jersey.api.core.PackagesResourceConfig. Annotate your new class with javax.ws.rs.ApplicationPath. Do your <init-param> settings in the constructor of this class.
DataResource.java: no change.
ServiceAvailabilityFilter.java: no change.
MyApplication.java (or whatever you want to call it):
package thepackage;
import com.sun.jersey.api.core.PackagesResourceConfig;
import javax.ws.rs.ApplicationPath;
#ApplicationPath("/")
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
// "com.sun.jersey.config.property.packages" init-param
super(DataResource.class.getPackageName());
// "com.sun.jersey.spi.container.ContainerRequestFilters" init-param
getContainerRequestFilters().add(ServiceAvailabilityFilter.class);
}
}
web.xml: delete at least the <servlet> element, or you can delete the file altogether.
I'm using Jersey and I implemented a ContainerRequestFilter.
Now I also want to add a ContainerResponseFilter to add a header to each request but nothing happens when the webservice is accessed.
This is how the filter looks like:
public class ResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Cache-Control", "whatever");
}
}
My jersey dependencies:
compile 'org.glassfish.jersey.core:jersey-client:2.18'
compile 'org.glassfish.jersey.containers:jersey-container-servlet-core:2.18'
compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.18'
I register the providers in an xml like this:
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice</param-value>
</init-param>
//This is the request filter, which is working fine
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>model.filter.AuthenticationFilter</param-value>
</init-param>
//Response filter, does not work
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>model.filter.ResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
This didn't work, so I tried to register the provider with the annotation #Provider and in the webservice like this:
register(TokenModifier.class);
But none of these worked. I read several other posts but I couldn't find an answer to it. Does anyone have a thought on this?
This init-param
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice</param-value>
</init-param>
is used by Jersey to scan package(s) listed for #Path and #Provider annotated classes, and register them. The package(s) listed will be recursively scanned. So for example, with your current configuration, all the following packages will get scanned
controller.webservice
controller.webservice.x
controller.webservice.x.y
controller.webservice.x.y.z
The value of the param can also be multiple values separated by a comma or semi-colon. So if your filter is in a different package base, you can add that package to the list of packages to scan
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice, com.my.filters</param-value>
</init-param>
Personally though, if I'm using web.xml, I will just use one base package, and have all other packages extend from that package. Something like
com.company.app
com.company.app.domain
com.company.app.filters
com.company.app.resources
Then you can just put the com.company.app as the init-param value, and all other sub packages will get scanned also.
I have
#ApplicationPath("/resourcesP")
public class RestfulPrediction extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(PredictionsRS.class);
return set;
}
}
And
#ApplicationPath("/resourcesA")
public class RestfulAdage extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(Adages.class);
return set;
}
}
Two different ApplicationPath and the class are as follows.
#Path("/")
public class service.Adages {}
#Path("/")
public class webservices.PredictionsRS {}
Both of them are declared in different ApplicationPath. I'm using Jersey and the config in web.xml looks like
<servlet>
<servlet-name>jersey</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>
service
webservices
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
And I'm getting
SEVERE: Conflicting URI templates. The URI template / for root
resource class service.Adages and the URI template / transform to the
same regular expression (/.*)?
Why if I have two different ApplicationPath this exception comes at startup ?
If I take out a package in param-value this works, also if I change one of the #Path annotations this works, so it is a problem with my configuration ?
I'm using Jersey 1.10. Thanks all.
You did not define your JAX-RS applications in your web.xml. Try the following:
<servlet>
<servlet-name>full.name.RestfulAdage</servlet-name>
</servlet>
<servlet>
<servlet-name>full.name.RestfulPrediction</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>full.name.RestfulPrediction</servlet-name>
<url-pattern>/resourcesP/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>full.name.RestfulPrediction</servlet-name>
<url-pattern>/resourcesA/*</url-pattern>
</servlet-mapping>
and remove the #ApplicationPAth annotations from code.
I checked the above code with Jersey 2.7, servlet container 3.0 and it works. If still having that bug, try upgrading to Jersey 1.17 (which should not change any behavior from Jersey 1.10, and fix bugs instead) and eventually using also a servlet container 3.0.
UPDATE
After checking the possibilities the configuration below work with Jersey 1.17
<servlet>
<servlet-name>jersey</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.koitoer.webservices
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
It seems there is bug in the spec in older version of Jersey that kind of circle back the references and mark as duplicate endpoints. Using the configuration above both endpoints load without any problem.
8/04/2014 09:13:40 PM
com.sun.jersey.server.impl.container.servlet.JerseyServletContainerInitializer
addServletWithApplication INFO: Registering the Jersey servlet
application, named com.koitoer.webservices.chapter2.service2.RestfulPrediction, at the
servlet mapping, /resourcesP/*, with the Application class of the same
name
8/04/2014 09:13:40 PM com.sun.jersey.server.impl.container.servlet.JerseyServletContainerInitializer
addServletWithApplication INFO: Registering the Jersey servlet
application, named com.koitoer.webservices.chapter2.RestfulAdage, at
the servlet mapping, /resourcesA/*, with the Application class of the
same name
You should have a single subclass of javax.ws.rs.core.Application in your webapp, and then use different #Path annotation values on your service.Adages and webservices.PredictionsRS resource types.
AFAIK, in JEE6 containers, you are not allowed to have 2 such subclasses...
I have spring mvc application
if in web.xml i write so:
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I go to http://localhost:8080/Mvc/controllerPath/sayHello
I see my page
if I write
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/controllerPath/*</url-pattern>
</servlet-mapping>
I go to http://localhost:8080/Mvc/controllerPath/sayHello - I see 404
I think you understood what I want.
Can you hel me?
UPDATE
controller:
#Controller
#RequestMapping("/controllerPath")
public class MyController {
#RequestMapping("/sayHello")
public String sayHello(Model model){
model.addAttribute("name", "Vasya");
return "hello";
}
}
if I write
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/Mvc/controllerPath*</url-pattern>
</servlet-mapping>
i see 404
If you remove:
#RequestMapping("/controllerPath")
to
#RequestMapping("/")
the new servlet mapping will work.
The reason for this is that whatever you have in the servlet mapping url is stripped before spring tries to match it to a controller.
For example, in your first url mapping with just / (and assuming your web appllication is deployed to /mvc), your path of /mvc/controllerPath/sayHello spring strips the url mapping away from the url and expects to find a controller that maps to /controllerPath/sayHello
When you change the url-pattern to /controllerPath, since will strip that as well and look for a controller that answers to just /sayHello, which your controller won't since it's expecting /controllerPath/sayHello
I already done a lot of search and I can't fix this.
I'm bulding a Web Service with Tomcatv7.0, Jersey and Eclipse.
This is the root cause:
java.lang.ClassCastException: org.jersey.webservice.Login cannot be cast to javax.servlet.Servlet
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
...
This is the exception:
javax.servlet.ServletException: Class org.jersey.webservice.Login is not a Servlet
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
...
I have a simple class:
package org.jersey.webservice;
import ...
#Path("/login") public class Login {
// This method is called if HTML is request
#GET
#Produces(MediaType.TEXT_HTML)
public String sayHtmlHello() {
return "<html> " + "<title>" + "Hello Andre" + "</title>"
+ "<body><h1>" + "Hello Andree" + "</body></h1>" + "</html> ";
}
}
And this is my web.xml:
`<?xml version="1.0" encoding="UTF-8"?>
<display-name>org.jersey.andre</display-name>
<servlet>
<servlet-name>Andre Jersey REST Service</servlet-name>
<servlet-class>org.jersey.webservice.Login</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Andre Jersey REST Service</servlet-name>
<url-pattern>/rest</url-pattern>
</servlet-mapping>
</web-app>`
The Login class is in the package org.jersey.webservice and in WEB-INF/lib I´ve imported the needed jars (jersey-api, jersey-core, etc...).
Do you find anything wrong?
I follow the documentation and this isn´t working. Damn!
Thanks in advance.
What tutorial were you reading? This is not the right way to declare a Jersey web service. It is indeed not directly a Servlet as the exception is trying to tell you. You need to declare the main Jersey servlet container with an init param pointing to the package containing the webservice classes.
<servlet>
<servlet-name>Andre Jersey REST Service</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>org.jersey.webservice</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Andre Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Also note that you should map it on the path /rest/* not the name /rest, otherwise you won't be able to use path information like http://example.com/context/rest/foo/bar and so on.
See also:
Jersey's own User Guide
Oracle's JAX-RS with Jersey tutorial (part of Java EE 6 tutorial)
Vogela's JAX-RS with Jersey tutorial
Unrelated to the concrete problem, consider choosing something else than org.jersey as main package. E.g. org.andreelias.