Inner dependency is not being injected (Weld + JavaSE) - java

I am in the process of refactoring an old module, by adding CDI.
I end with
public interface ApiFactory {
...
}
public class ApiFactorySp
implements ApiFactory {
#Inject
UrlProducer urlProducer; // <-- Does not get injected
...
}
and
public interface UrlProducer {
public String getUrl();
}
#Alternative
public class UrlProducerTest
implements UrlProducer {
#Override
public String getUrl() {
return "https://myTestEnv.mydomain/myWebApp";
}
}
For testing, I create a beans.xml file in META-INF:
<beans
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/beans_1_1.xsd"
bean-discovery-mode="all">
<alternatives>
<class>myOrg.myProject.myPackage.UrlProducerTest</class>
</alternatives>
</beans>
To test it, I am doing like shown in this blog
public class WeldContext {
public static final WeldContext INSTANCE = new WeldContext();
private final Weld weld;
private final WeldContainer container;
private WeldContext() {
this.weld = new Weld();
this.container = weld.initialize();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
weld.shutdown();
}
});
}
public <T> T getBean(Class<T> type) {
return container.instance().select(type).get();
}
}
and
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
public WeldJUnit4Runner(Class<Object> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Object createTest() {
final Class<?> test = getTestClass().getJavaClass();
return WeldContext.INSTANCE.getBean(test);
}
}
Now, when I try to test the logic, I do
#RunWith(WeldJUnit4Runner.class)
public class MyTest {
#Inject
UrlProducer urlProducer;
#Inject
ApiFactory apiFactory;
#Test
public void test() {
apiFactory.doSomethingThatRequiresUrlProducer();
}
}
When I run this, both of the test attributes are inject, but I get NPE because the urlProducer attribute inside of the apiFactory instance has not been assigned a value.
Why is Weld not recognizing the #Inject attribute inside ApiFactory?
JDK 7, Weld 2.2.10, Junit 4.12
UPDATE: After posting the question, started trying with a simpler, brand new project (with just two interfaces and three classes). Using Weld "standalone" did not solve the issue, using CDI-Unit did solve it.
Then I modified my original project to use CDI-Unit, but it did not improve anything. After that I change the injection of UrlProducerTest in ApiFactory from field to constructor (i.e., defining the #Inject ApiFactory(UrlProducer urlProducer) constructor) solved it. I still have not tried this solution with "standalone" Weld (that is for tomorrow), but nonetheless I am still interested in know why field injection is not working.

If UrlProducerTest is an alternative and you want to inject this bean this class should be added to beans.xml into <alternatives> tag.
EDIT:
I believe if some bean can't be injected you get exception with 'unsatisfied/ambiguous dependencies' message. Null could be injected if you used CDI producer method that returned null but this is not your scenario.
So if there are no errors in console I have two assumptions:
Injection doesn't work at all and you get NPE because apiFactory is null
You use urlProducer before injection. For example, from constructor or initialization block (apiFactory.doSomethingThatRequiresUrlProducer() is not provided). So move this logic to some method and annotate it by #PostConstruct

Because ApiFactorySp isn't a CDI bean. You need to annotate the class with #Named to identify the class as a CDI bean for CDI to perform dependency injection.

Related

How inject Hk2 beans with Guice

There is a dropwizard app, which is jersey based.
I rewrote Hk2 bean definitions into Guice and now I can inject Guice beans into Jersey Resources,
but I noticed that Hk2 beans, defined in dropwizard bundles, which I cannot rewrite, are not
visible by Guice and it fails to inject dependencies defined in Hk2.
Guice doesn't see beans defined in Hk2 bundles and Guice creates new uninitialized beans by default.
I disabled this behavior with requireExplicitBindings.
I experimented with HK2IntoGuiceBridge, but its matcher is not invoked for beans I am interested in.
ConfiguredBundleX is located in external artifact.
I tried to copy and translate bean definitions from bundles and stuck with jersey bean Provider<ContainerRequest>, I have no idea where it comes from.
public class ConfiguredBundleX implements ConfiguredBundle<MyAppConf> {
public void run(T configuration, Environment environment) throws Exception {
environment.jersey().register(new AbstractBinder() {
protected void configure() {
this.bind(new MyHk2Bean()).to(MyHk2Bean.class);
}
});
}
}
public class DependsOnHk2Bean { #Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
public class MainModule extends AbstractModule {
private final ServiceLocator locator;
protected void configure() {
binder().requireExplicitBindings();
install(new HK2IntoGuiceBridge(locator));
bind(DependsOnHk2Bean.class);
}
public class GuiceFeature implements Feature {
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
Injector injector = Guice.createInjector(
new HK2IntoGuiceBridge(locator),
new MainModule(locator));
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
return true;
}
}
// ...
public void initialize(Bootstrap<X> bootstrap) {
bootstrap.addBundle(new ConfiguredBundleX());
}
public void run(X config, Environment env) {
env.jersey().register(new GuiceFeature());
}
Unfortunately in Guice beans you have to use #HK2Inject rather than #Inject in order to inject hk2 beans into Guice. So in your code above you would do:
public class DependsOnHk2Bean { #HK2Inject public DependsOnHk2Bean(MyHk2Bean b) {} }
This is because of limitation in guice (it may be fixed by now) such that #Inject behavior could not overwritten
I have not tried the above code myself so I'm not sure it'll work, but that was the deal back when the bridge was written...
See HK2Inject and injecting-hk2-services-into-guice-services
After digging Guice and HK2ToGuiceTypeListenerImpl I figured out that there is bindListener to kind of intercept missing bindings and pull them from somewhere. #HKInject code is there, but I noticed that the listener is not called for some bean including the bean I was interested in. Yes HKInject doesn't support constructor injection (4.2.1 version)
So I decided to manually import HK beans and bind them in Guice.
Dropwizard terminology is horrible there are methods get context something, get admin context is totally something different and beans must be get with getService method!
#RequiredArgsConstructor
public class HkModule extends AbstractModule {
private final ServiceLocator locator;
#Override
protected void configure() {
binder().requireExplicitBindings();
Provider<Bar> barProvider = locator.getService(
new TypeLiteral<Provider<Bar>>(){}.getType());
bind(Bar.class).toProvider(barProvider);
bind(Foo.class).toInstance(locator.getService(Foo.class));
}
}

How to instantiate Spring managed beans at runtime?

I stuck with a simple refactoring from plain Java to Spring. Application has a "Container" object which instantiates its parts at runtime. Let me explain with the code:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
public void load() {
// repeated several times depending on external data/environment
RuntimeBean beanRuntime = createRuntimeBean();
runtimeBeans.add(beanRuntime);
}
public RuntimeBean createRuntimeBean() {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
}
}
Basically, during load container asks some external system to provide him information about number and configuration of each RuntimeBean and then it create beans according to given spec.
The problem is: usually when we do in Spring
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");
our object is fully configured and have all dependencies injected. But in my case I have to instantiate some objects which also needs dependency injection after I execute load() method.
How can I achieve that?
I am using a Java-based config. I already tried making a factory for RuntimeBeans:
public class BeanRuntimeFactory {
#Bean
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
Expecting #Bean to work in so called 'lite' mode. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html Unfortunately, I found no difference with simply doing new RuntimeBean();
Here is a post with a similar issue: How to get beans created by FactoryBean spring managed?
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html but it looks like a hammer in my case.
I also tried ApplicationContext.getBean("runtimeBean", args) where runtimeBean has a "Prototype" scope, but getBean is an awful solution.
Update 1
To be more concrete I am trying to refactor this class:
https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
#see #load() method and find "return create(cd, false);"
Update 2
I found quite interesting thing called "lookup method injection" in spring documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection
And also an interesting jira ticket https://jira.spring.io/browse/SPR-5192 where Phil Webb says https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-86051 that javax.inject.Provider should be used here (it reminds me Guice).
Update 3
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
Update 4
The issue with all these 'lookup' methods is they don't support passing any arguments.. I also need to pass arguments as I would do with applicationContext.getBean("runtimeBean", arg1, arg2). Looks like it was fixed at some point with https://jira.spring.io/browse/SPR-7431
Update 5
Google Guice have a neat feature for it called AssistedInject. https://github.com/google/guice/wiki/AssistedInject
Looks like I found a solution. As I am using java based configuration it is even simpler than you can imagine. Alternative way in xml would be lookup-method, however only from spring version 4.1.X as it supports passing arguments to the method.
Here is a complete working example:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
private RuntimeBeanFactory runtimeBeanFactory;
public void load() {
// repeated several times depending on external data/environment
runtimeBeans.add(createRuntimeBean("Some external info1"));
runtimeBeans.add(createRuntimeBean("Some external info2"));
}
public RuntimeBean createRuntimeBean(String info) {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
return runtimeBeanFactory.createRuntimeBean(info);
}
public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
this.runtimeBeanFactory = runtimeBeanFactory;
}
}
public interface RuntimeBeanFactory {
RuntimeBean createRuntimeBean(String info);
}
//and finally
#Configuration
public class ApplicationConfiguration {
#Bean
Container container() {
Container container = new Container(beanToInject());
container.setBeanRuntimeFactory(runtimeBeanFactory());
return container;
}
// LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
#Bean
public BeanRuntimeFactory runtimeBeanFactory() {
return new BeanRuntimeFactory() {
public RuntimeBean createRuntimeBean(String beanName) {
return runtimeBean(beanName);
}
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RuntimeBean runtimeBean(String beanName) {
return new RuntimeBean(beanName);
}
}
class RuntimeBean {
#Autowired
Container container;
}
That's it.
Thanks everyone.
i think that your concept is wrong by using
RuntimeBean beanRuntime = createRuntimeBean();
you are bypassing Spring container and resorting to using regular java constructor therefore any annotations on factory method are ignored and this bean is never managed by Spring
here is the solution to create multiple prototype beans in one method, not pretty looking but should work, I autowired container in RuntimeBean as proof of autowiring shown in log also you can see in log that every bean is new instance of prototype when you run this .
'
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Container container = (Container) context.getBean("container");
container.load();
}
}
#Component
class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
#Autowired
ApplicationContext context;
#Autowired
private ObjectFactory<RuntimeBean> myBeanFactory;
public void load() {
// repeated several times depending on external data/environment
for (int i = 0; i < 10; i++) {
// **************************************
// COMENTED OUT THE WRONG STUFFF
// RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
// createRuntimeBean();
//
// **************************************
RuntimeBean beanRuntime = myBeanFactory.getObject();
runtimeBeans.add(beanRuntime);
System.out.println(beanRuntime + " " + beanRuntime.container);
}
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
// #Component
class RuntimeBean {
#Autowired
Container container;
} '
A simple approach:
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
Instead of using SingletonBeanRegistry you can use this:
BeanFactory beanFactory = configContext.getBeanFactory();
Anyway SingletonBeanBuilder extends HierarchicalBeanFactory and HierarchicalBeanFactory extends BeanFactory
You don't need the Container because all of the runtime objects should be created, held and managed by ApplicationContext. Think about a web application, they are much the same. Each request contains external data/environment info as you mentioned above. What you need is a prototype/request scoped bean like ExternalData or EnvironmentInfo which can read and hold runtime data through a static way, let's say a static factory method.
<bean id="externalData" class="ExternalData"
factory-method="read" scope="prototype"></bean>
<bean id="environmentInfo" class="EnvironmentInfo"
factory-method="read" scope="prototype/singleton"></bean>
<bean class="RuntimeBean" scope="prototype">
<property name="externalData" ref="externalData">
<property name="environmentInfo" ref="environmentInfo">
</bean>
If you do need a container to save the runtime objects, code should be
class Container {
List list;
ApplicationContext context;//injected by spring if Container is not a prototype bean
public void load() {// no loop inside, each time call load() will load a runtime object
RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
list.add(bean);// do whatever
}
}
Official doc Singleton beans with prototype-bean dependencies.
It is possible to register beans dynamically by using BeanFactoryPostProcesor. Here you can do that while the application is booting (spring's application context has been initialized). You can not register beans latest, but on the other hand, you can make use of dependency injection for your beans, as they become "true" Spring beans.
public class DynamicBeansRegistar implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (! (beanFactory instanceof BeanDefinitionRegistry)) {
throw new RuntimeException("BeanFactory is not instance of BeanDefinitionRegistry");
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// here you can fire your logic to get definition for your beans at runtime and
// then register all beans you need (possibly inside a loop)
BeanDefinition dynamicBean = BeanDefinitionBuilder.
.rootBeanDefinition(TheClassOfYourDynamicBean.class) // here you define the class
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addDependsOn("someOtherBean") // make sure all other needed beans are initialized
// you can set factory method, constructor args using other methods of this builder
.getBeanDefinition();
registry.registerBeanDefinition("your.bean.name", dynamicBean);
}
#Component
class SomeOtherClass {
// NOTE: it is possible to autowire the bean
#Autowired
private TheClassOfYourDynamicBean myDynamicBean;
}
As presented above, you can still utilize Spring's Dependency Injection, because the post processor works on the actual Application Context.

Spring call a factory to autowire

Is there any way to have spring call a factory with a runtime parameter of the type of the parent class of a variable it is trying to autowire?
For example, let's say I have something like this:
interface IConfig {
}
interface IConfigProvider {
IConfig getConfig(Class<?> type)
}
class MyClass {
#Autowired
private IConfig _config;
}
Is there anyway to have spring, when autowiring MyClass._config to essentially call IConfigProvider.getConfig(MyClass.class) (well the concrete version that is in the context) at runtime to wire the variable?
I know I could autowire the factory and call it myself, I could even "hide" it in a base class but I am trying to avoid this.
NOTE: I am very new to Spring so if I am asking something really stupid/not using the right terminology, I apologise.
You would need to create a FactoryBean for that. Something like this should do the trick.
class IConfigFactoryBean implements FactoryBean<IConfig> {
#Autowired
private IConfigProvider configProvider;
private IConfig config;
#PostConstruct
public void initialize() {
config = configProvider.getConfig(...);
}
#Override
public IConfig getObject() throws Exception {
return config;
}
#Override
public Class<?> getObjectType() {
return ...;
}
#Override
public boolean isSingleton() {
return true;
}
}
I am not sure what the argument would be. Note that if you have several instances of IConfig you will need to qualify them as Spring won't be able to know which one it has to inject based on a simple #Autowired annotation. Check the javadoc of #Qualifier for more information.

Injection of #alternative bean with code, instead of beans.xml

We're testing an EJB with EJBContainer in JUnit. Another EJB called by the tested bean is mocked by the test with an #alternative bean. This mock bean is configured as <alternative> in beans.xml
Everything works as like a charm, the EJB is tested and uses the mocked service.
Question: Is it possible to do the same only with code, instead of using a beans.xml?
Of course a beans.xml gives good flexibility, though sometimes one might want to have a different #alternative for the same bean and the possibility to select one specific for a single/other test in the same project. Perhaps another solution would be a specific beans.xml for certain tests (with the question how to select it)?
Some of our test code (ExtensionMock is called by the tested EchoRemote implementation and part of the tests):
public class EchoTest {
private EJBContainer ejbContainer;
private Context ctx;
#Before
public void setUp() throws NamingException {
ejbContainer = EJBContainer.createEJBContainer();
ctx = ejbContainer.getContext();
}
#After
public void tearDown() {
ejbContainer.close();
}
#Test
public void testFindAll() {
try {
EchoRemote userEJB = (EchoRemote) ctx.lookup("java:global/ssb-ejb/Echo!examples.ssb.EchoRemote");
assertNotNull(userEJB);
assertEquals("Hello World", userEJB.echo("Hello World"));
} catch (NamingException e) {
throw new AssertionError(e);
}
}
}
<beans>
<alternatives>
<class>examples.ssb.EchoExtensionMock</class>
</alternatives>
</beans>
Instead of creating an independent #Alternative implementation you can extend an existing one and annotate it with #Specializes.
See also: http://docs.oracle.com/javaee/6/api/javax/enterprise/inject/Specializes.html
You may be able to do this with an extension, but I've never tried.
Leave the beans.xml alternative section empty. Annotate the test class with
#RunWith(org.jglue.cdiunit.CdiRunner.class)
#org.jglue.cdiunit.ActivatedAlternatives(EchoExtensionMock.class)
public class MyTest {
...
Or even better annotate the class with the same runner but produce an alternative mock:
#RunWith(org.jglue.cdiunit.CdiRunner.class)
public class MyTest {
#Produces
#org.jglue.cdiunit.ProducesAlternative
#org.mockito.Mock
private EchoExtensionMock echoExtension;
#Inject
private EchoRemote echoRemote;
#Test
public void test() throws Exception {
Mockito.when(echoExtension.someMethod()).thenReturn(new Object());
// here comes the testing code
}

NoSuchBeanDefinitionException for dependencies of mocked beans

I am attempting to use mocks in my integration test and am not having much luck. I am using Spring 3.1.1 and Mockito 1.9.0, and the situation is as follows:
#Component
public class ClassToTest {
#Resource
private Dependency dependency;
}
and
#Component
public class Dependency {
#Resource
private NestedDependency nestedDependency;
}
Now, I want to do an integration test of ClassToTest using Spring's JavaConfig. This is what I have attempted, and it doesn't work:
#Test
#ContextConfiguration
public class ClassToTestIntegrationTest {
#Resource
private ClassToTest classToTest;
#Resource
private Dependency mockDependency;
#Test
public void someTest() {
verify(mockDependency).doStuff();
// other Mockito magic...
}
#Configuration
static class Config {
#Bean
public ClassToTest classToTest() {
return new ClassToTest();
}
#Bean
public Dependency dependency() {
return Mockito.mock(Dependency.class);
}
}
}
I have simplified my setup to make the question easier to understand. In reality I have more dependencies and only want to mock some of them - the others are real, based on config imported from my prod #Configuration classes.
What ends up happening is I get a NoSuchBeanDefinitionException saying that there are no beans of type NestedDependency in the application context. I don't understand this - I thought Spring would receive Mockito's mocked instance of Dependency and not even look at autowiring it. Since this isn't working I end up having to mock my entire object graph - which completely defeats the point of mocking!
Thanks in advance for any help!
I had the same problem and I found another solution.
When Spring instantiate all your beans, it will check if it's a Mockito Mock and in this case, I return false for injection property. To use it, just inject it in a Spring context
Code below:
public class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
private static final MockUtil mockUtil = new MockUtil();
public MockBeanFactory() {
super();
}
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockUtil.isMock(bean);
}
}
What Mockito does when mocking classes is it creates a subclass using cglib having some fancy name like: Dependency$EnhancerByMockito (IIRC). As you probably know, subclasses inherit fields from their parent:
#Component
public class Dependency {
#Resource
private NestedDependency nestedDependency;
}
public class Dependency$EnhancerByMockito extends Dependency{
//...
}
This means Spring still sees the field in base class when presented with mock. What you can do:
Use interfaces, which will cause Mockito to employ dynamic proxies rather than CGLIB-generated classes
Mock NestedDependency - I know it will just cascade the problem one level further
Disable #Resource annotation scanning for tests

Categories

Resources