i'm trying to create a global ExceptionHandler in this way:
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(Exception exception)
{
return Response.status(Status.BAD_REQUEST).entity("TEST").build();
}
}
in order to catch all the exception in the controller:
#Path("/policy")
#GET
public void getPolicy(#DefaultValue("no") #QueryParam("_id") String _id) throws Exception{
int a = Integer.parseInt("test"); // it generates NumberFormatException
}
the NumberFormatException isn't handled by MyExceptionHandler.
Where i'm wrong?
Thanks!
If you use jersey < 2.5 it could be a bug while component package scanning:
#see: https://java.net/jira/browse/JERSEY-2175
Otherwise, you properly missed to add the package of your MyExceptionHandler for component scans. The #Provider annotation means, that the implementation "should be discoverable by JAX-RS runtime during a provider scanning phase", but in fact it does'nt did it like we thought. Probably a bug, but i'm not sure.
Meaning: Afaig, you have to register package or mapper by yourself - otherwise it will not work for now!
Note: I did not tested this with servlet 3.x without Application subclass and basic web.xml #see 4.7.2.3.1. JAX-RS application without an Application subclass link
Maybe also interesting:
Disable MBW, MBR, ExceptionMapper automatic registration via META-INF/services
The jersey-metainf-services (Jersey extension module enabling automatic registration of JAX-RS providers (MBW/MBR/EM) via META-INF/services mechanism) was added #since 2.9.x
For the sake of completeness:
You can do the registration by using ResourceConfig.packages(String...packages):
import org.glassfish.jersey.server.ResourceConfig;
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
packages(new String[] {
"path.to.foo",
"path.to.bar"
});
}
}
Alternative in web.xml servlet config
...for packages:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>path.to.foo,path.to.bar</param-value>
</init-param>
...for classes
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
path.to.foo.MyExceptionHandler,
path.to.bar.FooBar
</param-value>
</init-param>
It's worth checking that the ExceptionMapper is registered as a singleton. Registering a component as a singleton depends on your framework. Here's a couple of examples:
Guice
In your injector instance --
Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
/* ... this is where modules are installed and where
* component dependencies are binded
*/
bind(MyExceptionHandler.class).in(Scopes.SINGLETON);
/* configure filters etc, omitted for brevity */
}
}
Spring
In your Spring config
<bean id="exceptionMapper" class="my.package.MyExceptionHandler" scope="singleton" />
Or using component scanning, mark your ExceptionMapper as #Component --
Config:
<context:component-scan base-package="my.package"/>
Annotated class:
#Component
#Provider
public class MyExceptionHandler implements ExceptionMapper<Exception> {
}
Related
It is possible to publish jersey rest service based on spring profile?
lets say as following example, how can I publish RegisterServices1 when using profile1?
public class ApiGWRestApplicationConfig extends ResourceConfig {
public ApiGWRestApplicationConfig() {
register(RegisterServicesApiGWInterface.class);
}
}
#Service
#Profile("profile1")
#Path(SystemConstants.REST_REGISTER)
public class RegisterServices1 implements RegisterServicesApiGWInterface {
}
#Service
#Profile("profile2")
#Path(SystemConstants.REST_REGISTER)
public class RegisterServices2 implements RegisterServicesApiGWInterface{}
web.xml
<servlet>
<servlet-name>jersey-servlet-kagw</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.ttech.tims.imos.web.ApiGWRestApplicationConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
So what you can do is get a hold of the ApplicationContext and use getBeansWithAnnotation(Path.class). This will give you all the resource instances that are part of the profile. Then you can register the instances.
I though it would be possible to inject the ApplicationContext into the ResourceConfig, but as mentioned in the comment above, it seems the creation of the ResourceConfig doesn't have access to it yet.
What I was able to get to work, is to use a JAX-RS Feature which also has access to registration methods, just like you have in the ResourceConfig. Using the Feature will give you access to the ApplicationContext
public class SpringProfilesFeature implements Feature {
#Inject
private ApplicationContext context;
#Override
public boolean configure(FeatureContext featureContext) {
Map<String, Object> resources = context.getBeansWithAnnotation(Path.class);
resources.values().forEach(resource -> featureContext.register(resource));
return true;
}
}
Then just register the feature with the ResourceConfig
public AppConfig() {
register(SpringProfilesFeature.class);
}
Remove any other registrations you have for all your resources. Just let the feature register them.
I've confirmed that this works. Not sure how you set your profile for the environment, but hopefully this is something you already know how to do.
Can I create a restful service with interface and implementation class?
If so, will all JAX-RS related imports go into the interface?
I am using jersey2.4 and jetty8.1.
Here is my MyService interface:
package foo.bar;
#Path("/abc")
public interface MyService {
#GET
#JSONP
#Path("/method/{id}")
public MyResponse getStuff(#PathParam("id") Integer id);
}
And an implementation of MyServiceImpl that interface
package foo.bar.impl;
public class MyServiceImpl implements MyService {
public MyServiceImpl() {}
#Override
public MyResponse getStuff(Integer id) {
// do stuff
return MyResponse;
}
}
Here's the web.xml file:
<servlet>
<servlet-name>Scivantage 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>foo.bar</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I registered this service provider package (foo.bar) but it complains saying this
javax.servlet.ServletException: A MultiException has 1 exceptions. They are:|1. java.lang.NoSuchMethodException: Could not find a suitable constructor in foo.bar.MyService class.
When I tried with implementation class package (foo.bar.impl), it complains saying this
I get HTTP ERROR 404; doesn't do anything else; no exceptions on console
When I tried both -- it complains the same as above:
javax.servlet.ServletException: A MultiException has 1 exceptions. They are:|1. java.lang.NoSuchMethodException: Could not find a suitable constructor in foo.bar.MyService class.
What I am doing wrong?
Here's a solution I came across after a few trials (I'm working with jetty 9 and jersey 2.13): instead of annotate the interface (with #Path("/abc")), try to annotate the implementation class instead.
I think this makes good sense since interface are 'abstract' and not supposed to be bound to physical paths. This way, the interface can be reused in different paths.
If you want to use interfaces with JAX-RS annotation you can no longer scan a package with the web.xml
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>XXX</param-value>
You need to manually bind your interface with your resource implementation
bind(YourResource.class).to(YourResourceImpl.class);
Reason for this :
We decided for performance reasons that during scanning the interfaces will be ignored. Also we fixed that Jersey will not try to instantiate interfaces.
https://java.net/jira/browse/JERSEY-1004
I was struggling with the "Could not find a suitable constructor" issue as well. I wanted to put all of my annotations (including #Path) on my interfaces. I was able to make it work by managing the lifecycle of the resources myself rather than have Jersey instantiate them.
For example, if you had YourImplementation which implements YourRestInterface, you'd do something like this to register an instance of the implementation with Jersey:
public class RestConfig extends ResourceConfig {
#Inject
public RestConfig(ServiceLocator locator) {
super();
DynamicConfiguration c = Injections.getConfiguration(locator);
Object implInstance = new YourImplementation();
ServiceBindingBuilder<Object> bb = Injections.newFactoryBinder(new BeanFactory(locator, implInstance));
// tell Jersey to use the factory below to get an instance of YourRestInterface.class
bb.to(YourRestInterface.class);
Injections.addBinding(bb, c);
c.commit();
}
private static class BeanFactory implements Factory<Object> {
private ServiceLocator locator;
private Object bean;
BeanFactory(ServiceLocator locator, Object bean)
{
this.locator = locator;
this.bean = bean;
}
#Override
public Object provide() {
// have Jersey inject things annotated with #Context
locator.inject(bean);
return bean;
}
#Override
public void dispose(Object instance) {
}
}
}
In the class ResourceConfig, there is a constructor like this
ResourceConfig(Class<?>... classes)
The constructor create a new resource configuration initialized with a given set of resource/provider classes.
So you can extend ResourceConfig to register the implementation class.
public class RestConfig extends ResourceConfig {
public RestConfig() {
// register the implementation class
super(MyServiceImpl.class);
}
}
Then, configure web.xml.
<servlet>
<servlet-name>Scivantage REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<!-- the path of RestConfig -->
<param-value>foo.bar.RestConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
But the simplest way is that register the implementation class in web.xml.
<servlet>
<servlet-name>Scivantage REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<!-- the path of implementation class -->
<param-value>foo.bar.impl.MyServiceImpl</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Yes you can use the interface to annotate. In our application we have implemented by this way. following quote is taken from Jersy specifications.
JAX-RS annotations MAY be used on the methods and method parameters of
a super-class or an implemented interface. Such annotations are
inherited by a corresponding sub-class or implementation class method
provided that method and its parameters do not have any JAX-RS
annotations of its own. Annotations on a super-class take precedence
over those on an implemented interface. If a subclass or
implementation method has any JAX-RS annotations then all of the
annotations on the super class or interface method are ignored
I think in your case the error because of you may have missed mapping please check.
<servlet-mapping>
<servlet-name>api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
In my web application, I want to create Listener which will get notified when my server get started and all bean get loaded.
In that Listener, I want to call a service method.
I used ServletContextListener.
it has contextInitialized method but it does not work in my case. it get involked when server get started but before spring bean creation.
so I get instance of service class as null.
Is there other way to create Listener.
I would go for registering an instance of ApplicationListener in the Spring context configuration, that listens for the ContextRefreshedEvent, which is signalled when the application context has finished initializing or being refreshed. After this moment you could call your service.
Below you will find the ApplicationListener implementation (which depends on the service) and the Spring configuration (both Java and XML)that you need to achieve this. You need to choose the configuration specific to your app:
Java-based configuration
#Configuration
public class JavaConfig {
#Bean
public ApplicationListener<ContextRefreshedEvent> contextInitFinishListener() {
return new ContextInitFinishListener(myService());
}
#Bean
public MyService myService() {
return new MyService();
}
}
XML
<bean class="com.package.ContextInitFinishListener">
<constructor-arg>
<bean class="com.package.MyService"/>
</constructor-arg>
</bean>
This is the code for the ContextInitFinishListener class:
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ContextInitFinishListener implements ApplicationListener<ContextRefreshedEvent> {
private MyService myService;
public ContextInitFinishListener(MyService myService) {
this.myService = myService;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//call myService
}
}
You can use Spring's event handling. The event that you are looking for is probably ContextRefreshedEvent
Yes you need to add ContextLoaderListener in web.xml, only if you want to load other Spring context xml files as well while loading the app and you can specify them as
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
</param-value>
</context-param>
for more u can visit this link might helpful to you.
Click Here
I am using Jersey 1.17 and Jersey-Spring 1.8.
I am trying to get a jersey ResourceFilter to install correctly. I want this filter to be application-wide.
I have defined the ResourceFilter implementation as a bean in spring (defined in XML) and annotated it with the #Provider annotation.
#Provider
public class ContainerResourceFilterTest implements ResourceFilter
{
public ContainerRequestFilter getRequestFilter()
{
return null; //TODO
}
public ContainerResponseFilter getResponseFilter()
{
return null; //TODO
}
}
But it doesn't get hit when I send a request in.
If I use the #ResourceFilters annotation on a particular resource then it works as expected, but I don't want to do that on every single class.
How do I register a filter that is application wide?
Answering my own questions.
Doing some further reading of the jersey source code I have found that the #Provider annotation doesn't do anything for ResourceFilterFactory or ContainerRequest/Response. These can only be registered in one of the following 2 ways
Using the META-INF/services
Using the init params of the servlet, for example in web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.my.path.ResourceFilterFactory</param-value>
</init-param>
Not sure why the code doesn't inspect the registered spring beans for #Providers that implement the appropriate interfaces.
Worth noting that registering a class that implements ResourceFilter does not work for either of the above methods. You must do it for ResourceFilterFactory and have that return ResourceFilter implementations.
The good news is once you have made jersey aware of the classes that need to be registered it calls into spring to provide those classes so auto-wiring etc works as per usual.
Jersey doesn't scan the classpath for everything - ResourceFilters are one of the things it won't find automagickally. If you're using a ResourceConfig class, register your ResourceFilter class:
#ApplicationPath("/myapp")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages(getClass().getPackage().toString());
register(ContainerResourceFilterTest.class);
}
}
Jersey will then scan that class for the filter interface, create an instance (all filters are singleton scope according to their docs) and insert it into the Jersey filter chain.
I need to make a custom ExceptionMapper in Jersey to handle the JsonProcessingException returned by Jackson.
The Jackson library already includes ExceptionMapper providers for this exception in the form of JsonMappingExceptionMapper.java and JsonParseExceptionMapper.java (link).
If I add a new provider for this exception mapper in "my.package" I get unpredictable results regarding the selected provider. Sometimes it will select the provider in "my.package" and sometimes it will select the provider in the Jackson library. The code I'm using to scan the packages is below.
PackgesResourceConfig packagesResourceConfig = new PackgesResourceConfig("com.fasterxml.jackson.jaxrs", "my.package");
Proposed Solution
Currently I am getting around this by filtering out the provider in the Jackson library manually. But what I really want to know is whether there is a more acceptable and supported way of doing this.
First I extend PackagesResourceConfig.
public class FilteredPackgesResourceConfig extends PackagesResourceConfig {
private Set<Class<?>> classesToFilter = new HashSet<Class<?>>();
public FilteredPackgesResourceConfig(String... packages) {
super(packages);
}
public FilteredPackgesResourceConfig(Map<String, Object> props) {
super(props);
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = super.getClasses();
if (classes != null && classesToFilter != null) {
classes.removeAll(classesToFilter);
}
return classes;
}
public Set<Class<?>> getClassesToFilter() {
return classesToFilter;
}
public void setClassesToFilter(Set<Class<?>> classesToFilter) {
this.classesToFilter = classesToFilter;
}
}
This I use this class to filter out the specific providers I don't want.
FilteredPackgesResourceConfig packagesResourceConfig = new FilteredPackgesResourceConfig("com.fasterxml.jackson.jaxrs", "my.package");
classesToFilter.add(com.fasterxml.jackson.jaxrs.json.JsonMappingExceptionMapper.class);
classesToFilter.add(com.fasterxml.jackson.jaxrs.json.JsonParseExceptionMapper.class);
packagesResourceConfig.setClassesToFilter(classesToFilter);
This solution gives me the desired result of only using the providers I specified.
Is there a more correct way of achieving the same result?
I also came across this problem, in my case I solved it by instead of registering com.fasterxml.jackson.jaxrs.json package I only registered the class I wanted, which in my case was com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider.
There are several ways to do this, I did it using web.xml like so:
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
my.own.package
my.other.package
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.disableMoxyJson</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Note: I'm using Jersey 2.0, in 1.x the property names and servlet class are diferent but the same config is possible.
I have the same requirement, where I have to use the custom ExceptionMapper which implements ExceptionMapper<Exception> for all Exception types. Unfortunately, Jersey by default registers org.glassfish.jersey.jackson.JacksonFeature if the dependency jersey-media-json-jackson present in the classpath which inturn registers JsonParseExceptionMapper and JsonMappingExceptionMapper automatically and due to this default mechanism these default JSON exception mappers receives all JSON related exceptions, that is the reason the custom exception mapper is not invoked particularly for JSON exceptions.
Fortunately, Jersey 2.29.1 added support for registering JacksonFeature without the exception handlers. link feature request link, code changes.
Therefore we have to override the default JacksonFeature by excluding the exception mappers as below
#Provider
public class ApplicationInitializer extends ResourceConfig {
public ApplicationInitializer() {
register(JacksonFeature.withoutExceptionMappers());
}
}