Jersey and HK2 ServiceLocator - jersey

I'm trying to initialize some components in my Jersey application in the Application constructor (the thing that inherits from ResourceConfig) . It looks like this
public Application(#Context ServletContext context,
#Context ServiceLocator locator)...
When I try to use the locator at any point, I still can't create instances of things that I have registered in an AbstractBinder using the locator.create(MyThing.class) method.
I'm certain that they are bound correctly because they are injected properly into my resource classes via the #inject field annotation.
The difference is that the Jersey/HK2 framework is instantiating my resource classes (as expected, since they're in my package scan path), but I can not seem to leverage the ServiceLocator through code.
My ultimate goal is to have other non-jersey classes injected when they have the #Inject attribute, eg. I have a worker class that needs to be injected with the configured database access layer. I want to say
locator.Create(AWorker.class)
and have it injected.
How do I get the real ServiceLocator that will inject everything I've already registered/bound with my Binder? (Or should I be using something other than ServiceLocator?)

I am going to assume you are starting up a servlet and have a class extending org.glassfish.jersey.server.ResourceConfig and your bindings are correctly registered (e.g. using a Binder and registerInstances). If you then want to access the ServiceLocator in order to perform additional initialization, you have two choices:
One approach is to register a ContainerLifecycleListener (as seen here in this post):
// In Application extends ResourceConfig constructor
register(new ContainerLifecycleListener() {
#Override
public void onStartup(final Container container) {
// access the ServiceLocator here
final ServiceLocator serviceLocator = container.getApplicationHandler().getInjectionManager().getInstance(ServiceLocator.class);
// Perform whatever with serviceLocator
}
#Override
public void onReload(final Container container) {
/* ... */}
#Override
public void onShutdown(final Container container) {
/* ... */}
});
The second approach is to use a Feature, which can also be auto-discovered using #Provider:
#Provider
public final class StartupListener implements Feature {
private final ServiceLocator sl;
#Inject
public ProvisionStartupListener(final ServiceLocator sl) {
this.sl = sl;
}
#Override
public boolean configure(final FeatureContext context) {
// Perform whatever action with serviceLocator
return true;
}

How are you starting up your container? If you are using ApplicationHandler, you can just call:handler.getServiceLocator(). The ServiceLocator is, indeed, what you want to be using to access your dependencies.
If you are starting up a servlet, I found that the best way to get access to the service locator was to have a Jersey feature set it on my startup class:
private static final class LocatorSetFeature implements Feature {
private final ServiceLocator scopedLocator;
#Inject
private LocatorSetFeature(ServiceLocator scopedLocator) {
this.scopedLocator = scopedLocator;
}
#Override
public boolean configure(FeatureContext context) {
locator = this.scopedLocator; // this would set our member locator variable
return true;
}
}
The feature would just be registered with our resource config with config.register(new LocatorSetFeature()).
It would be important to tie in startup of other components based on the lifecycle of your container, so this still feels a bit hacky. You might consider adding those classes as first class dependencies in the HK2 container and simply injecting the appropriate dependencies into your third party classes (using a Binder, for example).

Related

Define a custom InjectionResolver for #Inject

My goal is to add some additional logic around the System Injection Resolver (to decorate it, so to speak). Basically, I want to register a custom injection resolver (e.g. described here), but for the javax.inject.#Inject Annotation. The setup works if I create a different custom annotation, but not when using #Inject. My implementation is as follows:
Injection Resolver:
#Singleton
#Rank(Integer.MAX_VALUE)
public class InjectInjectionResolver
implements InjectionResolver<Inject> {
private final InjectionResolver<Inject> injectionResolver;
#Inject
public InjectInjectionResolver(
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME) final InjectionResolver<Inject> injectionResolver) {
this.injectionResolver = injectionResolver;
}
#Override
public Object resolve(final Injectee injectee, final ServiceHandle<?> root) {
throw new RuntimeException("Why is this never called?");
}
#Override
public boolean isConstructorParameterIndicator() {
return injectionResolver.isConstructorParameterIndicator();
}
#Override
public boolean isMethodParameterIndicator() {
return injectionResolver.isMethodParameterIndicator();
}
}
And I register the resolver as follows (inside ResourceConfig):
register(new AbstractBinder() {
#Override
protected void configure() {
bind(InjectInjectionResolver.class).to(new GenericType<InjectionResolver<Inject>>() {}).in(Singleton.class);
}
});
What I tried so far:
using #Rank
using both the org.glassfish.jersey.internal.inject.AbstractBinder or org.glassfish.hk2.utilities.binding.AbstractBinder with TypeLiteral, along with implementing the org.glassfish.hk2.api.InjectionResolver or org.glassfish.jersey.internal.inject.InjectionResolver
making sure the same setup works with #CustomAnnotation (where both the constructor and the resolve methods are called)
Does anyone know how to solve this issue?
Versions used: jersey 2.27
EDIT: When I #Inject the InjectionResolver itself, it seems that hk2's org.jvnet.hk2.internal.ThreeThirtyResolver is injected for the org.glassfish.hk2.api.InjectionResolver, while my custom injection resolver is injected for org.glassfish.jersey.internal.inject.InjectionResolver. However, the custom injection resolver is not used for injection.
The following configuration works for me, thanks to #jwells131313 for bringing me back on track. I tried to use fully qualified names, else it's probably from the javax.inject.* package.
#Singleton
#Rank(Integer.MAX_VALUE)
#org.jvnet.hk2.annotations.Service
public class MyHk2InjectionResolver
implements org.glassfish.hk2.api.InjectionResolver<Inject> {
private final org.glassfish.hk2.api.InjectionResolver<Inject> injectionResolver;
#Inject
public MyHk2InjectionResolver(
#Named(org.glassfish.hk2.api.InjectionResolver.SYSTEM_RESOLVER_NAME) final org.glassfish.hk2.api.InjectionResolver<Inject> injectionResolver) {
this.injectionResolver = injectionResolver;
}
// ... required methods, could just delegate to system resolver
}
To register the resolver, we bind it to the correct type (I do it in the ResourceConfig of a jersey application):
register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
#Override
protected void configure() {
bind(MyHk2InjectionResolver.class)
.to(new org.glassfish.hk2.api.TypeLiteral<org.glassfish.hk2.api.InjectionResolver<Inject>>() {})
.in(Singleton.class);
}
});
With this setup, you can keep using the #Inject annotation and write some custom injection logic around it, possibly using the default system injection resolver as a fallback.

How to use CDI into JAX-RS client

I have searched a while on SO and official documentation but I cannot found a way to use directly CDI injection into a JAX-RS client.
I retrieve a client using the builder method and I want to register a WriterInterceptor (or any filter like component) which uses injection to retrieve another bean.
I want to use CDI injection and avoid registering each bean with HK2.
ClientBuilder.newBuilder()
.register(MyWriter.class)
.build();
And MyWriter with the injected class.
#Provider
public class MyWriter implements WriterInterceptor {
private final MyRepo repo;
#Inject
public MyWriter(MyRepo repo) {
this.repo = repo;
}
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
context.proceed();
}
}
public class MyRepo {
}
I am running in an embedded jetty with Jersey 2 and Weld SE.
Its possible to inject in java se application using wield .
#Singleton
public class Application {
private static Logger logger = LoggerFactory.getLogger(Application.class);
#inject
private SomeOtherBean injectedBean;
public void run() {
logger.debug("application initialized");
injectedBean.doSomething();
}
}
inside main initialize weild
import java.io.IOException;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class EntryPoint {
public static void main(String[] args) throws IOException {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Application application = container.instance().select(Application.class).get();
application.run();
weld.shutdown();
}
}
Have a look at below doc
https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_java_se
also below tutorial
https://randling.wordpress.com/2011/08/15/cdi-in-java-se/
If I understand everything correctly, this has already been asked and answered. In a nutshell: you have to override the default behaviour of the H2K Binder, so it reaches for the Weld Bean Manager. You don't have to register every Bean with H2K later on.
Edit: to contain everything in the post, so you don't have to read the comments:
The linked answer is for the server-side, not the client.
With standard tools (Jersey Client-side injection providers and the Weld bridge), it seems to be a too big overhead/impossible to do
Apparently in the Dropwizard project they managed to do custom client-side injection.

ClassBridge with DAO class injected

I have a Hibernate Search ClassBridge where I want to use #Inject to inject a Spring 4.1 managed DAO/Service class. I have annotated the ClassBridge with #Configurable. I noticed that Spring 4.2 adds some additional lifecycle methods that might do the trick, but I'm on Spring 4.1
The goal of this is to store a custom field into the index document based on a query result.
However, since the DAO, depends on the SessionFactory getting initialized, it doesn't get injected because it doesn't exist yet when the #Configurable bean gets processed.
Any suggestions on how to achieve this?
You might try to create a custom field bridge provider, which could get hold of the Spring application context through some static method. When provideFieldBridge() is called you may return a Spring-ified instance of that from the application context, assuming the timing is better and the DAO bean is available by then.
Not sure whether it'd fly, but it may be worth trying.
Hibernate Search 5.8.0 includes support for bean injection. You can see the issue https://hibernate.atlassian.net/browse/HSEARCH-1316.
However I couldn't make it work in my application and I had implemented a workaround.
I have created an application context provider to obtain the Spring application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
ApplicationContextProvider.context = context;
}
}
I have added it to the configuration class.
#Configuration
public class RootConfig {
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
Finally I have used it in a bridge to retrieve the spring beans.
public class AttachmentTikaBridge extends TikaBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
// get service bean from the application context provider (to be replaced when HS bridges support beans injection)
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
ExampleService exampleService = applicationContext.getBean(ExampleService .class);
// use exampleService ...
super.set(name, content, document, luceneOptions);
}
}
I think this workaround it's quite simple in comparision with other solutions and it doesn't have any big side effect except the bean injection happens in runtime.

Java 8 and Spring 4 : Use autowiring in interface

Java 8 added a new feature by which we can provide method implementation in interfaces.
Is there any way in Spring 4 by which we can inject beans in the interface which can be used inside the method body?
Below is the sample code
public interface TestWiring{
#Autowired
public Service service;// this is not possible as it would be static.
//Is there any way I can inject any service bean which can be used inside testWiringMethod.
default void testWiringMethod(){
// Call method of service
service.testService();
}
}
This is a bit tricky but it works if you need the dependency inside the interface for whatever requirement.
The idea would be to declare a method that will force the implemented class to provide that dependency you want to autowire.
The bad side of this approach is that if you want to provide too many dependencies the code won't be pretty since you will need one getter for each dependency.
public interface TestWiring {
public Service getService();
default void testWiringMethod(){
getService().testService();
}
}
public class TestClass implements TestWiring {
#Autowire private Service service;
#Override
public Service getService() {
return service;
}
}
You can created Class utils of application context and use it everywhere even not bean class .
you can have code somethins this :
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext context) {
ApplicationContextUtil.applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
and add this to your spring configuration
<bean class="com.example.ApplicationContextUtil" id="applicationContextUtil"/>
now simple to use when you need :
ApplicationContextUtil.getApplicationContext().getBean(SampleBean.class)
this word in web and simple spring app.

How to Initialize Jersey Application (ResourceConfig) With Spring?

I'm using Jersey 2 and Spring, and I'm trying to initialize my Jersey application (i.e. the class derived from ResourceConfig) with parameters from the Spring context.
Background: I have a single Jersey application that I build (i.e. a single WAR) and I deploy it across a server cluster with different Spring configurations on different servers to enable or disable different parts of the server, e.g. some of the servers have /search resources turned on, etc. This was really easy in Jersey 1.0: I just put,
<context:component-scan base-package="com.mycompany.resources.search"/>
in a Spring config to have Jersey scan that particular package and enable the JAX-RS resource providers in it.
Now in Jersey 2.0 the Spring <context:component-scan ... /> doesn't work, so resources have to be programmatically registered in a startup class derived from ResourceConfig:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.resources.search");
}
}
So far so good, but I need to conditionally scan that package, and I can't figure out how to get any Spring configuration into the MyApplication class. I thought that constructor injection might work:
public class MyApplication extends ResourceConfig {
#Autowired
public MyApplication(#Qualifier("my-config") MyConfiguration myConfiguration) {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
However HK2 complains that it can't find a default constructor to use... so this indicates to me that DI is in play in the construction of this class, but that the DI isn't using Spring.
Similarly, using the the Spring bean lifecycle doesn't work:
public class MyApplication extends ResourceConfig implements InitializingBean {
#Autowired
private MyConfiguration myConfiguration;
public MyApplication() {
}
#Override
public void afterPropertiesSet() throws Exception {
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.search");
}
}
}
(The afterPropertiesSet method isn't called.)
So now I'm stuck: is there any way to configure a Jersey ResourceConfig application object using Spring?
UPDATE:
I accepted #JohnR's answer below but I'll also include my eventual solution which I think is a bit cleaner. #JohnR's answer was to have the object initialized twice: first by Spring and then by Jersey/HK2. When Spring initializes the object you cache the dependencies in a static member, and then when Jersey/HK2 initializes it later you can retrieve the dependencies.
I ended up doing this:
public class MyApplication extends ResourceConfig {
public MyApplication() {
ApplicationContext rootCtx = ContextLoader.getCurrentWebApplicationContext();
MyConfiguration myConfiguration = rootCtx.getBean(MyConfiguration.class);
if (myConfiguration.isEnabled()) {
packages("com.mycompany.resources.whatever");
}
}
}
Rather than having the object initialized twice, we let Jersey/HK2 initialize it but then we retrieve the dependencies from Spring.
Both solutions are vulnerable to timing: they both assume that Spring is initialized before Jersey/HK2.
Expanding on my previous comment:
Trying to extend ResourceConfig is dangerous if you don't know what you're doing. Jersey becomes unpredictable, and if you try to subclass it into an Abstract class, Jersey crashes.
Instead, the JAX-RS specification provides us with a very useful interface called Feature: It allows you to register any classes you want as if you were configuring your own application. Furthermore, you don't need to use the awkward AbstractBinder, you just specify what contracts you register your classes with.
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
// Don't use #Component here, we need to inject the Spring context manually.
public class MySpringFeature implements Feature {
#Context
private ServletContext servletContext;
private ApplicationContext applicationContext;
#Autowired
private MySecurityDAO mySecurityDAO;
#Autowired
private MySpringResponseFilter myResponseFilter;
#Override
public boolean configure(FeatureContext context) {
if(this.servletContext == null) {
return false; // ERROR!
}
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if(this.applicationContext == null) {
return false; // ERROR!
}
// This is where the magic happens!
AutowireCapableBeanFactory bf = applicationContext.getAutowireCapableBeanFactory();
bf.autowireBean(this);
// From here you can get all the beans you need
// Now we take a Spring bean instance,
// and register it with its appropriate JAX-RS contract
context.register(myResponseFilter, ContainerResponseFilter.class);
// Or, we could do this instead:
SomeSecurityFilter mySecurityFilter = new SomeSecurityFilter();
mySecurityFilter.setSecurityDAO(mySecurityDAO);
context.register(mySegurityFilter, ContainerRequestFilter.class);
// Or even this:
SomeOtherSpringBean someOtherBean = applicationContext.getBean(SomeOtherSpringBean.class);
context.register(someOtherBean, SomeOtherJerseyContract.class);
// Success!
return true;
}
}
And in your ResourceConfig:
public class MyApplication extends ResourceConfig() {
public MyApplication() {
register(MySpringFeature.class);
}
}
Ta-da!
So now I'm stuck: is there any way to configure a Jersey
ResourceConfig application object using Spring?
I don't think you can configure Jersey to obtain your ResourceConfig from Spring as a Spring managed bean. It's a bit hackish, but you could do something like this. Note that you'll end up with two instance of your ResourceConfig: one managed by Spring and another by Jersey:
public class MyApplication extends ResourceConfig {
// static, available to all instances
private static MyConfiguration myConfiguration;
public MyApplication() {
// when Spring creates the first instance of MyApplication, myConfiguration
// will be null because the setter wasn't called yet
if (myConfiguration != null)
{
// second instance created by jersey... Spring will have autowired
// the first instance, and myConfiguration is static
if (myConfiguration.isEnabled())
packages("com.mycompany.resources.search");
}
}
#Autowired
public void setMyConfiguration(MyConfiguration config)
{
// instance level setter saves to a static variable to make it available for
// future instances (i.e. the one created by jersey)
MyApplication.myConfiguration = config;
}
}
Again, this is fairly hackish. You'll want to make sure Spring is initialized before Jersey and look closely at any threading issues that could occur during initialization.

Resources