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.
Related
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>
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).
I have a web.xml that defines a Jersey Servlet of class org.glassfish.jersey.servlet.ServletContainer, that is then configured with my own application class that extends ResourceConfig using:
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.mypackage.AppResourceConfig</param-value>
</init-param>
In my AppResourceConfig class I am constructing and registering Metric's MetricRegistry and HealthCheckRegistry instances.
Now I want to add a Metric's Health Check servlet like what is described at https://dropwizard.github.io/metrics/3.1.0/manual/servlets/ - That is, create the Metric's servlets and listeners to provide instances of the metrics registries to the servlet, such as:
public class MyHealthCheckServletContextListener extends HealthCheckServlet.ContextListener {
public static final HealthCheckRegistry HEALTH_CHECK_REGISTRY = new HealthCheckRegistry();
#Override
protected HealthCheckRegistry getHealthCheckRegistry() {
return HEALTH_CHECK_REGISTRY;
}
}
and (in web.xml)
<listener>
<listener-class>com.example.MyHealthCheckServletContextListener</listener-class>
</listener>
But taking this route would mean I have two versions of each. I know that the registries are declared as public static so I can just access them directly, but I'd prefer not to do it that way.
Is there a way to inject these directly into my own servlet AppResourceConfig class?
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> {
}
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());
}
}