How to inject a bean into custom argument resolver? - dependency-injection

Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}

Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}

This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}

Related

jBeret + Weld SE - Inject managed bean from Batchlet

I'm trying to make CDI work on JBeret SE.
This is my code:
SampleBatchlet class
#Named
public class SampleBatchlet extends AbstractBatchlet
{
#Inject
#BatchProperty(name = "foo")
String foo;
#Inject
StepContext stepContext;
#Inject
Logger logger;
#Override
public String process() throws Exception {
final String say = stepContext.getProperties().getProperty("say");
System.out.println("hello foolish");
return null;
}
}
SampleBatchletTest class
#EnableWeld
class SampleBatchletTest {
#Inject
Logger logger;
#WeldSetup
public WeldInitiator weld = WeldInitiator
.from(
LoggerProducer.class
)
.activate(
RequestScoped.class,
ApplicationScoped.class
)
.build();
#Test
void app() throws InterruptedException {
final JobOperator jobOperator = BatchRuntime.getJobOperator();
long id = jobOperator.start("simplebatchlet", null);
final JobExecutionImpl jobExecution = (JobExecutionImpl) jobOperator.getJobExecution(id);
jobExecution.awaitTermination(5, TimeUnit.SECONDS);
Assertions.assertEquals(BatchStatus.COMPLETED, jobExecution.getBatchStatus());
}
}
Server class
#ApplicationScoped
public class Server {
#Inject
private Logger logger;
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) throws InterruptedException {
logger.info("init");
}
LoggerProducer class
public class LoggerProducer {
#Produces
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
The issue is Logger instance is not injected on SampleBatchlet, whereas is correctly injected either on test and server class above.
Any hints?
LITTLE UPDATE
By reading this reference
https://jberet.gitbooks.io/jberet-user-guide/content/batch_properties/
I discovered java.util.logging.Logger can be injected.
Therefore I added
<batchlet ref="it.infocert.shop.main.SampleBatchlet" >
<properties>
<property name="logger" value="java.util.logging.Logger" />
</properties>
</batchlet>
where value can be actually anything..
On SampleBatchlet I added
#Inject
#BatchProperty
Logger logger;
and now it is injected. I'm a bit perplexed by the way, because I wish to use another logger implementation..
When injecting via #BatchProperty, JBeret tries to check the type of injection field and match it up with the injection value, and instantiate an instance for injection. That's why the logger created by JBeret, instead of your own logger, is injected. For details, see JBeret BatchBeanProducer.java
To inject your own logger via a producer method, you may need to add a qualifier to disambuiguise it. For example,
public class LoggerProducer {
#Produces
#Named("myLogger")
public Logger produceLogger(InjectionPoint injectionPoint) {
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
}
#Inject
#Named("myLogger")
Logger logger;
I changed batchet ref on my xml from:
<batchlet ref="it.infocert.shop.main.SampleBatchlet">
to:
<batchlet ref="sampleBatchlet">
now it works

hk2: why bind(X.class).to(X.class)

I am learning Java, but found the following piece of code. I am confused. What is bind(X.class).to(X.class); for?
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(X.class).to(X.class);
}
}
Thanks
You're configuring how you want your services to be discovered in the DI (dependency injection) system. bind(Service).to(Contract) is basically saying that you want to provide the Service as an injectable service, and want to "advertise" it as Contract. By "advertise", I mean what you want to be able to inject it as. For instance Service can be UserRepositoryImpl, while Contract can be UserRepository (interface). With this you would only be able #Inject UserRepository as that's what you advertise. The benefit of this is all the benefits that come with programming to an interface.
Example
interface UserRepository {
List<User> findAll();
}
class UserRepositoryImpl implements UserRepository {
#Override
public List<User> findAll() {
return Arrays.asList(new User("username"));
}
}
#Path("users")
class UserResource {
#Inject
private UserRepository repository;
#GET
public List<User> getUsers() {
return repository.findAll();
}
}
class JerseyApp extends ResourceConfig {
public JerseyApp() {
register(UserResource.class);
register(new AbstractBinder() {
#Override
public void configure() {
bind(UserRepositoryImpl.class)
.to(UserRepository.class);
}
});
}
}
Here the UserRepository is injected into the UserResource. When the DI system injects it, it will actually be the UserRepositoryImpl instance.
By doing that you are actually binding a new contract to a service.
bind(Service.class).to(Contract.class);
OR (binding a new contract to a service in Singleton)
bind(Service.class).to(Contract.class)..in(Singleton.class);

Inject dependencies into ServletContextListener using HK2 in Jersey

In order to initialize the application at startup, I extended ServletContextListener:
#WebListener
public class MyServletContextListener implements javax.servlet.ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
... initialization code here
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now I want to extract the initialization logic into a standalone StartupManager class, and delegate to this class from MyServletContextListener:
public class StartupManager {
public void performStartup() {
... initialization code here
}
}
I tried to inject StartupManager into ServletContextListener by simply adding #Inject annotation:
#WebListener
public class MyServletContextListener implements javax.servlet.ServletContextListener {
#Inject StartupManager mStartupManager;
#Override
public void contextInitialized(ServletContextEvent sce) {
mStartupManager.performStartup();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
This did not work - the reference is null when contextInitialized(ServletContextEvent ) is called.
Then I thought that I might have to register a binder:
#ApplicationPath("")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new DependencyInjectionBinder());
}
}
public class DependencyInjectionBinder extends AbstractBinder {
#Override
protected void configure() {
bind(StartupManager.class).to(StartupManager.class);
}
}
This did not work either.
My question is how can I perform injection of dependencies into ServletContextListener? Preferably constructor injection, but field injection will also be alright.
It's not going to work, as the servlet listener and Jersey are not linked to the same system. As an alternative, you can use Jersey's Event Listeners. You can implement an ApplicationEventListener where you would be able to initialization and clean up in the same way you would in the servlet listener. You would be able to inject your services into Jersey's listener.

Jersey injection

Is there a way to change the implementation of UriInfo that's injected into all the resources and classes? I want to keep most of the implementation the same, but just change one part of it (the part that provides a UriBuilder - I want to provide a different implementation of the UriBuilder).
You can create wrapper around the original UriInfo
public class MyUriInfo implements UriInfo {
private final UriInfo delegate;
public MyUriInfo(UriInfo uriInfo) {
this.delegate = uriInfo;
}
#Override
public String getPath() {
return delegate.getPath();
}
#Override
public UriBuilder getRequestUriBuilder() {
return new MyUriBuilder();
}
...
}
Then just create a Factory to return your custom UriInfo. This Factory will be used by the DI framework to inject the UriInfo.
public class MyUriInfoFactory
extends AbstractContainerRequestValueFactory<MyUriInfo> {
#Override
public MyUriInfo provide() {
return new MyUriInfo(getContainerRequest().getUriInfo());
}
}
Then just create the AbstractBinder and register it with the ResourceConfig
public class Binder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(MyUriInfoFactory.class)
.to(UriInfo.class)
.in(RequestScoped.class)
.proxy(true)
.proxyForSameScope(false)
.ranked(10);
}
}
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new Binder());
}
}
If you are using web.xml, check out this post.
Now you should be able to just inject it
#GET
public String get(#Context UriInfo uriInfo) {
return uriInfo.getClass().getName();
}
If you want to be able to retain being able to inject the original UriInfo, you can change the binding to
bindFactory(MyUriInfoFactory.class)
.to(MyUriInfo.class) // <--- Change here to MyUriInfo
.in(RequestScoped.class)
.proxy(true)
.proxyForSameScope(false)
.ranked(10);
This way, you would need to inject MyUriInfo
#GET
public String get(#Context MyUriInfo uriInfo) {
return uriInfo.getClass().getName();
}
Doing this, you are still able to inject the original UriInfo if you needed to.
See Also:
Custom Injection and Lifecycle Management

Jersey #Context scope

I have a hard time understanding the injection mechanism of Jersey. The JAX-RS Specification (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) states that injection via #Context is possible in Application subclasses, root resource classes and providers.
I now have a class that is instantiated at startup and has a method which is called on every request. Inside the method I need access to the current UriInfo object. The problem is, that this method is not called from my code. So I can't pass UriInfo directly to the method.
I actually want to do something like this:
public class MyClass implements ThirdPartyInterface {
// not possible because class is no Application subclass, root resource class or provider
#Context
private UriInfo uriInfo;
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
I tried this. Obviously with no success:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public MyClass(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
// uriInfo is null at this time :(
myClass = new MyClass(uriInfo);
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
}
}
The only workaround I can think of is this. I don't think it's very clean:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public void setUriInfo(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
myClass = new MyClass();
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
myClass.setUriInfo(uriInfo);
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
myClass.setUriInfo(null);
}
}
I hope there is a better solution, but maybe I'm completely on the wrong track.
Thanks!
Late answer, but a good question ... so lets go:
You can use a org.glassfish.hk2.api.Factory and javax.inject.Provider for injections. I don't know since which version this is available, so maybe you have to upgrade your jersery version. For the following samples i used jersey 2.12.
First you have to implement and register/bind a Factory for your MyClass:
MyClassFactory:
import javax.inject.Inject;
import javax.ws.rs.core.UriInfo;
import org.glassfish.hk2.api.Factory;
// ...
public class MyClassFactory implements Factory<MyClass> {
private final UriInfo uriInfo;
// we will bind MyClassFactory per lookup later, so
// the constructor will be called everytime we need the factory
// meaning, uriInfo is also per lookup
#Inject
public MyClassFactory(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public MyClass provide() {
return new MyClass(uriInfo)
}
#Override
public void dispose(UriInfo uriInfo) {
// ignore
}
}
Registration via ResourceConfig:
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
// ...
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class);
// ... bind additional factories here
}
});
// ...
}
}
Now you are able to inject MyClass per lookup to providers, resources etc.
But Attention: Afaig there are two approaches and only one will work as eventually aspected for providers ...
import javax.inject.Inject;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
// ...
#Provider
#Produces("application/foo-bar")
public class MyBodyWriter implements MessageBodyWriter<MyView> {
// first approache - don't do it!
// will only injected once, cause MyBodyWriter is only instantiated once
#Inject
private MyClass myClass;
// second approache - works fine!
private final javax.inject.Provider<MyClass> provider;
// MyBodyWriter instantiate once
// get an inject provider here
#Inject
public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) {
this.provider = myClassProvider;
}
#Override
public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) {
return t == MyView.class;
}
#Override
public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) {
// deprecated by JAX-RS 2.0 and ignored by Jersey runtime
return 0;
}
#Override
public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException {
// attention: its not per lookup !!!
MyClass myClassDirectInjected = myClass;
System.out.println(myClassDirectInjected); // same instance everytime
// but this is ;)
MyClass myClassFromProvider = provider.get();
System.out.println(myClassFromProvider); // it's a new instance everytime
// ...
}
}
Hope this was somehow helpfull.

Resources