JSR 2.0 annotations are not inherited by implementation - java

I'm trying to generate a rest-api using cxf and I have run into trouble, it seems like the PATH-annotation is not inherited correctly or something, If create a method that does not need #Path it works, but when I use Path I have to annotate the implementating method aswell
I have the following Api for use with cxf
#Path("/web")
public interface WebApi
{
#GET
#Path("/{id}/")
String getStuff(#PathParam("id") String id);
}
with the following implementation
public class WebApiServer implements WebApi
{
private final Logger logger = LoggerFactory.getLogger(getClass());
#GET
//#Path("/{id}/") //if I uncomment this line, it works
public String getStuff(#PathParam("id") String id)
{
logger.info("get called with args {}", id);
return "asdf";
}
}
I'm using CXF version 3.0.1 and javax.ws.rs-api version 3.0.1
my web.xml
<web-app>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
<init-param>
<param-name>jaxrs.serviceClasses</param-name>
<param-value>com.richo.merlin.web.api.WebApiServer</param-value>
</init-param>
<init-param>
<param-name>jaxrs.address</param-name>
<param-value>/</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
I have also put the entirety of my code here: https://github.com/RichoDemus/stackoverflow-cxf-troubleshoot

Keep your contract as is, but:
Move the class level annotations to the concrete implementaions of your WebApi interface.
Remove all method level annotations from the concrete implementation class, that is redundant.
After why would you have an interface and many implementations if those can only registered to the /web URI, and why would you redeclare the annotations if those are already declared in your interface.
If you want to go with other annotations, you would need to overload you methods declared in the interface level because overriding won't do it:
public interface WebApi
{
#GET
#Path("/{id}/")
String getStuff(#PathParam("id") String id);
}
#Path("/web")
public class WebApiServer implements WebApi
{
private final Logger logger = LoggerFactory.getLogger(getClass());
public String getStuff(String id)
{
logger.info("get called with args {}", id);
return "asdf";
}
}

According to the Inherited type class reference, You can't inherit annotations to implementing classes:
Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.
A possible solution is to define an abstract class WebApiImpl implements WebApi and inherit from it.

Related

#Context not injecting ServletContext in RESTEasy JAX-RS application

I am deploying a JAX-RS application to JBoss EAP 6.2.
I am trying to obtain a ServletContext from inside of a JAX-RS resource class so that I can read some context-param values that I have set in the WEB-INF/web.xml file.
I.e., after I've gotten hold of the ServletContext I was planning to call ServletContext#getInitParam to get the value.
I am using injection to get the ServletContext as advised here.
The relevant portion of my web.xml is:
<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>foo.MyApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/jax-rs/*</url-pattern>
</servlet-mapping>
So I am using RESTEasy which is bundled with JBoss.
Class MyApplication is:
public class MyApplication extends Application {
private Set<Object> singletons = new HashSet<>();
public MyApplication() {
singletons.add( new MyResource() );
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
… and finally in class MyResource I have the following:
#Path(...)
public class MyResource {
#Context
ServletContext context;
public MyResource() {
// I understand context is supposed to be null here
}
// ... but it should have been injected by the time we reach the service method.
#Path("/somePath")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response someMethod( ) {
if (context==null) throw new RuntimeException();
...
}
}
The above code always results in the RuntimeException being thrown. I.e. RESTEasy somehow fails to inject the ServletContext. Note that I don't have any other JAX-RS problems. I.e. if I hardcode the context-param values I was hoping to be able to retrieve via `ServletContext#getInitParameter", then the JAX-RS rest functionality works as expected when the WAR is deployed to JBoss.
Experimenting further I discovered that the ServletContextis only injected if I perform the injection at an argument of the service method like this:
#Path("/somePath")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response someMethod(#Context ServletContext servletContext) {
...
}
… however I would prefer not to change the API. Moreover, I would like to perform some costly initialization based on the context-param value once and for all, not on every service method invocation.
My questions are:
why is the injection failing?
I am a little tired of the annotations magic failing at runtime, is there a way to get the ServletContext without using annotations?
Alternatively, is it possible for my MyApplication class to obtain the ServletContext and pass it to the MyResource class as a constructor parameter?
If all else fails I guess I can always read and parse the web.xml file myself using Class#getResourceAsStream ?
Based on the comment by FrAn that links to this answer, this is what I ended up doing:
public class JaxRsApplication extends Application {
private Set<Object> singletons = new HashSet<>();
public JaxRsApplication(#Context ServletContext servletContext) {
Assert.assertNotNull(servletContext);
singletons.add( new UserDatabaseResource(servletContext) );
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
… and then, in the UserDatabaseResource class I have the following:
public UserDatabaseResource(ServletContext servletContext) {
Assert.assertNotNull(servletContext);
...
String jndiNameForDatasource = servletContext.getInitParameter("whatever")) ;
...
}
This works as the UserDatabaseResource class which is my DAL layer is a singleton and I just needed to get the JNDI name of the datasource to use (from the web.xml file). But maybe this approach also works with some minor adjustments for non-singleton classes as well.

Descriptor-less Jersey servlet container run as filter in Servlet 3.x container

Is there a way to run the Jersey servlet container (2.x) descriptor-less as a javax.servlet.Filter in a Servlet 3.x container? I need to serve static resources alongside my services and therefore need to use jersey.config.servlet.filter.forwardOn404 or jersey.config.servlet.filter.staticContentRegex which only work when run as a filter according to Javadoc
The property is only applicable when Jersey servlet container is configured to run as a javax.servlet.Filter, otherwise this property will be ignored.
I'd like to get rid of the web.xml completely
<?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">
<display-name>My-Webservice</display-name>
<filter>
<filter-name>Jersey Filter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.foo.webservices.MyApplication</param-value>
</init-param>
</filter>
</web-app>
and have everything in my custom Applicationclass
#ApplicationPath(value = "/")
public class MyApplication extends ResourceConfig
{
public MyApplication()
{
packages("com.foo.webservices.services");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
The official documentation (https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet.3) doesn't state anything about filters unfortunately.
It's possible, but not gonna be as easy as just setting some config property. It would help if you understand a little about how it actually works. With Servlet 3.x, introduced a ServletContainerInitializer that we can implement to load servlets dynamically (this is discussed further here). Jersey has an implementation that it uses. But it follows the JAX-RS which says that the application should be loaded as a servlet. So Jersey doesn't doesn't offer any way around this.
We could write our own ServletContainerInitializer or we can just tap into Jersey's. Jersey has a SerletContainerProvider we can implement. We would need to register the servlet filter ourselves. The implementation would look something like this
#Override
public void preInit(ServletContext context, Set<Class<?>> classes) throws ServletException {
final Class<? extends Application> applicationCls = getApplicationClass(classes);
if (applicationCls != null) {
final ApplicationPath appPath = applicationCls.getAnnotation(ApplicationPath.class);
if (appPath == null) {
LOGGER.warning("Application class is not annotated with ApplicationPath");
return;
}
final String mapping = createMappingPath(appPath);
addFilter(context, applicationCls, classes, mapping);
// to stop Jersey servlet initializer from trying to register another servlet
classes.remove(applicationCls);
}
}
private static void addFilter(ServletContext context, Class<? extends Application> cls,
Set<Class<?>> classes, String mapping) {
final ResourceConfig resourceConfig = ResourceConfig.forApplicationClass(cls, classes);
final ServletContainer filter = new ServletContainer(resourceConfig);
final FilterRegistration.Dynamic registration = context.addFilter(cls.getName(), filter);
registration.addMappingForUrlPatterns(null, true, mapping);
registration.setAsyncSupported(true);
}
Once we have our implementation, we need to create a file
META-INF/services/org.glassfish.jersey.servlet.internal.spi.ServletContainerProvider
Which should be at the root of the class path. The contents of that file should be the fully qualified name of our implementation.
You can see a complete example in this GitHub Repo

Autowired is null and not working with Jersey + Spring

I have problem figuring out why My Jersey RESTful entry point can't find the Spring Bean that I configure when the app server starts. It kept getting NullPointerException after trying from
Spring DI - Autowired property is null in a REST service
NullPointerException on #Autowired attribute with Jersey and Spring for REST service
#Autowired is not working with jersey and spring
Integrating both spring mvc and Jersey, getting a null pointer when viewing a jersey endpoint
Jersey 2 + Spring: #Autowired is null
Web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.testing.resource</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Spring-context.xml
<context:annotation-config />
<context:component-scan base-package="com.testing.config, com.testing.repository, com.testing.workflow" />
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:jdbc.properties</value>
</property>
</bean>
Jersey servlet entry point
#Component
#Produces(MediaType.APPLICATION_JSON)
#Path("/{userId}/items")
public class UserResource
{
#Autowired
private UserWorkFlow userWorkFlow;
#GET
public Response getAllItems(#PathParam("userId") String userId)
{
// ** NullPointerException here complains about the UserWorkFlow
return Response.status(200).entity(userWorkFlow.getItemsFor(userId)).build();
}
}
Service layer
I also tried to make an interface for this but it didn't work.
#Service
public class UserWorkFlow
{
#Autowired
private AllItems allItems;
public List<Item> getItemsFor(String id)
{
return allItems.getItemsFor(id);
}
}
Repository and DAO
#Repository
public class AllItems
{
#Autowired
private ItemSql itemSql;
public List<Item> getItemsFor(String id)
{
return itemSql.getAllItemsFor(id);
}
}
MyBatis Mapper (this has a XML associated with it)
public interface UserSql
{
List<Item> getAllItemsFor(#Param("userId") String userId);
}
I also changed to com.sun.jersey from org.glassfish.jersey but didn't work. I am running out of ideas what could be wrong. Can anyone spot what did I do wrong ?
The link I provided for your previous question had links to four fully working examples. You could have easily just grabbed one of the examples and built on top of it.
I will just walk you through one of the examples. Maybe it was too hard to follow. I will use the Jersey 2.x with Spring XML config.
First, make sure you have the dependencies (only showing versions to ones not shown in the pom)
jersey-container-servlet: 2.22.1
spring-web: 3.2.3.RELEASE
commons-logging
jersey-spring3: 2.22.1. (Notice the snapshot project uses jersey-spring*4*. This is not yet released, and will be released in the next Jersey release)
Second, make sure your web.xml is in order
Third, add your applicationContext.xml to the project class-path.
Fouth, the MyApplication class listed in the previous web.xml.
If you follow the example to the T, you will have a working example. It may not be the exact way you want to configure your Spring components, but you will have a working example you can build off of and tweak around to see what works and what doesn't. When you get more comfortable, you can see the other examples (in the first link of this answer) for other configuration styles/options.
I have extended ServiceImpl and DAOImpl classes with SpringBeanAutowiringSupport, It solved my autowired null pointer exception.

Swagger JAX-RS with Jersey 1.19 gives 'Conflicting URI templates' error

I am trying to add Swagger to an existing application that uses Jersey 1.19. For adding Swagger to the application, I have been following this guide: https://github.com/swagger-api/swagger-core/wiki/Swagger-Core-Jersey-1.X-Project-Setup-1.5.
When I deploy the application on Apache Tomcat, I get the following error:
SEVERE: Conflicting URI templates. The URI template / for root resource class io.swagger.jaxrs.listing.ApiListingResource and the URI template / transform to the same regular expression (/.*)?
The odd thing is that my Jersey servlet is not deployed at the root context, but at the /api/* context as shown in the web.xml file:
<servlet>
<servlet-name>MyApp Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>app.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyApp Service</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
My MyApplication class is defined below:
public class MyApplication extends Application {
private final Set<Object> singletons = new HashSet<Object>();
private final Set<Class<?>> classes = new HashSet<Class<?>>();
public MyApplication() {
MyResource resource= new MyResource();
singletons.addAll(Arrays.asList(resource));
BeanConfig beanConfig = new BeanConfig();
beanConfig.setBasePath("/api");
beanConfig.setResourcePackage(getClass().getPackage().getName());
beanConfig.setTitle("REST API");
beanConfig.setVersion("1.0.0");
beanConfig.setScan(true);
classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
}
#Override
public Set<Class<?>> getClasses() {
return classes;
}
#Override
public Set<Object> getSingletons() {
return singletons;
}}
I have tried other configurations, such as defining the Swagger servlet in the web.xml file instead of using the BeanConfig, but the same error still occurs. I have gotten Swagger to work this way on a different project that uses Jersey 2, but unfortunately the current project has to remain on Jersey 1.19. Here is the Swagger dependency defined in the pom.xml file:
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey-jaxrs</artifactId>
<version>1.5.0</version>
</dependency>
Any help would be appreciated.
Update 2: Looks like version 1.5.8 of swagger-core fixes that issue. See this commit for details.
Update: Instead of adding Swagger resource as sub-resource it much easier to just override #Path mapping. See Solution 2 for details.
I was facing exactly the same problem. The cause of that is Swagger resource being mapped to root #Path("/") public class ApiListingResource.
Solution 1 - No concurring mappings
One simple and inflexible way around it, is not to define any resource mapping to root path #Path("/").
Solution 2 - Override #Path mapping
2.1 Override Swagger classes
ApiListingResource should get a new #Path mapping
package my.api.package.swagger
import javax.ws.rs.Path;
#Path("swagger")
public class ApiListingResource extends io.swagger.jaxrs.listing.ApiListingResource {}
SwaggerSerializers should get new package
package my.api.package.swagger;
import javax.ws.rs.ext.Provider;
#Provider
public class SwaggerSerializers extends io.swagger.jaxrs.listing.SwaggerSerializers {}
2.2 Configure overriden classes
Add my.api.package.swagger instead of io.swagger.jaxrs.listing in Swagger package config.
Solution 3 - Swagger resource as sub-resource
Other solution is to move Swagger to a different path, allowing your resources to be mapped anywhere you like. To achieve this you need to:
3.1 Remove ApiListingResource from provider classes.
if you subclass Application:
public MyApplication() {
//init BeanConfig
//add your resource classes
//classes.add(io.swagger.jaxrs.listing.ApiListingResource.class);
classes.add(io.swagger.jaxrs.listing.SwaggerSerializers.class);
}
if you configure via web.xml using com.sun.jersey.config.property.packages param:
<servlet>
<servlet-name>your-rest-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>
{your_application_packages},
<!--io.swagger.jaxrs.json,-->
<!--io.swagger.jaxrs.listing-->
</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.classnames</param-name>
<param-value>
io.swagger.jaxrs.listing.SwaggerSerializers,
io.swagger.jaxrs.json.JacksonJsonProvider
</param-value>
</init-param>
</servlet>
BTW, I have noticed that GF 3.1.2.2 with Jersey configured using <filter/> in web.xml does not work with Swagger due to Grizzly related bug.
3.2 Add ApiListingResources as a subresource to one of your resources
#Path("/")
class RootResource {
#Context ServletContext context;
#Path("/swagger")
public ApiListingResource swagger() {
return new ApiListingSubResource(context);
}
}
As ApiListingResource is now not managed by Jersey, it does not get ServletContext injected. To overcome this problem you have to pass it as a constructor parameter, and for that subclass ApiListingResource and provide proper constructor:
// context has 'default' visibility
// so we need to stay in the same package
// to be able to access it
package io.swagger.jaxrs.listing;
import javax.servlet.ServletContext;
public class ApiListingSubResource extends ApiListingResource {
public ApiListingSubResource(ServletContext sc) { this.context = sc; }
}
Now your Swagger descriptors will be under http://hostname/app-path/swagger/swagger.json and you will still be able to use the root resource.
It's a little bit longer way , but works! Hope that helps.

Provider not registering with Jersey 2.8

I have a Jersey 1.x application which I am migrating to Jersey 2.8. My .WAR depends on a .JAR file which contains a #Provider and #Component annotated class.
package com.usamp.core.ui.model.utility;
#Component
#Provider
public class UIModelObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final static Logger LOG = LoggerFactory.getLogger(UIModelObjectMapperProvider.class);
private ObjectMapper
uiModelObjectMapper;
#Override
public ObjectMapper getContext(final Class<?> type) {
return
Model.class.isAssignableFrom(type)
? uiModelObjectMapper
: null;
}
public UIModelObjectMapperProvider() {
LOG.debug("UIModelObjectMapperProvider Constructed");
}
public ObjectMapper getUiModelObjectMapper() {
return uiModelObjectMapper;
}
#Resource
public void setUiModelObjectMapper(final ObjectMapper uiModelObjectMapper) {
this.uiModelObjectMapper = uiModelObjectMapper;
}
}
I see that spring creates this #Component and properly configures it. However, even though I am using this configuration, the #Provider is not utilized:
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.usamp.biddingtool.utilities.BiddingToolConfig</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
com.usamp.biddingtool.rest
com.usamp.core.ui.model.utility
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I also tried using com.usamp as the only package and still the #Provider is not picked up in another JAR.
As a test, I created a brand new class and placed it along the classes with the .WAR:
package com.usamp.biddingtool.rest.providers.contextresolver;
#Component
#Provider
public class UIMapperProvider extends UIModelObjectMapperProvider {
}
As you can see, this class simply extends the class coming from the .JAR dependency.
When server starts up what I see is:
Spring creates the a bean to represent UIModelObjectMapperProvider and calls proper setters to configure dependencies
Spring creates the a bean to represent UIMapperProvider and calls proper setters to configure dependencies
Jersey creates a new object for UIMapperProvider and no dependencies are set
So it seems that Spring is doing it's thing, however, Jersey is:
Not seeing a #Provider in a dependent .JAR (not scanning across jars for packages?)
When a #Provider IS found with the .WAR a new object is created and Spring's bean is not used
I am using this to configure my application in web.xml:
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.usamp.biddingtool.utilities.BiddingToolConfig</param-value>
</init-param>
My BiddingToolConfig class looks like this:
public class BiddingToolConfig extends ResourceConfig {
public BiddingToolConfig() {
register(MultiPartFeature.class);
register(UIModelObjectMapperProvider.class);
}
}
I am also using the jersey-spring3 dependency:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.8</version>
</dependency>
In fact, my API methods ARE getting wired up correctly, so the following works:
#Path("bid)
#Component
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class BidAPI {
#Resource
private BidService
bidService;
#Resource
private SFBidMappingService
sfBidMappingService;
So it just seems that the #Providers are not being found when in a separate JAR, and when they are found, Spring is not utilized.
Temporarily, I created a new #Provider class in the .WAR and implemented it to forward the call to Spring's bean:
#Provider
public class UIMapperProvider implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(final Class<?> type) {
return
ApplicationContextProvider
.getApplicationContext()
.getBean(UIModelObjectMapperProvider.class)
.getContext(type);
}
}
But I should not have to do this.
Any idea what I am missing?
Thanks.
-AP_

Categories

Resources