Grails initialization - spring

In my Grails app, I need access to configuration exposed by a Java class similar to the below
public class Config {
private Properties properties = new Properties();
static load(String path) {
File configFile = new File(path);
FileReader fileReader = new FileReader(configFile);
properties.load(fileReader);
}
String getProperty(String name) {
properties.getProperty(name);
}
}
I trigger the initialisation of this class in the first line of Bootstrap.groovy by calling Config.load("/conf.properties"). However, the initialization of various Spring beans needs properties that are exposed by Config, but by the time Bootstrap.groovy is executed, Spring initialization has already completed.
So I need to find a way to call Config.load() before construction of the Spring beans, is this possible? I guess there might be an event handler available in /script/_Events.groovy that I could invoke it from, but I'm not sure which handlers are available.
Unfortunately, changing the source code of Config.java isn't an option, and neither is eliminating my usage of this class.

You could try declaring a suitable bean in web-app/WEB-INF/applicationContext.xml, which is the definition of the root web application context as opposed to the GrailsApplication's internal context.
<bean id="initConfig" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="com.example.Config" />
<property name="targetMethod" value="load" />
<property name="arguments">
<list><value>/conf.properties</value></list>
</property>
</bean>
and modify the grailsApplication bean to depend on that:
<bean id="grailsApplication" depends-on="initConfig" class="...">

Related

Convert XML bean definition with parent to Java with annotations

I have an application using a framework that provides certain Spring beans via XML files in the framework. The configuration of my application is currently done partly in XML but mostly with Spring annotations.
Some of the XML bean definitions have parents referring to beans supplied by the framework, e.g.
<bean id="MyBean" parent="FrameworkBean">
<property name="context">
<map merge="true">
<entry key="SomeKey" value-ref="SomeValue" />
</map>
</property>
</bean>
FramwworkBean is defined in an XML file in the framework. There is a chain of bean inheritance. At each step some entries are added to the context:
<bean id="FrameworkBean" parent="AbstractBean">
<map merge="true">...
<bean id="AbstractBean" abstract="true" class="ClassWithContext"/>
I understand the result of all this is construction of a ClassWithContext
instance with a map containing all the entries up the chain.
Is it possible to write Java code to do the same, without duplicating code from the framework XML files?
#Bean("MyBean") ClassWithContext myBean() {
return ??? // code that uses "FrameworkBean" somehow
}
The XML bean definition has no dependency on the type of AbstractBean.
If MyBean can be created by Java code, can that code be written to be equally type-agnostic? Or should I just leave this in XML?
If your "FrameworkBean" is not abstract bean you can try the following:
#Bean
public SomeType myBean(#Qualifier("FrameworkBean") FrameworkBeanType frameworkBean) {
SomeType type = getSomeType();
type.setFrameworkBean(frameworkBean);
return type;
}

Spring transaction propogation on non spring managed beans

I am working on a migration project which involves upgrading the platform to Spring 4 with MyBatis. In the legacy code, transactions are handled at a central locations wherein call to start/end transactions are spread across various classes like service class, helper class and DAO class.
I managed to convert all service classes to spring managed component and DAO classes to support MyBatis-spring API. Problem is my service class use several other classes to perform a function and those classes are all instantiated manually and used. Now if i start a transaction on service class methods and perform database transactions inside other helper or DAO classes which are not spring managed, my transaction handling doesn't work correctly. I have illustrated this problem in the below code. Could you tell what are the ways to acheive transaction handling without modifying the code?
Example :
package com.service;
#Service
class MyService {
#Transactional( propagation=Propagation.REQUIRED)
public void processRequest () {
HelperClass helper = new HelperClass();
helper.performOperation();
}
}
package com.helper;
// this class is not spring bean
class HelperClass {
// MyBatis mapper class
private EmployeeMapper mapper;
public HelperClass () {
mapper = // retrieve mapper class bean from spring context
}
public performOperation () {
// call to mapper class insert operation
// call to mapper class update operation
}
}
package com.dao;
#Component
interface EmployeeMapper {
// method definition to perform database operation
}
Spring configuration details:
<context:component-scan base-package="com" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
....
....
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" />
<mybatis:scan base-package="com.dao" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath*:mybatis/*.xml" />
</bean>
In the above code HelperClass.performOperation() method is doing 2 database operations (insert,update). Say if insert succeeds and update fails, my database transaction doesn't get rollback. Since I already started the transaction at MyService.processRequest() should this not rollback the operations that are carried inside that method call? Correct me if my understanding is wrong.

Spring + Hibernate How to use AspectJ to control transaction

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.
So I want to use AspectJ, but I can not get session by sessionFactory.getCurrentSession(), which leads to following Exception.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have added VM arguments at runtime.
-javaagent:/Users/xx/workStation/workspace/SpringHibernate/src/lib/spring-instrument-3.2.8.RELEASE.jar
Here is configuration file
aop.xml
<aspectj>
<weaver options="-Xset:weaveJavaxPackages=true -showWeaveInfo">
</weaver>
beans.xml
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref = "dataSource"/>
<property name="mappingResources">
<list>
<value>config/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=false
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref = "sessionFactory"/>
<property name="nestedTransactionAllowed" value = "true"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="txManager" />
<context:load-time-weaver />
Thank you for your answers.
If I removed aop.xml I got a warning:
warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified.
else I got following info
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(java.util.List com.yonghui.sh.service.impl.UserServiceBean.getUsers())' in Type 'com.yonghui.sh.service.impl.UserServiceBean' (UserServiceBean.java:106) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(void com.yonghui.sh.service.txtest.TxTestB.txC())' in Type 'com.yonghui.sh.service.txtest.TxTestB' (TxTestB.java:32) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
.
I think classes are weaved. I just use JUnit to run some tests not in web context.
It seems that the problem is "static".
static UserService userService;
static TxTest txTest;
static TxTestX txTestx;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
userService = (UserService)ac.getBean("UserService");
txTest = (TxTest)ac.getBean("txTestA");
txTestx = (TxTestX)ac.getBean("txTestX");
}
If I don't use JUnit it works well.
public class TxTestMain {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
TxTest txTestA = (TxTest)ac.getBean("txTestA");
txTestA.txA();
}
}
How comes??
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional.
I dont know if it is a problem here But a bean can invoke its own method through proxy. Just implement in your bean interfaces like ApplicationContextAware and BeanNameAware and having this two you can ask the context about the bean where you actually are and cal any method through proxy (in transaction). This of course work only when the scope is SINGLETON if it is PROTOTYPE there are also some ways to do that through API.

Ant task for Spring Validation

I need an ANT task to validate spring configuration. I need to find problems at build time before runtime ? For example, In spring context file contains a property a bean, but this bean doesnt have this property.
In eclipse, there is a tool Spring Explorer that do this validation.
thanks,
org.springframework.web.context.ContextLoaderListener failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'readController' defined in class path resource [applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'productOperations' of bean class [com.bee.view.json.ReadController]: Bean property 'productOperations' is not writable or has an invalid setter method.
Does the parameter type of the setter match the return type of the getter?.
An easy way to ensure that your context is valid would be to create a JUnit test, which loads the context. Using the spring-test.jar support classes makes that easy:
public class MyTest extends AbstractDependencyInjectionSpringContextTests {
// this will be injected by Spring
private QueryDao queryDao;
private MyBusinessObject myBusinessObject;
// ensure that spring will inject the objects to test by name
public MyTest () {
setAutowireMode(AUTOWIRE_BY_NAME);
}
#Override
protected String[] getConfigLocations() {
return new String[] { "applicationContextJUnit.xml" };
}
public void testQueryDao() {
List<SomeData> list = queryDao.findSomeData();
assertNotNull(list);
// etc
}
public void testMyBusinessObject() {
myBusinessObject.someMethod();
}
public void setQueryDao(QueryDao queryDao) {
this.queryDao = queryDao;
}
}
The problem with loading a context that is used in a web application is that JUnit does not necessarily have access to the same resources (e.g. JNDI data sources), so if you've got the following in your "applicationContext.xml":
<beans ...>
<bean id="myBusinessObject" class="com.test.MyBusinessObject">
<property name="queryDao" ref="queryDao"/>
</bean>
<bean id="queryDao" class="com.test.QueryDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<jee:jndi-lookup
id="dataSource"
jndi-name="jdbc/mydatasource"
resource-ref="true"
cache="true"
lookup-on-startup="false"
proxy-interface="javax.sql.DataSource"/>
</beans>
and your "applicationContextJUnit.xml" would import your "real" application context and redefine resources:
<beans ...>
<import resource="classpath:applicationContext.xml"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:..."/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
</beans>
That way your unit tests will load the application context (even the ones that you don't explicitly test in your unit test), and you can have the confidence that your context is correct, because Spring itself loaded it. If you have an error, then the unit tests will fail.

Spring's overriding bean

Can we have duplicate names for the same bean id that is mentioned in the XML?
If not, then how do we override the bean in Spring?
Any given Spring context can only have one bean for any given id or name. In the case of the XML id attribute, this is enforced by the schema validation. In the case of the name attribute, this is enforced by Spring's logic.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behaviour depends on the ordering of the files when they get loaded by the context.
So while it's possible, it's not recommended. It's error-prone and fragile, and you'll get no help from Spring if you change the ID of one but not the other.
I will add that if your need is just to override a property used by your bean, the id approach works too like skaffman explained :
In your first called XML configuration file :
<bean id="myBeanId" class="com.blabla">
<property name="myList" ref="myList"/>
</bean>
<util:list id="myList">
<value>3</value>
<value>4</value>
</util:list>
In your second called XML configuration file :
<util:list id="myList">
<value>6</value>
</util:list>
Then your bean "myBeanId" will be instantiated with a "myList" property of one element which is 6.
Not sure if that's exactly what you need, but we are using profiles to define the environment we are running at and specific bean for each environment, so it's something like that:
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="originalValue" />
</bean>
<beans profile="DEV, default">
<!-- Specific DEV configurations, also default if no profile defined -->
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="overrideValue" />
</bean>
</beans>
<beans profile="CI, UAT">
<!-- Specific CI / UAT configurations -->
</beans>
<beans profile="PROD">
<!-- Specific PROD configurations -->
</beans>
So in this case, if I don't define a profile or if I define it as "DEV" myBean will get "overrideValue" for it's name argument. But if I set the profile to "CI", "UAT" or "PROD" it will get "originalValue" as the value.
An example from official spring manual:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
Is that what you was looking for?
Updated link
Since Spring 3.0 you can use #Primary annotation. As per documentation:
Indicates that a bean should be given preference when multiple
candidates are qualified to autowire a single-valued dependency. If
exactly one 'primary' bean exists among the candidates, it will be the
autowired value. This annotation is semantically equivalent to the
element's primary attribute in Spring XML.
You should use it on Bean definition like this:
#Bean
#Primary
public ExampleBean exampleBean(#Autowired EntityManager em) {
return new ExampleBeanImpl(em);
}
or like this:
#Primary
#Service
public class ExampleService implements BaseServive {
}
Another good approach not mentioned in other posts is to use PropertyOverrideConfigurer in case you just want to override properties of some beans.
For example if you want to override the datasource for testing (i.e. use an in-memory database) in another xml config, you just need to use <context:property-override ..."/> in new config and a .properties file containing key-values taking the format beanName.property=newvalue overriding the main props.
application-mainConfig.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.postgresql.Driver"
p:url="jdbc:postgresql://localhost:5432/MyAppDB"
p:username="myusername"
p:password="mypassword"
destroy-method="close" />
application-testConfig.xml:
<import resource="classpath:path/to/file/application-mainConfig.xml"/>
<!-- override bean props -->
<context:property-override location="classpath:path/to/file/beanOverride.properties"/>
beanOverride.properties:
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:MyTestDB
Whether can we declare the same bean id in other xml for other reference e.x.
Servlet-Initialize.xml
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
Other xml (Document.xml)
<bean id="inheritedTestBean" class="org.springframework.beans.Document">
<property name="name" value="document"/>
<property name="age" value="1"/>
</bean>
Question was more about XML but as annotation are more popular nowadays and it works similarly I'll show by example.
Let's create class Foo:
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and two Configuration files (you can't create one):
#Configuration
public class Configuration1 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration1");
return foo;
}
}
and
#Configuration
public class Configuration2 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration2");
return foo;
}
}
and let's see what happens when calling foo.getName():
#SpringBootApplication
public class OverridingBeanDefinitionsApplication {
public static void main(String[] args) {
SpringApplication.run(OverridingBeanDefinitionsApplication.class, args);
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
Configuration1.class, Configuration2.class);
Foo foo = applicationContext.getBean(Foo.class);
System.out.println(foo.getName());
}
}
in this example result is: configuration2.
The Spring Container gets all configuration metadata sources and merges bean definitions in those sources. In this example there are two #Beans. Order in which they are fed into ApplicationContext decide. You can flip new AnnotationConfigApplicationContext(Configuration2.class, Configuration1.class); and result will be configuration1.

Resources