How to inject a bean into JAX RS resource? - jersey

I am sure this is most probably a silly question but I am not familiar with JAX RS (and Jersey).
We've had a standalone Java application that basically starts a RESTful service. As part of a refactoring, we've moved this application to be just a thread within another application. That other application uses Spring beans that are defined in an application-context.xml. So, I need to inject some of those beans to the resource class (if that's the correct name for it: the one with #Path annotations, etc.). The problem is I don't know what instantiates this particular class. There is a main class of the legacy app that is creating a (jetty) Server instance with ServletContexthandler to which a ServletHolder is added to which a ResourceConfig is set. Something like that.
So, I can inject my stuff from Spring to this main class but can't see how exactly I can pass those objects to the JAX RS resource?
I am sure I miss something pretty simple.
Edit: I have added a better explanation to my problem and a solution I found below.

Jersey has integration with Spring support. For this case, there are really only two things you need to do:
Make sure you have the integration dependency. You'll also need to the commons logging, so it doesn't complain
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring4</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Just add a ContextLoaderListener along with a WebApplicationContext containing your Spring context configuration.
ServletContextHandler context
= new ServletContextHandler(ServletContextHandler.SESSIONS);
AnnotationConfigWebApplicationContext wac
= new AnnotationConfigWebApplicationContext();
wac.register(SpringConfig.class);
context.addEventListener(new ContextLoaderListener(wac));
Here the SpringConfig is just a "Java config" Spring configuration class. If you wanted you could use an XML application context, but the example I used in the below link uses a Java config class, but also show how to easily import an XML into the class if you just want to use your XML config. You can combine two.
That's pretty much it. Once you have this configured, you should be able to #Autowired your Spring beans into your Jersey resources.
For a complete example, check out this GitHub repo

Maybe I wasn't able to explain well my problem, so basically it was a problem of how to inject beans into JAX-RS resource classes when the actual JAX-RS app is not being instantiated through its own DI-mechanism but from somewhere else. In my case I already had a Spring container that creates those beans and there was no easy way to link the Spring's own bean application context to the JAX-RS's one. A better solution would have been the one already answered but additional problem is that our existing Spring solution is XML-based, whereas the #Injected annotation in JAX-RS won't work with it (at least that's what I've read in their documentation).
So, JAX-RS supports #Injected annotations and in order for it to know where to get bean definitions from, I had to go to the class that defines the ResourceConfig and add the following lines to it:
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(beanImpl1).to(BeanInterface1.class);
bind(beanImpl2).to(BeanInterface2.class);
}
})
The actual beanImpl1 and beanImpl2 bean instances were coming through the constructor of that class, which in turn was instantiated from our Spring through the XML configuration.

Related

How to use Java Spring boot cache

I have java spring boot application. I want to use cache for frequently read data. For this i included the following dependencies in my jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
I have also used the #EnableCaching annotation
#EnableCaching
public class SpringBootConfig {
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootConfig.class, args);
}
}
Used #Cacheable annotation with the function that return the data i want to cache
#Cacheable(value = "country",key = "'countryCache'+#countryCode")
private Country getCountry(String countryCode) {
return new Country(countryCode);
}
But I am still not able to cache the data. Is there anything that i am missing?
The annotation #Cacheable is only available for public exposed method that are allowed to be intercepter. But you could get the "CacheManager" services and use it in your code to handle internally the cache in the privated methods if required. But only to solve some "special" problems the usual way is to annotate the public methods.
Also if you use only the starter you are using only the basic and poor implementation of Spring, a simple memory cache.
Think about how your application will work (single app, distributed app, short/long amount of data cached,...) and the memory consumption to add a dependency of any of the supported cache managers like ehCache, Hazelcast,Caffeine,... that meets your requirements and improves your cache performance.
did you already had a look in the Getting Started Guide for Caching Data?
There is a paragraph which will explain why the cache isn't working in your code.
The #EnableCaching annotation triggers a post-processor that inspects every Spring bean for the presence of caching annotations on public methods. If such an annotation is found, a proxy is automatically created to intercept the method call and handle the caching behavior accordingly.
Because your getCountry Method is private the caching won't work.
Maybe it is reasonable for you to cache the result of the calling method?
Only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual cache interception at runtime even if the invoked method is marked with #Cacheable.
Also, I would recommend using Ehcache implementation with spring boot, which allows you to do conditional caching. check this post

Following Spring 5 webmvc Rest service guide - No converter found for return value of type

I am following the official guide for Building a RESTful Web Service from Spring.io
In short, I'm following the above guide but using Tomcat to deploy and execute.
I've looked at some SO questions and answers regarding this issue.
Spring Boot Application: No converter found for return value of type
and some others.
I have the getter methods in the Greeting class though I don't have setters. This is exactly how the class is on the guide.
I tried adding the fasterxml json dependency on my pom.xml but the error message is the same.
All my setup is exactly the same as the guide except the app is bootstrapped by a dispatch-servlet.xml instead of a main method.
web.xml
<servlet>
<servlet-name>greeting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>greeting</servlet-name>
<url-pattern>/greeting/*</url-pattern>
</servlet-mapping>
greeting-servlet.xml
<context:component-scan base-package="com.test" />
This is it. All the other classes are written exactly the same as the guide. with #RestController and no #ResponseBody as #RestController is rolled out with #Controller and #ResponseBody according to the guide itself.
This is my error message.
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.test.Greeting
I don't think it's the issue with getters( I definitely have them).
Nor it's the issue with not having the fasterxml dependency.
What am I missing?
You definitely need the main method:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The #SpringBootApplication annotation does more than you think. The most important thing is that it calls#EnableAutoConfiguration. This is used to figures out which other dependencies you do in your project. In your case that annotation would configure Jackson to parse your Java object to JSON.
Directly from the JavaDoc:
Enable auto-configuration of the Spring Application Context,
attempting to guess and configure beans that you are likely to need.
Auto-configuration classes are usually applied based on your classpath
and what beans you have defined. For example, If you have
tomcat-embedded.jar on your classpath you are likely to want a
TomcatEmbeddedServletContainerFactory (unless you have defined your
own EmbeddedServletContainerFactory bean).
If you still think creating a war file is a good idea, check if this can help you setup Jackson with plain Spring.
EDIT: How to run Spring Boot without Tomcat
You could check this out, the doc says to do this:
..
<packaging>war</packaging>
..
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
And in theory you should be good to go, BUT that might not be the case so check out the sample project from Spring.
These people also got some issues, so check out if it also effects you.

For dependency Injection to work, what is the prerequisite

Although I have worked in Spring framework for a while, I believe I still don't fully understand it. I want to know how and where dependency injection works.
To reinforce the concepts, I created a standalone Java program, and wanted to see how to inject the implementation using Spring framework.
Here is the snippet of spring bean file:
<beans .....>
<bean id="CommomImpl" class="com.example.common.impl.CommomImpl">
</bean>
<bean id="SimpleImpl" class="com.example.simple.impl.SimpleImpl">
</bean>
</beans>
Now, the below is the standalone Java program where I wanted to see how DI works.
public class MainApp {
Resource(name = "SimpleImpl") -------------------> (1)
static SimpleTasks simpleTasks1;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean-config.xml");
SimpleTasks simpleTasks = null;
CommonTasks commonTasks = null;
simpleTasks = (SimpleTasks) context.getBean("SimpleImpl"); ----->(2)
simpleTasks.print("USER");
commonTasks = (CommonTasks) context.getBean("CommomImpl"); ----->(3)
commonTasks.add(3,3);
simpleTasks1.print("USER"); ---------(4)
}
}
With regards to above program, I have following questions:
Q1: The code at (1) doesn't provide any injection, that is simpleTasks1 is null. What this is so? How come in web-applications, we can inject implementation using similar fashion?
Q2: Are (2) and (3) dependency injection?
Q3: The code at (4) gives null pointer exception, because (1) doesn't inject the implementation. It is this which is confusing me. Under what situations does DI work? When we create the web-application, we deploy the .war inside a web-container and NOT in Spring container, then how come #Resouce(name = "xyz") works, when the spring framework jars itself are provided as dependency to the application.
Any detailed explanation would be of great help to clear these doubts.
Dependency injection works by having a way (configuration) to provide dependencies in your classes. Instead of having the class create its dependencies, you pass the required objects from outside. This makes your code testable and you can take advantage of object reusability.
Dependency injection in Spring works in a similar way with other DI frameworks.
You can either inject a dependency of a class through its constructor, or through a member field (private fields also work in Spring).
What you're doing is calling the spring context to locate the dependency. Once you do this, Spring will try to locate a bean (provider) for the dependency you want. Once it finds it, it will create the object (by resolving its own dependencies as well) or use an existing instance of it and it will return the instance to you.
Spring provides the #Autowired annotation, as a replacement of using the context, which you can use to inject dependencies in your classes and it makes your code cleaner and safer since you don't use the bean name to inject you dependency.
You could also have a look at Java based configurations for spring to avoid configuring your beans in XML files and use the #Bean annotation.
I would strongly suggest to have a look at the guides in the Spring website, especially the ones that use Spring Boot since they are giving you all the info you need to setup a basic example using Java annotations.

Spring boot metrics with Jersey2

I have an application running spring-boot, jersey2 and spring metrics:
below is maven snippet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Jersey used to work well until introducing actuator dependency.
Then following bean has been created to make Jersey working as filter:
#Bean
public FilterRegistrationBean jerseyFilterRegistration() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("jerseyFilter");
bean.setFilter(new ServletContainer(resourceConfig()));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex", managementContextRegex);
return bean;
}
Metrics are mapped to /admin path. With this configuration I cannot make metrics working. However by adding management.port (different than main app port) both Jersey resource and metrics are available.
What I'm missing here to make both metrics and Jersey resource start working on the same port?
"com.sun.jersey.config.property.WebPageContentRegex"
This is the wrong property. That's for Jersey 1.x. For 2.x, it should be
"jersey.config.servlet.filter.staticContentRegex"
See ServletProperties.FILTER_STATIC_CONTENT_REGEX
As an aside you can avoid having to define your own FilterRegistrationBean by simply setting a couple configuration properties. In your application.properties, you could use the following
spring.jersey.type=filter
spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=<your-regex>
Or you can configure the regex in your ResourceConfig subclass
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "<your-regex>");
}
}
As another side, just an FYI, the cause of the problem is the default /* url-mapping used for Jersey. If you can change it, doing so would solve the problem. For instance /api. You can configure that in the properties with spring.jersey.applicationPath=/api or with #ApplicationPath("/api") on the ResourceConfig subclass.
And the final aside, there is also a property
ServletProperties.FILTER_FORWARD_ON_404
"jersey.config.servlet.filter.forwardOn404"
I'm not exactly sure how the staticContenRegex property works, I never really dug into to source code. So I don't know if it just does some file IO to get the static file or it forwards the request, but if it does some file IO, then I don't think the property will work for your use case, as the endpoints are not files. In which case the forwardOn404 should work.

Prevent XXE (External Entity Processing) Attack with JAXB + Spring RESTful Web Services

I know that we can prevent the XXE attack by setting the property IS_SUPPORTING_EXTERNAL_ENTITIES in the abstract class XMLInputFactory to false in JAXB.
I have also seen this stackoverflow answer.
My question here is,
How do I create a instance of XMLInputFactory and set this IS_SUPPORTING_EXTERNAL_ENTITIES property to false when the spring application loads up. And that particular XMLInputFactory instance should only be used for all the JAXB conversion for all the classes that uses javax.xml.bind.annotation package.
Spring uses RequestMappingHandlerAdapter which is an AbstractHandlerMethodAdapter that supports HandlerMethods with the signature -- method argument and return types, defined in #RequestMapping.
There are 7 seven HttpMessageConverters and one of them is Jaxb2RootElementHttpMessageConverter
Jaxb2RootElementHttpMessageConverter is from the spring-web package.
From 3.2.8 version of spring-web onwards Jaxb2RootElementHttpMessageConverter sets the processExternalEntities to false which in turn sets the XMLInputFactory property IS_SUPPORTING_EXTERNAL_ENTITIES to false.
Refer : Jaxb2RootElementHttpMessageConverter from Spring
Answer use
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>

Resources