Access Spring bean definition properties at runtime for logging - spring

I want to be able to read the properties from certain bean definitions that are configured in my Spring configuration file (i.e. the one set by contextConfigLocation in my web.xml) at run time. I want to do this so that I can log property values so that I when I receive the log files for diagnosis I can see how the system integrators have set up the application.
Looking at the Spring debug logs I can see that they are read from the config file by the class XmlBeanDefinitionReader. I'm guessing there is a way that Spring provides for accessing the resulting bean definitions, but I can't find it.
By way of example these are the sort of bean definitions that for which I would like to read configurations.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/an_example_db</value>
</property>
<property name="user">
<value>exampleuser</value>
</property>
<property name="password">
<value>examplepwd</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="packagesToScan" value="com.my.example.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.id.new_generator_mappings">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.query.substitutions">true 1, false 0, yes 'Y', no 'N'</prop>
</props>
</property>
</bean>
Please note that these are examples only. I'd like to be able to read properties for any bean definition if that is possible.

You can create a bean that plugs in the post-construction phase of all beans created at which point you can perform you logging. This can be done by a bean that implements BeanPostProcessor.
Example:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
#Component
public class BeanPostProcessorAuditChecker implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean: " + beanName + " initialized with props: " + bean);
return bean;
}
}
Things to note:
This bean can be defined using annotations (as depicted above) or XML config as usual.
The interface is intercepting all bean constructions.
The interface method implementations are expecting you to return the bean object.
In my example I just System.out.println but you really want to SLF4J or otherwise log. Also I'm trusting that the toString() method is implemented properly exposing the properties you are interested in.
If you want to filter the logging to happen only on a subset of beans and not for all beans you'll have to do that yourself in the body of the method with reflection
Snippet:
if(bean instanceof DataSource ||
bean instanceof SessionFactory) { log.debug("{}",bean); }
Since your business domain is not known some alternatives provided:
If you can pointcut your domain you want to plugin AOP is another alternative proxy approach.
You could expose JMX on the beans and consume externally (decoupled approach)
Another decoupled approach is publishing Events using ApplicationEventPublisher. This is fyi since not advisable in your case - you are interested in construction only state properties.
Before Edit
The beans you are demonstrating are 1) singletons (by virtue of Spring default scope) and 2) properties are not likely to be changing after bean setup (via set method calls).
Under these assumptions why aren't you amassing these properties values in a props file
database.driverClass=com.mysql.jdbc.Driver
database.user=exampleuser
...
inject them in the context definition
...
<property name="user" value="#{database.user}"/>
...
and use the PropertyPlaceholderConfigurer to check the values.

Related

Spring Transaction configuration programmatically with Spring 4.3.4 version

We are currently migrating from xml configuration to complete annotation with java configuration based Spring application. With the annotation approach #Transactional we can achieve but the we need to write for each and every method.
In XML we configured (OLD).
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="update*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="save*">PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED</prop>
<prop key="get*">PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly</prop>
<prop key="is*">PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly</prop>
<!--<prop key="*">PROPAGATION_REQUIRED</prop> -->
</props>
</property>
</bean>
transactionManager is org.springframework.orm.hibernate3.HibernateTransactionManager
<bean id="xxxxSVC" parent="txProxyTemplate">
<property name="target">
<bean class="XXX.XXX.XXX.SVCImpl">
<property name="xxxxDao" ref="xxxDao"></property>
</bean>
</property>
</bean>
txProxyTemplate is parent class of each service class.
So, please suggest how to configure similar code in java configuration. Thanks for your valuable time spent and support us.
#Barath Comment
Bean
#Bean
public TransactionProxyFactoryBean setTransactionProperties() throws IOException {
TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
transactionProxyFactoryBean.setTransactionManager(transactionManager(sessionFactory()));
Properties transactionAttributesProps = new Properties();
transactionAttributesProps.setProperty("delete*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("update*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("save*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
transactionAttributesProps.setProperty("get*", "PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly");
transactionAttributesProps.setProperty("is*", "PROPAGATION_SUPPORTS,ISOLATION_READ_COMMITTED,readOnly");
transactionProxyFactoryBean.setTransactionAttributes(transactionAttributesProps);
transactionProxyFactoryBean.afterPropertiesSet();
return transactionProxyFactoryBean;
}
How to configure with each service implementation class, we can use it for single as a service layer may contain N classes. There is a method setTarget(Object target). Now how can we configure all the N classes. Please sugggest how can we configure.
A sample configuration for this case :
#Bean
public TransactionProxyFactoryBean txProxyTemplate(){
TransactionProxyFactoryBean txFactory=new TransactionProxyFactoryBean();
txFactory.setTransactionManager(new JpaTransactionManager()); // any transcation manager
txFactory.setTransactionAttributes(properties());
return txFactory;
}
#Bean
Properties properties(){
Properties properties=new Properties();
properties.put("delete*", "PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED");
//set al lthe properties
return properties;
}
#Bean
public TransactionProxyFactoryBean xxxxSVC(TransactionProxyFactoryBean txFactory){
txFactory.setTarget(testEntity());
return txFactory;
}
#Bean
TestEntity testEntity(){
return new TestEntity();
}

Bean property 'persistenceManagerFactory' is not writable or has an invalid setter method

I am running maven jdo project. And i want to use annotational transactions so i have set my dispatcher according to this but i am getting this error following error.
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'myController': Injection of autowired dependencies
failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.titas.dao.UserDAO
com.titas.controller.MyController.userDAO; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'userDAO' defined in ServletContext resource [/WEB-
INF/dispatcher-servlet.xml]: Error setting property values; nested
exception is org.springframework.beans.NotWritablePropertyException:
Invalid property 'persistenceManagerFactory' of bean class
[com.titas.dao.UserDAOImpl]: Bean property 'persistenceManagerFactory'
is not writable or has an invalid setter method. Does the parameter type
of the setter match the return type of the getter?
My dispatcher.xml is:
<!-- declare mvc to be annotation driven -->
<mvc:annotation-driven/>
<!-- provide Your Base package to scan annotations for components -->
<context:component-scan base-package="com.titas.controller"></context:component-scan>
<mvc:resources location="/resources/" mapping="/resources/**"/>
<!-- enable the configuration of transactional behavior based on annotations-->
<tx:annotation-driven transaction-manager="txManager"/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<!--
Most controllers will use the ControllerClassNameHandlerMapping above, but
for the index controller we are using ParameterizableViewController, so we must
define an explicit mapping for it.
-->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="index">indexController</prop>
</props>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
<!--The index controller.-->
<bean name="indexController"
class="org.springframework.web.servlet.mvc.ParameterizableViewController"
p:viewName="index" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/login"
p:username="root"
p:password="" />
<!-- PMF Bean -->
<bean id="myPmf" class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="jdoPropertyMap">
<props>
<prop key="javax.jdo.PersistenceManagerFactoryClass">org.datanucleus.api.jdo.JDOPersistenceManagerFactory</prop>
<prop key="javax.jdo.option.ConnectionDriverName">com.mysql.jdbc.Driver</prop>
<prop key="javax.jdo.option.ConnectionURL">jdbc:mysql://localhost:3306/login</prop>
<prop key="javax.jdo.option.ConnectionUserName">root</prop>
<prop key="javax.jdo.option.NontransactionalRead">true</prop>
<prop key="javax.jdo.option.RetainValues">false</prop>
<prop key="javax.jdo.option.DetachAllOnCommit">true</prop>
<prop key="javax.jdo.option.Multithreaded">true</prop>
<prop key="datanucleus.appengine.ignorableMetaDataBehavior">NONE</prop>
</props>
</property>
</bean>
<bean id="pmfProxy" class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
<property name="targetPersistenceManagerFactory" ref="myPmf"/>
<property name="allowCreate" value="false"/>
</bean>
<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceManagerFactory" ref="pmfProxy"/>
</bean>
<bean id="userDAO" class="com.titas.dao.UserDAOImpl" >
<property name="persistenceManagerFactory" ref="pmfProxy"/>
</bean>
UserDAOImpl.java
#Repository
public class UserDAOImpl implements UserDAO{
static Logger log = Logger.getLogger(UserDAOImpl.class.getName());
#Autowired
#Qualifier("dataSource")
private DataSource dataSource;
#Autowired
#Qualifier("myPmf")
private PersistenceManagerFactory persistenceManagerFactory;
HttpServletRequest request;
#Override
#Transactional
public User getUser(String user_name, String user_password) {
PersistenceManager pm = this.persistenceManagerFactory.getPersistenceManager();
try {
User user=new User();
pm.makePersistent(user);
Query query = pm.newQuery(User.class, "this.userName == userNameParam && this.userPassword==userPasswordParam");
query.setFilter("this.userName == userNameParam && this.userPassword==userPasswordParam");
query.declareParameters("String lastNameParam, String userPasswordParam");
log.info("hereeee 5");
log.info("hereeee again 5");
user = (User)query.execute(user_name,user_password);
log.info("hereeee 6");
log.info("hereeee again 6");
log.info(user.getUserEmail()+"..........."+user.getUserProfileName());
return user;
}
finally {
pm.close();
}
}
MyController.java
#Controller
//#RequestMapping(value = "/test")
public class MyController{
static Logger log = Logger.getLogger(MyController.class.getName());
#Autowired
private UserDAO userDAO;
List<User> allUser = new ArrayList<User>();
May i know where i need to change.
Thanks in advance.
Your bean definition is:
<bean id="userDAO" class="com.titas.dao.UserDAOImpl" >
<property name="persistenceManagerFactory" ref="pmfProxy"/>
</bean>
In your UserDaoImpl you havent defined a method called setPersistenceManagerFactory which Spring is complaining about.
By above bean definition, Spring is going to inject factory object within user dao via setter method which is not present and hence you see exception.
So instead of:
#Qualifier("myPmf")
You should use bean name for proxy factory object (although i don't see problem using myPmf, it's just what you tries to inject property using <property name="persistenceManagerFactory" ref="pmfProxy"/> in userDao bean and hence this suggestion and you could ignore this) like:
#Qualifier("pmfProxy")
And remove the line which tries to inject property when you say autowired:
<bean id="userDAO" class="com.titas.dao.UserDAOImpl" />
from your bean definition for userDAO as Spring is going to inject it for you.
If you want to manually specify the dependency and inject it via setter then you would need to define setter method like:
.. setPersistenceManagerFactory(..) {
....
}

Spring Hibernate "No Current Transaction" for getCurrentSession method

I am using the Spring configuration to test Spring-Hibernate Transactions.
<beans ...>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- hibernate 4 onwards annotationsessionfactorybean is replaced with localsessionfactory bean -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.fg.arch.test.transaction.Foo</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<!-- <prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</prop> -->
</props>
</property>
</bean>
</beans>
My service layer is annotated with #Transactional.
This is my DAO:
public class FooHibernateDaoImpl implements FooDao {
private SessionFactory sessionFactory;
public void testFoo(Foo foo) throws Throwable {
System.out.println(" --- ");
sessionFactory.openSession().save(foo);
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
}
Explicitly opening the session using the openSession() method does not cause a problem however when I change to getCurrentSession() I am getting an exception.
I have two questions.
Is it good practice to call openSession() in every DAO method.
How can I make getCurrentSession() work so that it will not give me an exception like no active transaction present ?
Thanks.
To answer your questions:
No, its not. The #Transactional annotation that should be on your service class method calling testFoo() is opening the session for you. You should use getCurrentSession() in the DAO to get this session.
You can, but you shouldn't. That's the entire point of using the Hibernate SessionFactory with annotation based transaction management. As long as you are marking your service methods transactional, you shouldn't have a problem.
As a side note, why are you not Autowiring your SessionFactory? Don't use setters to set something that should be Autowired. Otherwise you may as well not use Spring.

Is JTA manager necessary to use transaction features in hibernate 4

I'm following the tutorial here:
http://www.javacodegeeks.com/2013/05/hibernate-4-with-spring.html
to enable the "#Transactional annotation" in my Java web application but failed to make it run properly. Please advise if the JTA manager is really required, and why?
Please note that my webapp is based on Spring 3 + Hibernate 4 + Tomcat 7.
Background and my doubts:
My current web application uses my own custom class (implements HandlerInterceptor) to enable one-hibernatesession-per-request basis. Now I want to improve my application's maintainability by using the "#Transactional annotation" instead since that could save many lines of code.
According to my understanding, the #Transactional basically relies on the AOP concept to ensure the session (Hibernate session) is ready for use in the annotated method. This seems nothing to do with the JTA. But I wonder why can't I make it work on my webapp in Tomcat 7 (without JTA-provider).
After few searches on google, it looks like the JTA is required. This confuses me since this seems to be a very basic functionality that shouldn't have the complicated JTA-provider as a requirement.
Here is the error I got:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
...
This is the code I use for testing:
....
#Autowired
org.hibernate.SessionFactory sessionFactory;
#Transactional
#RequestMapping(method = RequestMethod.GET)
protected String home() {
Session session = sessionFactory.getCurrentSession(); // I expected the session is good to use now
Province p = (Province) session.get(Province.class, 1L); // This causes no session found error :(
return "home";
}
The spring XML:
....
<tx:annotation-driven/>
<context:component-scan base-package="..."/>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/..."/>
<property name="lookupOnStartup" value="true"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor" class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
....
Thank you !
Just a speculation:
Your controller is defined in some kind of dispatcher-servlet.xml therefore seperates from the applicationContext in which < tx:annotation-driven/> is defined. The compoment you want to enhance with #Transactional need to be within the same context with < tx:annotation-driven> if I'm not mistaken. So the #Transactional does not work.
That was my silly mistake. The Spring uses CGLIB to proxy methods with #Transactional annotated and it seems like CBLIB can't enhance protected method.
protected String home() {
Changing this to
public String home() {
fixed the problem.

Is it possible to read a combination of a System Property and a Customized Property in Spring config file?

I need to know if its possible in Spring to derive a value for a Property by combination of a System-Property and a Customized-Property defined over a PropertyFile.
What I've done:
1) Configured a -D argument in server as : -Dapp.Env="dev"
2) In my spring-config.xml, defined datasource details as below:
<!-- DataSource configurations -->
<bean id="myDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="myDataSource" />
<property name="xaDataSourceClassName" value="${my.database.xaDriver.class}" />
<property name="xaProperties">
<props>
<prop key="URL">#{systemProperties['appEnv']}.${my.database.jdbcUrl}</prop>
<prop key="user">#{systemProperties['appEnv']}.${my.database.user}</prop>
<prop key="password">#{systemProperties['appEnv']}.${my.database.password}</prop>
</props>
</property>
<property name="maxPoolSize" value="${my.database.maxPoolSize}" />
<property name="minPoolSize" value="${my.database.minPoolSize}" />
<property name="borrowConnectionTimeout" value="60" />
<property name="maintenanceInterval" value="120" />
</bean>
3) Defined the following properties in my .properties file:
my.database.xaDriver.class=oracle.jdbc.xa.client.OracleXADataSource
my.database.initialPoolSize=2
my.database.maxPoolSize=10
my.database.minPoolSize=2
dev.my.database.jdbcUrl=jdbc:oracle:thin:#test.com:1535:myDb
dev.my.database.user=myuserid
dev.my.database.password=mypwd
4) Defined .properties file location in the spring-config.xml as :
<context:property-placeholder location="/WEB-INF/spring/spring-config.properties" />
What I get on start of server :
Throwable occurred: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'myDataSource' defined in ServletContext resource [/WEB-INF/spring/spring-config.xml]: Could not resolve placeholder 'my.database.jdbcUrl' in string value "#{systemProperties['appEnv']}.${my.database.jdbcUrl}"
What I also tried :
5) Defined a property in .properties file as below:
app.Env=dev
6) Modified the datasource bean configuration as below:
<prop key="URL">${app.Env}.${my.database.jdbcUrl}</prop>
<prop key="user">${app.Env}.${my.database.user}</prop>
<prop key="password">${app.Env}.${my.database.password}</prop>
However on start of server, I get same exception message :
Throwable occurred: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'myDataSource' defined in ServletContext resource [/WEB-INF/spring/spring-config.xml]: Could not resolve placeholder 'my.database.jdbcUrl' in string value "${app.Env}.${my.database.jdbcUrl}"
Can someone help me out in pointing out what mistake I might be doing and what is/are the possible fix for this issue ?
1) You can read system properties into Spring bean:
<bean id="systemProperties" class="java.lang.System" factory-method="getProperties"/>
and then use multiple property resolvers
2) You can use flag systemPropertiesMode of PropertyPlaceholderConfigurer
3) You can write your custom bean factory that would produce single java.util.Properties object from system properties and file properties.
That bean would have method
public Properties merge() {
Properties merged = new Properties();
for (Properties p : properties) {
if (p != null) {
merged.putAll(p);
}
}
return merged;
}
Invoked in spring like that:
<bean id="factory" class="my.Factory">
<property name="properties">
<util:list>
<bean class="java.lang.System" factory-method="getProperties"/>
<bean ref="otherProperties">
</util:list>
</property>
</bean>

Resources