How to access non-singleton objects from a Jersey Resource? - java

In our company we work a lot with small Java applications as service in Windows. To be able to get status reports from these applications we use Jersey to output some JSON data.
To get the needed application data we currently setup the application as a singleton. From the resource handler in Jersey we can access the object via it's static getInstance method.
Now we are upgrading the complete application landscape and have made some changes to our applications. One of the changes is that the applications are no longer singletons. Is there any other way of accessing the application object without it being singleton and without the handler being an inner class?
Here is a simplified version of the code:
public class Main {
protected int data; // a property which has to be accessible by
// the jersey handler
protected Closeable server;
protected ResourceConfig resourceConfig;
public Main() {
// set the jersey handle
resourceConfig = new DefaultResourceConfig(JerseyHandler.class);
// start the jersey server
server = SimpleServerFactory.create("http://0.0.0.0:" + port, resourceConfig);
}
public int getData() {
return data;
}
}
#Path("/")
public class JerseyHandler {
#Path("status")
#GET
public Response status() {
// how to access Main's getData() method from here without
// anything being a singleton or an inner class???
int data = ????;
Response.ok().entity(data).build();
}
}

You should inject Main as dependency with Dependency Injection mechanism that is supported by Jersey. I use Jersey 2.12 and Google Guice 3.0 for the same purpose.
Example:
#Singleton
#Path("language")
#Produces(MediaType.APPLICATION_JSON)
public class LanguageResource {
private final LanguageService langService;
#Inject
public LanguageResource(LanguageService dateService) {
langService = dateService;
}
}
To configure the listener with custom Guice module:
ServletContextHandler handler = new ServletContextHandler();
handler.setServer(server);
handler.addEventListener(new ServletGuiceConfig());
Your Guice config could look like this:
public class ServletGuiceConfig extends GuiceServletContextListener {
protected static Injector injector;
#Override
protected Injector getInjector() {
injector = Guice.createInjector(new ServiceConfig(), new ServletModule() {
#Override
protected void configureServlets() {
bind(LanguageService.class).to(LanguageServiceImpl.class);
}
});
return injector;
}
}

Related

Java Guice DI error: UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl

I have a simple REST API project using Jersey 2.x. I tried using Google Guice to inject my dependencies, but it doesn't seem to work. I get this error:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=AccountService,parent=AccountsResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1658198405)
I have this simple resource class
#Path("/accounts")
#Produces(MediaType.APPLICATION_JSON)
public class AccountsResource {
private final AccountService accountService;
#Inject
public AccountsResource(AccountService accountService) {
this.accountService = accountService;
}
#GET
#Path("test")
public String test() {
return this.accountService.test();
}
I want to inject this service into my resource class
public class AccountService {
public AccountService() {}
public String test() {
return "test";
}
}
So, following Guice's guide, I created this module class
import com.google.inject.*;
public class AccountsResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
}
}
Finally, I added the injector in my main method
public class TradingServer implements Runnable {
private static final int PORT = 8181;
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AccountsResourceModule());
AccountsResource accountsResource = injector.getInstance(AccountsResource.class);
new TradingServer().run();
}
public void run() {
Server server = new Server(PORT);
ServletContextHandler contextHandler = new ServletContextHandler(server, "/");
ResourceConfig packageConfig = new ResourceConfig().packages("ca.ulaval.glo4002.trading");
ServletContainer container = new ServletContainer(packageConfig);
ServletHolder servletHolder = new ServletHolder(container);
contextHandler.addServlet(servletHolder, "/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
server.destroy();
}
}
}
When I call my server, I get the error mentioned above. It seems like the dependency injection didn't work. Please help
So Jersey knows nothing about Guice. It already uses it's own DI framework, HK2. There are a couple things you can do. You can either tie Guice together with HK2 so that HK2 can find services that are bound inside Guice, or another way is to just bind your resource classes inside Guice and and register instances of those resources with Jersey.
Tie Guice with HK2
To tie Guice with HK2, you need to use the Guice HK2 Bridge. First you need to add the following dependency
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
To get the hk2.version look at your Jersey dependencies (you can run mvn dependency:tree and see what version of HK2 Jersey is pulling in). You want to make sure you are using the exact same version.
Next thing you need to do is to programmatically link the two systems. One way to do this is inside a Feature.
public class GuiceFeature implements Feature {
#Override
public boolean configure(FeatureContext context) {
// This is the way in Jersey 2.26+ to get the ServiceLocator.
// In earlier versions, use
// ServiceLocatorProvider.getServiceLocator(context);
ServiceLocator locator = InjectionManagerProvider.getInjectionManager(context)
.getInstance(ServiceLocator.class);
Injector injector = Guice.createInjector(new AccountResourceModule());
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
Then just register the feature with Jersey.
ResourceConfig packageConfig = new ResourceConfig()
.packages("ca.ulaval.glo4002.trading")
.register(GuiceFeature.class);
And that's it. It should work, as I have tested.
Bind resources with Guice
With the above configuration, Jersey will be creating instances of your resource classes (#Path annotated classes). The reason we need the bridge is that Jersey is tightly coupled with HK2, so when we inject our resources classes, when creating the instance, Jersey will call HK2 to try to find all the dependencies for the resource.
In this case though, we will not rely on Jersey to create the instance of the resource. We will bind the resource to Guice and let Guice create the instance when we request it. We will use that instance to register with Jersey.
First bind the resource
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
bind(AccountResource.class);
}
}
Also make sure that the #Inject annotation in the resource class is com.google.inject.Inject.
Get instance of resource and register it
Injector injector = Guice.createInjector(new AccountResourceModule());
AccountResource accountResource = injector.getInstance(AccountResource.class);
ResourceConfig config = new ResourceConfig()
.register(accountResource);
You probably have to figure out a cleaner way to do this as you don't want to have to do this for every resource you have. But this is the gist if what you need to do.
Update
So here's a quick implementation to clean up the second solution. What we can do is scan a package recursively to get all the #Path annotated classes and then bind them in Guice and register them with Jersey.
From this SO post, we can use the Reflections library to easily get all the classes. Just add the following dependency
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
Then make a little helper classes
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Path;
import org.reflections.Reflections;
public class ResourceClassHelper {
private static Set<Class<?>> resourceClasses;
public static Set<Class<?>> getResourceClasses() {
if (resourceClasses != null) {
return resourceClasses;
}
// the package to scan for #Path classes "com.example"
Reflections reflections = new Reflections("com.example");
resourceClasses = reflections.getTypesAnnotatedWith(Path.class);
resourceClasses = Collections.unmodifiableSet(resourceClasses);
return resourceClasses;
}
}
Then in your Guice module
public class AccountResourceModule extends AbstractModule {
#Override
protected void configure() {
bind(AccountService.class);
ResourceClassHelper.getResourceClasses().forEach(this::bind);
}
}
And your resource registration
Injector injector = Guice.createInjector(new AccountResourceModule());
ResourceConfig config = new ResourceConfig();
ResourceClassHelper.getResourceClasses()
.forEach(cls -> config.register(injector.getInstance(cls)));

jax-rs share information between ContainerRequestFilter and ReaderInterceptor

I'm using Jax-rs, and I'm doing some logic in the Filters, and then I would like to share information between ContainerRequestFilter(Filter) and ReaderInterceptor(Interceptor).
I can see that between Filters and Interceptors is possible through the set/getProperties but between Filter and Interceptors is not possible.
Any idea if there's any other mechanism?.
Regards.
You can use a request scoped service that you inject into both the filter and the interceptor. For instance
public interface RequestScopedService {
void setSomething(Object something);
Object getSomething();
}
public class RequestScopedServiceImpl implements RequestScopedService {
#Override
public void setSomething(Object something) {}
#Override
public Object getSomething() {}
}
It's best practice to use interfaces, that's why I did that here. To configure it, you register an AbstractBinder with your ResourceConfig 1.
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AbstractBinder() {
#Override
public void configure() {
bind(RequestScopedServiceImpl.class)
.to(RequestScopedService.class)
.proxy(true)
.proxyForSameScope(false)
.in(RequestScoped.class);
}
});
}
}
Now you can inject it into both the filter and the interceptor.
public class MyFilter implements ContainerRequestFilter {
private final RequestScopedService service;
#Inject
public MyFilter(RequestScopedService service) {
this.service = service;
}
}
public class MyInterceptor implements ReaderInterceptor {
private final RequestScopedService service;
#Inject
public MyInterceptor(RequestScopedService service) {
this.service = service;
}
}
We use the proxy() method to configure it because the service is a request scoped service (meaning a new one is created for each request) and the filter and writer interceptor are singletons. So we need it to be a proxy that forwards calls to the real service under the hood 2.
1. If you are not using a ResourceConfig for your configuration (maybe you are using web.xml), see both my answer in "Dependency Injection with Jersey 2.0 and What exactly is the ResourceConfig class in Jersey 2?.
2. You can learn some more about this in this article
So any easier way than using a separate service for this is to simply inject the ContainerRequestContext into the ReaderInterceptor. We need to inject it as a javax.inject.Provider so that we can lazy retrieve it. If we don't do this, we will run into scoping issues, as the interceptor is inherently a singleton and the request context is request scoped (meaning a new one is created for each request).
public static class MyInterceptor implements ReaderInterceptor {
private final javax.inject.Provider<ContainerRequestContext> requestProvider;
#Inject
public MyInterceptor(javax.inject.Provider<ContainerRequestContext> requestProvider) {
this.requestProvider = requestProvider;
}
#Override
public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext) throws IOException, WebApplicationException {
ContainerRequestContext request = requestProvider.get();
String prop = (String) request.getProperty("SomeProp");
}
}
With the javax.inject.Provider, we get the actual service by calling get(). Because we are using Provider, the service will be retrieved from a request scoped context, meaning that the instance will be tied to the request.
1. See Request Scoped Injection into a Singleton with Jersey for more information.

Google Guice Assisted Inject object is null

I need to create objects with user defined data at runtime.TO do that i have used
google guice assisted inject.But when i run my test it throws null pointer exception.Please let me know where i made the mistake.
IArtifacts Interface
public interface IArtifacts {
MavenMetaDataXMLDTO getArtifactsVersions();
}
ArtifactsService.java
public class ArtifactsService implements IArtifacts {
private ProductProfile productProfile;
#Inject
public ArtifactsService(#Assisted ProductProfile productProfile){
System.out.println(productProfile.getArtifactManagementURL());
this.productProfile=productProfile;
}
#Override
public MavenMetaDataXMLDTO getArtifactsVersions() {
System.out.println(productProfile.getArtifactManagementURL());
return null;
}
}
ArtifactsFactory Interface
public interface ArtifactsFactory {
IArtifacts create(ProductProfile productProfile);
}
Module Class
#Override
protected void configure() {
install(new FactoryModuleBuilder().implement(IArtifacts.class,ArtifactsService.class).build(ArtifactsFactory.class));
}
TestArtifacts.java
public class TestArtifacts {
#Inject // this obj is null
private ArtifactsFactory artifactsFactory;
private IArtifacts s;
public TestArtifacts(){
}
public void getdata(){
//Pass custom data to factory
this.s=artifactsFactory.create(Products.QA.get());
System.out.println(s.getArtifactsVersions());
}
}
REST ENDPOINT
#GET
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public String getartifacts(){
new TestArtifacts().getdata();
}
you created an Instance of the class TestArtifacts on your own in your Rest Endpoint class but all of your classes need to be created by the Guice Framework and not by you.
So how should the Guice Framework inject something into your class when you create them with new? You also need to inject the class TestArtifacts into your Rest Endpoint and your Rest Endpoint has to be created by Guice too.
Update:
Maybe this link will help you
https://sites.google.com/a/athaydes.com/renato-athaydes/posts/jersey_guice_rest_api
I was able to fix it adding following code snippet to below TestArtifacts.java class
TestArtifacts.java
private Injector injector=Guice.createInjector(new MYModule());//where implemented configuration
#Inject
private ArtifactsFactory artifactsFactory=injector.getInstance(ArtifactsFactory.class);

Jersey HK2 injection in manually created objects

Is there any way to inject dependencies into manually created objects?
public class MyCommand {
#Inject Repository repository;
}
public Repository {
#Inject EntityManager em;
}
MyCommand command = new MyCommand();
Repository is properly registered the jersey ResourceConfig and can be injected in objects that are created through the CDI container for example a resource class.
But since I create the Command myself the #Inject annotation gets ignored.
Is there a way to get a registered class beside #Inject and #Context?
Something like Application.get(Repository.class)
public class MyCommand {
Repository repository;
public MyCommand() {
repository = Application.get(Repository.class);
}
}
----- EDIT -----
Thanks to your help and some rethinking I found a solution for my problem.
The first thing is that it's possible to inject the ServiceLocator without any preperation into you objects.
The second thing is that I moved from normal commands with a execute method to a a command bus system.
The reason for that is I have no controle over the creation of commands so there clean way to get dependencies injected.
The new approach looks like this:
class CommandBus {
private final ServiceLocator serviceLocator;
#Inject
public CommandBus(ServiceLocator serviceLocator) {
this.serviceLocator = serviceLocator;
}
public void dispatch(Command command) {
Class handlerClass = findHandlerClassForCommand(command);
CommandHandler handler = (CommandHandler) serviceLocator.getService(handlerClass);
handler.handle(command);
}
}
interface CommandHandler {
void handle(Command command);
}
interface Command {
}
class ConcreteCommand implements Command {
// I'm just a dto with getters and setters
}
class ConcreteHandler implements CommandHandler {
private final SomeDependency dependency;
#Inject
public ConcreteHandler(SomeDependency dependency) {
this.dependency = dependency;
}
#Override
public void handle(ConcreteCommand command) {
// do some things
}
}
And in my resources I have something like this:
#Path("/some-resource")
class Resource {
#Context
private CommandBus bus;
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void runCommand(ConcreteCommand command) {
bus.dispatch(command);
}
}
As pointed out by jwells - HK2 is an injection framework :)
I spent some time looking into it - I have to say, I find it much more complicated than say guice or spring. Maybe this is due to the fact that I use Dropwizard and it makes it not as easy to access the Service locators.
However, here is how you can do that.
First, you will have to get a reference to your ServiceLocator. It must be the same ServiceLocator that jersey is using as well. You can access it for example like:
How to get HK2 ServiceLocator in Jersey 2.12?
In my example code I will use an event listener, which is due to my Dropwizard Setup.
You now have 2 choices: Register your command with your Service Locator and have the injection framework handle creation, or pass the ServiceLocator to your command in order to use it.
I wrote up a quick example using Dropwizard and jersey:
public class ViewApplication extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
environment.jersey().register(new ApplicationEventListener() {
#Override
public void onEvent(ApplicationEvent event) {
if (event.getType() == ApplicationEvent.Type.INITIALIZATION_FINISHED) {
ServiceLocator serviceLocator = ((ServletContainer) environment.getJerseyServletContainer())
.getApplicationHandler().getServiceLocator();
ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {
#Override
protected void configure() {
bind(new Repository("test")).to(Repository.class);
bind(MyCommandInjected.class).to(MyCommandInjected.class);
}
});
MyCommandInjected service = serviceLocator.getService(MyCommandInjected.class);
MyCommandManual tmp = new MyCommandManual(serviceLocator);
}
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
});
}
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
super.initialize(bootstrap);
}
public static void main(String[] args) throws Exception {
new ViewApplication().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class HelloResource {
#GET
#Path("asd")
public String test(String x) {
return "Hello";
}
}
public static class Repository {
#Inject
public Repository(String something) {
}
}
public static class MyCommandInjected {
#Inject
public MyCommandInjected(final Repository repo) {
System.out.println("Repo injected " + repo);
}
}
public static class MyCommandManual {
public MyCommandManual(final ServiceLocator sl) {
Repository service = sl.getService(Repository.class);
System.out.println("Repo found: " + service);
}
}
}
In the Run method, i get access to my ServiceLocator. I bind my classes in there (so there is an example of how to do that). You can alternatively also register Binders with jersey directly - they will use the correct ServiceLocator.
The 2 classes MyCommandInjected and MyCommandManual are examples of how you can create this command.
The relevant line for you is probably:
Repository service = sl.getService(Repository.class);
This asks the service locator for a new instance of the Repository.
Now, this is just a quick example. I am much more fond of the guice bridge than using HK2 directly :) I find it much easier to use and much clearer. Using the guice-jersey-bridge you can do everything through guice and it will automatically do the right thing.
Hope that brings some inside,
Artur
You can use the inject method of ServiceLocator in order to inject already created objects. ServiceLocator is the basic registry of HK2 and should be available in your resource.

Using custom factory HK2 DI with Jersey

I'm using the HK2 container in my Jersey application . I need to use my custom factory method to get the injected instance from the HK2 container.
For example ,
// Here I declare the IOC binding.
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(Logger.class).to(ILogger.class).in(Singleton.class);;
bind(MySqlRepository.class).to(IRepository.class).in(Singleton.class);
}
}
public class MyApplication extends ResourceConfig {
public static ApplicationBinder binder ;
public MyApplication () {
binder = new ApplicationBinder();
register(binder);
packages(true, "com.myapplication.server");
}
}
Here is my code :
public class BusinessLogic
{
//#Inject
//ILogger logger ;
//Instead
ILogger logger = DependencyResolver .resolve(ILogger.class) // resolve will get ILogger from HK2 container
}
The reason I need to do this way is for sometimes , I allocate classes manually which has dependencies , so in this way each use of #Inject return null.
For example, if I use new BusinessLogic() , then the logger with #Inject is null. I have to bind businesslogic also and use IOC in order to get the ILogge.
I need something like this:
public class DependencyResolver {
public static <T> T resolve(Class<T> beanClass){
return instance;
}
}
I need to use the DependencyResolver in order to get the instances I registered in MyApplication.
Any suggestions.
Thanks in advance...
I'm not 100% sure what exactly you want to do, but ...
I think you misunderstood AbstractBinder.bind(...) or bindings itself. Also, afaig you can't inject something into an instance which is not kinda managed component (like your BusinessLogic).
See jersey.java.net - ioc for examples regarding your BusinessLogic. You may have a look at ComponentProvider and/or InjectableProvider
For your ILogger I would suggest to create and bind a Factory like this:
public class LoggerFactory implements Factory<ILogger> {
// inject stuff here if you need (just an useless example)
#Inject
public LoggerFactory(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public ILogger provide() {
// here you resolve you ilogger
return MyLocator.resolve(ILogger.class);
}
#Override
public void dispose(ILogger instance) {
// ignore
}
}
Bind Factory
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(LoggerFactory.class).to(ILogger.class).in(PerLookup.class /* or other scopeAnnotation if needed */);
// what's you Logger.class ?
// bind(Logger.class).to(ILogger.class).in(Singleton.class);
// bind(MySqlRepository.class).to(IRepository.class).in(Singleton.class);
}
}
Hope this was helpful somehow. Maybe someone is willing to write something about Providers for your case.

Categories

Resources