Dynamic context - autowiring - spring

I am dynamically adding contexts to the application context with the following code:
#Component
#Scope("singleton")
public class DynamicContextLoader implements ApplicationContextAware {
private static ApplicationContext context;
private Map<String, InterfacePropertyDto> contextMap;
#Autowired
IJpaDao jpaDao;
#PostConstruct
public void init() {
contextMap = (Map<String, InterfacePropertyDto>) context.getBean("contextMap");
contextMap.forEach((contextName, property) -> {
String p = jpaDao.getProperty(property.getPropertyName(), property.getPropertyType());
if (p != null) {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[]{"/META-INF/spring/integration/" + contextName},
false, context);
ctx.refresh();
}
});
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
}
This works well and all the beans defined in the new context are created. However the #Autowired does not work for any of these new beans.
For example a bean defined in the new context as:
<bean id="outboundContractJdbcFileMapper" class="com.......integration.model.contract.ContractMapper"/>
has the following autowiring:
public class ContractMapper implements RowMapper<ContractFile> {
#Autowired
IIntegrationDao integrationDao;
#Override
public ContractFile mapRow(ResultSet rs, int rowNum) throws SQLException {
......
}
}
At runtime the outboundContractJdbcFileMapper property integrationDao is null.
Is there a way to force the autowiring to occur when the beans are created? I was hoping that ctx.refresh() would do this.

That doesn't work automatically for the ClassPathXmlApplicationContext. You have to add <context:annotation-config/> to that child context as well:
<xsd:element name="annotation-config">
<xsd:annotation>
<xsd:documentation><![CDATA[
Activates various annotations to be detected in bean classes: Spring's #Required and
#Autowired, as well as JSR 250's #PostConstruct, #PreDestroy and #Resource (if available),
JAX-WS's #WebServiceRef (if available), EJB 3's #EJB (if available), and JPA's
#PersistenceContext and #PersistenceUnit (if available). Alternatively, you may
choose to activate the individual BeanPostProcessors for those annotations.
Note: This tag does not activate processing of Spring's #Transactional or EJB 3's
#TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
tag for that purpose.
See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext
for information on code-based alternatives to bootstrapping annotation-driven support.
]]></xsd:documentation>
</xsd:annotation>
</xsd:element>

Related

Spring Boot Custom AutoConfiguration and Autowire

I am creating a custom AutoConfiguration for Spring Boot. One of the features I was attempting to create was to create one or more Beans dynamically and adding them to the ApplicationContext at runtime.
The problem I ran into was with Autowiring. My #SpringBootApplication class autowires those beans, and since they do not exist yet, autowire fails.
My first solution was to put #Lazy on the autowire, and that solved my problem.
However, I ran into something interesting. I added two beans that I was looking for into the AutoConfiguration code, and of course, it worked. By accident, I only removed one of the beans and re-ran my code. It worked.
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("some_name")
private MyClass myClass;
#Autowired
#Qualifier("another_name")
private MyClass anotherClass;
...
}
#Configuration
public class MyAutoConfigurationClass {
#Bean(name="some_class")
public MyClass myClass () {
return null;
}
}
So the short of it is this. If I defined only one of the beans in my autoconfiguration class, this seems to satisfy Autowired and it does not blow up and when I dynamically add my other beans, both beans are found.
The stipulation is that the Autowired bean that is first, must be the bean that is defined in my autoconfiguration class.
I am running the following:
Spring Boot Starter 1.5.7-RELEASE
Various Spring Framework 4.3.11-RELEASE
Is this a bug? Or is this the way Autowired is supposed to work?
#SpringBootApplication
public class SpringBootDemoApplication {
#Autowired
#Qualifier("myclass")
private MyClass myClass;
#Autowired
#Qualifier("anotherMyClass")
private MyClass anotherMyClass;
...
}
#Configuration
public class MyAutoConfiguration {
private ConfigurableApplicationContext applicationContext;
private final BeanFactory beanFactory;
#Autowired
private MyClassFactory myClassFactory;
public MyAutoConfiguration(ApplicationContext applicationContext, BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
#PostConstruct
public void init() throws IOException, SQLException {
this.myClassFactory.create(this.applicationContext);
}
// without this #Bean definition SpringBoot will recieve the following error and stop
// AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization
#Bean(name="myClass")
public DataSource anyNameWillDoItDoesntMatter() {
return null;
};
}
#Component
class MyClassFactory {
public void create(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton(name, value);
}
}
So is this expected behavior of #Autowired?

#Autowired works but not #Inject

I have a Resource which injects the following class
#Component
public class CustomDozerBeanMapper implements Mapper {
private final DozerBeanMapper beanMapper;
public CustomDozerBeanMapper() {
this.beanMapper = new DozerBeanMapper();
BeanMappingBuilder builder = new BeanMappingBuilder() {
protected void configure() {
//some mapping stuff
}
};
beanMapper.addMapping(builder);
}
#Override
public <T> T map(Object o, Class<T> aClass) throws MappingException {
return beanMapper.map(o, aClass);
}
#Override
public void map(Object o, Object o1) throws MappingException {
beanMapper.map(o, o1);
}
#Override
public <T> T map(Object o, Class<T> aClass, String s) throws MappingException {
return beanMapper.map(o, aClass, s);
}
#Override
public void map(Object o, Object o1, String s) throws MappingException {
beanMapper.map(o, o1, s);
}
}
In my applicationContext.xml I have declared
<context:annotation-config/>
<context:component-scan base-package="foo.bar"/>
<bean id="customDozerMapper" class="foo.bar.CustomDozerBeanMapper" />
Then in our resource I inject it
class SomeResource {
#Inject CustomDozerMapper customDozerMapper;
//We have loads of other Injects which work just fine, only this class has problems
}
Caused by: A MultiException has 1 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CustomDozerBeanMapper,parent=SomeResource,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1098507248)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:75)
at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:947)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:975)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:965)
at org.glassfish.jersey.server.spring.SpringComponentProvider$SpringManagedBeanFactory.provide(SpringComponentProvider.java:191)
at org.jvnet.hk2.internal.FactoryCreator.create(FactoryCreator.java:153)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
at org.jvnet.hk2.internal.PerLookupContext.findOrCreate(PerLookupContext.java:70)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2072)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:761)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:700)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:284)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:74)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:109)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
Now if I change and use #Autowired, it works fine
We are using Spring for dependency management, but for some reason h2k is being used, and I get the following exception
Can anyone please explain what the problem might be?
Why does it work with #Autowired and not #Inject
Why is h2k being used, and not Spring?
Probably, the problem may be because of 2 bean declarations (one in the XML configuration and the another one with #Component) and a DI container couldn't able to pick up one of them.
All solutions that are available here:
removing one of the bean definitions (I'd prefer the XML one)
specifying a bean by the #Qualifier or #Named annotation
The problem might also be due to the bean name in config file customDozerMapper and actual injection customerDozerMapper are not matching. If #inject does not find matching bean, it throws an exception. However, there is provision for #autowired wherein you can set attribute required=false and it injects null if it does not find matching bean.
Note: Configuration in config xml overrides the annotation
configuration.

how to get beans from application context loaded by contextloaderlistener?

I'm new to Spring/Spring Mvc and here's my problem. In my webapp, besides the spring-servlet.xml , I have a jdbc.xml which define beans like datasource, dao ... Before using contextloaderlistener , I load my jdbc.xml inside the Controller's constructor like this ApplicationContext context = new ClassPathXmlApplicationContext("jdbcbeans.xml") then get the beans from that. But since I'm using contextloaderlistener to load the file, how can I get the reference to the context ? I was able to set up everything using those #Autowired things but I just want to know is there any way to do that ?
You can use WebApplicationContextUtils.
ApplicationContext context;
context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
See here for details.
You can do the following to get an instance of Application Context
in case of a container managed bean use ApplicationContextAware interface
public class MyBean implements ApplicationContextAware {
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext acontext) throws BeansException {
context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
Or you can write the following
#Autowired
private ApplicationContext Context;
An instance of the Application Context will be autowired.

Why Spring #Autowired ApplicationContext appContext is null?

I have Spring bean with annotations:
#Named
#Scope("session")
And this bean property:
#Autowired
ApplicationContext appContext;
The Spring configuration file has entry (that works for other anotations/injections):
<context:component-scan base-package="my.package.name" />
Why appContext is null after such code and configuration?
I am trying to get ApplicationContext (to call getBean(...) on it) and this can be quite involved task (judging from other discussions) in previous Spring versions (e.g. one is required to get ServletContext in Spring web application to create ApplicationContext and getting ServletContext can be quite involved task for beans that don't directly access HTTP Request objects). In Spring 3.x, as I understand, simple #Autwired injection can be used. How AppContext can be accessed?
Here the first problem is you are using #Named which is Java EE annotation and as for as I know Spring yet to support Java EE annotations. Hence instead of using #Named try to use Spring annotation #Service, #Component, #Repository etc.
Here is the example for you I have used JSF Managed bean as well to show how to integrate beans.
#ManagedBean(name="myBacking")
#RequestScoped
public class MyBacking {
private String myText;
#ManagedProperty(value="#{mySpring}")
MySpringBean mySpring;
public String getMyText() {
myText = mySpring.getText();
return myText;
}
public void setMyText(String myText) {
this.myText = myText;
}
public MySpringBean getMySpring() {
return mySpring;
}
public void setMySpring(MySpringBean mySpring) {
this.mySpring = mySpring;
}
}
#Service("mySpring")
#Scope("request")
public class MySpringBean {
#Autowired
MySecond mySecond;
public String getText(){
return "Hello KP" + mySecond.appObj();
}
}
#Service
#Scope("request")
public class MySecond {
#Autowired
ApplicationContext applicationContext;
public String appObj(){
MyThrid mythird =(MyThrid)applicationContext.getBean("myThrid");
return "My Second Bean calld "+ mythird.getTxt();
}
}
#Service
public class MyThrid {
public String getTxt(){
return "from thrid Bean";
}
}

Spring is ignoring #Transactional annotations in Apache Shiro Realm class

I am using Spring for IOC and transaction management, and am planning to use Apache Shiro as the security library.
Whenever I want to check a user's permissions, I call subject.isPermitted("right"), whereupon Shiro checks for the permission using a datastore. Within these calls, a database connection is established, and I have annotated the method with #Transactional. However, I always receive an error that there is no Hibernate session bound to the thread whenever I execute the permission check.
The method is in the Realm class. I defined a custom Shiro Realm class:
#Component
public class MainRealm extends AuthorizingRealm {
#Autowired
protected SysUserDao userDao;
#Transactional
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
...
final SysUser user = this.userDao.findByUsername(un);
...
return authInfo;
}
#Transactional
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
...
permissions = this.userDao.getAccessRights(un);
...
return authInfo;
}
}
Apache Shiro uses a Servlet Filter, so I have the following defined in web.xml:
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
I am using programmatic configuration for Spring. Here is my App Config class:
#Configuration //Replaces Spring XML configuration
#ComponentScan(basePackages = "com.mycompany")
#EnableTransactionManagement //Enables declarative Transaction annotations
public class SpringAppConfig {
#Bean
public DataSource sqlServerDataSource() throws Exception {...}
#Bean
#Autowired
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {...}
#Bean
public AnnotationSessionFactoryBean getSessionFactory() throws Exception {...}
#Bean
public static PersistenceExceptionTranslationPostProcessor exceptionTranslation() {...}
#Bean
#Autowired
public DefaultWebSecurityManager securityManager(MainRealm mainRealm) {
final HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(shiroHash);
hcm.setHashIterations(shiroIter);
hcm.setStoredCredentialsHexEncoded(shiroHexEncoded);
mainRealm.setCredentialsMatcher(hcm);
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(mainRealm);
return sm;
}
#Bean
#Autowired
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
final ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(securityManager);
return filter;
}
/**
* This method needs to be static due to issues defined here:<br>
* https://issues.apache.org/jira/browse/SHIRO-222
*/
#Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
LifecycleBeanPostProcessor lbpp = new LifecycleBeanPostProcessor();
return lbpp;
}
#Bean
#DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
#Bean
#Autowired
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager secMan) {
AuthorizationAttributeSourceAdvisor advBean = new AuthorizationAttributeSourceAdvisor();
advBean.setSecurityManager(secMan);
return advBean;
}
}
To summarize the issue, I believe my MainRealm class is being wired properly (it has an #Autowired dependency to a DAO object and I verified that it is not null) with the exception of the #Transactional annotation. Due to this, I cannot directly call user.isPermitted("") as it prompts an error: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
Would like to ask for help in checking whether I missed anything in my Spring configuration.
In the meantime I have hacked over this issue by calling the user.isPermitted("") function within a method in my Service class that's correctly bound by a #Transactional.
EDIT When I checked the logs for Spring initialization I can see this:
Bean 'mainRealm' of type [class com.x.security.MainRealm] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
According to this SO answer it means MainRealm isn't being postprocessed by the transaction manager therefore any #Transactional annotations get ignored. If so, how can I correct this?
EDIT 2 According to this SO question: "In other words, if I write my own BeanPostProcessor, and that class directly references other beans in the context, then those referenced beans will not be eligible for auto-proxying, and a message is logged to that effect." I just checked ShiroFilterFactoryBean and it is in fact a BeanPostProcessor. And the problem is it requires a SecurityManager instance that in turn requires a MainRealm instance. So both beans are autowired and are thus ineligible for proxying. I feel like I'm closer to the solution but I still can't resolve it.
The root cause of the issue is in fact due to the following:
All BeanPostProcessors and their directly referenced beans will be instantiated on startup... Since AOP auto-proxying is implemented as a BeanPostProcessor itself, no BeanPostProcessors or directly referenced beans are eligible for auto-proxying (and thus will not have aspects 'woven' into them.
Referenced SO question is here.
I have resolved this issue by decoupling the Realm bean creation from the SecurityManager bean creation.
The relevant change is from the following code:
#Bean
#Autowired
public DefaultWebSecurityManager securityManager(MainRealm mainRealm) {
final HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(shiroHash);
hcm.setHashIterations(shiroIter);
hcm.setStoredCredentialsHexEncoded(shiroHexEncoded);
mainRealm.setCredentialsMatcher(hcm);
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
sm.setRealm(mainRealm);
return sm;
}
to the following code:
#Bean
public DefaultWebSecurityManager securityManager() {
final DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
//sm.setRealm(mainRealm); -> set this AFTER Spring initialization so no dependencies
return sm;
}
Then I use a ServletContextListener which listens to when the Spring context initialization completes and I have the both the MainRealm and SecurityManager beans. Then I just plug one bean inside another.
#Override
public void contextInitialized(ServletContextEvent sce) {
try {
//Initialize realms
final MainRealm mainRealm = (MainRealm)ctx.getBean("mainRealm");
final DefaultWebSecurityManager sm = (DefaultWebSecurityManager)ctx.getBean("securityManager");
sm.setRealm(mainRealm);
} catch (Exception e) {
System.out.println("Error loading: " + e.getMessage());
throw new Error("Critical system error", e);
}
}
#Transactional annotation can be used before :
An interface def
An interface method
A class def
A PUBLIC method of a class
As explained in the documentation
The fact that your method is protected must be the reason of your problem, and your service method was maybe declared as public which would explained why it worked in that case

Resources