Overriding included provider in Jersey - java

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());
}
}

Related

cannot be cast to javax.ws.rs.core.Application in resteasy and EJB 3.0

I am getting exception com.sai.peps.ejb.frequentexceeding.FrequentExceedingBean cannot be cast to cannot be cast to javax.ws.rs.core.Application
I am using EJB 3.0 and jboss 5.1 & resteasy
Please suggested to me, where is my mistake.
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>com.sai.peps.ejb.frequentexceeding.MyRestApplication</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest/</param-value>
</context-param>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sai.peps.ejb.frequentexceeding.MyRestApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Edited:
package com.sai.peps.ejb.frequentexceeding;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/rest")
public class MyRestApplication extends Application {
private Set<Object> resourceObjects = new HashSet<Object>();
private Set<Class<?>> resourceClasses = new HashSet<Class<?>>();
public MyRestApplication() {
resourceClasses.add(FrequentExceedingBean.class);
}
#Override
public Set<Class<?>> getClasses() {
return resourceClasses;
}
#Override
public Set<Object> getSingletons() {
return resourceObjects;
}
}
i have added this code.. Still not working
If your class com.sai.peps.ejb.frequentexceeding.FrequentExceedingBean should be the Aplication class as it is configured, it must extend javax.ws.rs.core.Application because the servlet container will instantiate an object of this class and then cast ist to javax.ws.rs.core.Application to be able to call it's getClasses() and getSingletons() method.
Update
Alas JBoss 5.1 has not yet Servlet 3.0., so you cannot confugre it without the web.xml.
Please check the accepted answer of this question for the needed entries in the web.xml file. The configuration is different from yours in respect to where the classnames are. I myself haven't used web.xml configuration for quite a time anymore, so I am not so firm there.
your web.xml seems a little bit messy.
The question is: why should you provide your own implementation fo Application class?
If you have
resteasy.scan = true
you don't need anything else (exept for proper annotations on your Resource and Providers classes to publish your resources).
if you have
resteasy.resources
valued with a list of resources
you can omit the resteasy.scan parameter and explicitly provide a list of resources to be published (in this case you put com.sai.peps.ejb.frequentexceeding.MyRestApplication and it's wrong, probably you should have put com.sai.peps.ejb.frequentexceeding.FrequentExceedingBean)
You may override Application class to move configuration out of web.xml and still have a strict control on the list of resources to be published with custom logic. Are you sure this is your need? If this is the case you should skip above configurations.
In any case choose one of the three methods

JaxRS Exception mapper not found

I have a #Provider annotated exception mapper, but Jersey isn't loading or registering it.
In my web.xml (trimmed down):
<servlet>
<servlet-name>Jersey</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
com.my.company;
com.fasterxml.jackson.jaxrs.json;
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
the mapper:
package com.my.company.some.package;
#Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException exception) {
return Response.status(Status.NOT_FOUND).build();
}
}
I have #Path annotated API classes in the com.my.company.other.package package, those are found correctly and I can access them without problems. None of the #Provider annotated mappers are found.
If I put a log message in the mapper constructor and/or the toResponse method, those aren't displayed either. I'm forced to conclude that Jersey just can't find or decides not to load the classes. How do I get this to work?
The provider packages attribute in jersey is not recursive. It scans those packages only, not packages starting with that name.

Interface in RESTful webservices using jersey [duplicate]

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>

How do I configure my entity-filtering scope for security annotations in the web.xml?

Reading the jersey doc : https://jersey.java.net/documentation/latest/entity-filtering.html I was able to activate the SecurityEntityFilteringFeature by adding it to my web.xml along with other activated features.
So my web.xml's features part looks like that :
...
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.glassfish.jersey.server.gae.GaeFeature;
org.glassfish.jersey.server.mvc.jsp.JspMvcFeature;
org.glassfish.jersey.media.multipart.MultiPartFeature;
org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
org.glassfish.jersey.message.filtering.SecurityEntityFilteringFeature;
</param-value>
</init-param>
...
The annotations #PermitAll (which changes nothing) and #DenyAll (which always remove entity from json) work great.
The question is : to use the annotation #RolesAllowed I also need to register the roles in the entity-filtering scope as said in the documentation
EntityFilteringFeature.ENTITY_FILTERING_SCOPE - "jersey.config.entityFiltering.scope"
Defines one or more annotations that should be used as entity-filtering scope when reading/writing an entity.
But I can only configure it through my web.xml and I have nowhere to do the following :
new ResourceConfig()
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {SecurityAnnotations.rolesAllowed("manager")})
// Register the SecurityEntityFilteringFeature.
.register(SecurityEntityFilteringFeature.class)
// Further configuration of ResourceConfig.
.register( ... );
Any guess ?
You can use a ResourceConfig and a web.xml together. It is not "either one or the other". For example
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.foo.JerseyConfig</param-value>
</init-param>
</servlet>
package org.foo;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(...);
property(...);
}
}
Both the web.xml and the ResourceConfig registrations/configuration/properties, etc will be used. You can see some other deployment options, here.
If you really must stay away from the ResourceConfig (not sure why it would be such a problem), you can always create a Feature.
#Provider
public class MyFilteringFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
context.property(...);
context.register(...);
return true;
}
}
Then just register the feature (unless you are scanning packages, then it should be picked up with the #Provider annotation).

Jersey global ExceptionHandler doesn't work

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> {
}

Categories