Java, Spring - Logging Bean Properties - java

Say I have the following bean definition:
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="maxActive" value="50"/>
<property name="maxIdle" value="5"/>
Is there a way to get Spring to log all of the properties that it has set (without enabling verbose logging for Spring in log4j). I was thinking of something along the lines of... verbose="true"
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource" verbose="true">
Update
I used the following, as suggested in the answer:
public class SpringBeanLoggingPostProcessor implements BeanPostProcessor {
private static final Logger log = Logger.getLogger(SpringBeanLoggingPostProcessor.class);
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean.getClass() == org.apache.commons.dbcp.BasicDataSource.class) {
BasicDataSource ds = (BasicDataSource) bean;
log.info("url="+ds.getUrl());
log.info("username="+ds.getUsername());
}
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String arg1) throws BeansException {
return bean;
}
}

No, there is no such a facility. You can create a dedicated bean post processor that logs target bean properties for that.

Related

Setting up Tomcat JDBC connection pool with the Spring JDBCTemplate

I'm trying to setup a Tomcat connection pool (to MySQL) in my Java web app, while using the Spring JDBCTemplate.
This is the Java class to create the connection pool:
#Configuration
public class DataAccessConfiguration {
#Bean(destroyMethod = "close")
public javax.sql.DataSource datasource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:java-config");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
return ds;
}
#Bean public JdbcOperations tpl() {
return new JdbcTemplate(datasource());
}
}
This is how I get the ApplicationContext (in the main method for example):
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
How should I define the DataAccessConfiguration class in the Beans.xml file so Spring knows to use it?
**
Update:
**
This is the actual configuration method:
public javax.sql.DataSource datasource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
ds.setPoolProperties(p);
return ds;
}
Can you please help rewrite in the Beans.xml?
For the values passed in Properties setter methods exist. Therefore XML-based bean definition goes like:
<bean name="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="url" value="jdbc:mysql://localhost:3306/mysql"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
....
</bean>
<bean name="tpl" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="jdbcUrl" value="#{config['db.url']}" />
<property name="driverClass" value="#{config['db.driver']}" />
<property name="user" value="#{config['db.username']}" />
<property name="password" value="#{config['db.password']}" />
<property name="acquireIncrement" value="1" />
<property name="idleConnectionTestPeriod" value="300" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="20" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>

When to create a new SessionFactory?

In my app I've been passing my SessionFatory in my method parameters when I need to access my database within those methods. It's instantiated in my Controller using:
#Autowired
private SessionFactory sessionFactory;
And when I need to access my database I use lines like this:
sessionFactory.getCurrentSession().save()
// or
List products = sessionFactory.getCurrentSession().createSQLQuery("SELECT * FROM PRODUCTS").list();
Should I be creating new sessionFactories to get my current session or should is it okay to pass it as a parameter?
edit [added for clarity]:
From HomeController.java:
#Controller
public class HomeController {
#Autowired
private SessionFactory sessionFactory;
//Correlations Insert Handler
//DOCUMENT CREATE
#RequestMapping(value="/documents", method = RequestMethod.PUT)
public #ResponseBody String insertDocument(HttpServletRequest request, HttpServletResponse response){
DocumentControl documentControl = new DocumentControl();
documentControl.insertDocument(request, response, sessionFactory);
return "went through just find";
}
//DOCUMENT READ
#RequestMapping(value="/documents", method = RequestMethod.GET)
public #ResponseBody List<Documents> getAllDocuments(){
//List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
DocumentControl documentControl = new DocumentControl();
List documents = documentControl.getAllDocuments(sessionFactory);
return documents;
}
From DocumentControl.java:
public class DocumentControl {
private static Logger logger = Logger.getLogger(DocumentControl.class.getName());
public DocumentControl(){};
//CREATE
public void insertDocument(HttpServletRequest request, HttpServletResponse response, SessionFactory sessionFactory){
Documents document = new Documents(request)
sessionFactory.getCurrentSession().save(document);
}
//READ
public List<DocumentReturn> getAllDocuments(SessionFactory sessionFactory){
List documents = sessionFactory.getCurrentSession().createQuery("from Documents").list();
return documents;
}
private boolean ifProductExists (String productCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Products> productList = sessionFactory.getCurrentSession().createCriteria(Products.class).add( Restrictions.eq("productCode", productCode)).list();
if ( !productList.isEmpty() && productList.size() > 0 ) {
exists = true;
}
return exists;
}
private boolean ifStandardExists (String standardCode, SessionFactory sessionFactory) {
boolean exists = false;
List<Standards> standardList = sessionFactory.getCurrentSession().createCriteria(Standards.class).add( Restrictions.eq("standardCode", standardCode)).list();
if ( !standardList.isEmpty() && standardList.size() > 0 ) {
exists = true;
}
return exists;
}
}
hibernate.cfg.xml:
hibernate-configuration>
<session-factory>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">100</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.max_statements">10</property>
<property name="hibernate.c3p0.min_size">10</property>
<property name="hibernate.c3p0.timeout">100</property>
<mapping class="***.*****.********.model.Documents"/>
</session-factory>
</hibernate-configuration>
persistence-context.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key=" hibernate.use_sql_comments">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<
property name="sessionFactory" ref="sessionFactory" />
</bean>
SessionFactory is a Singleton. Therefore, you should get it from the ApplicationContext (i.e. using the #Autowired annotation). Passing it as a parameter will only complicate your API.
I think that you should leave creation of SessionFactory to the framework you are using and inject it as a resource just as you are doing by now. Besides you are not creating it in your code at all. Good practice is to create separate session for every chunk of data manipulation, if it is not done by a framework ofc. Session should be your main unit of work.

NullPointerException resulting when attempting to access database

This is the method which is giving me the NullPointerException. I have narrowed it down to the line which queries the database.
The code does not reach the second logger. The exception is thrown before it prints out.
UPDATE: I just tested the jdbcTemplate and it is what is NULL.
public boolean usernameVerificationProcessService(String string){
HomeController.logger.info("In method usernameVerificationProcessService in UIDao.");
HomeController.logger.info("Attention: " + string);
boolean result = false;
String sql = "select count(*) from users where username = ?";
int count = this.jdbcTemplate.queryForObject(sql, new Object[]{string}, java.lang.Integer.class);
HomeController.logger.info("Attention: " + count);
if(count == 0) {
result = true;
}
return result;
}
This is the config file description. I am using a BasicDataSource and MySQL.
<beans:bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<beans:property name="url" value="jdbc:mysql://localhost:3306"/>
<beans:property name="username" value="test"/>
<beans:property name="password" value="test"/>
<beans:property name="initialSize" value="2" />
<beans:property name="maxActive" value="5" />
</beans:bean>
Do you forget to inject the jdbcTemplate into the object?
public class DBaseAccess {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
.......
}
You must inject the jdbcTemplate to the object in your spring config file (applicationContext.xml)
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dBaseAccess" class="coc.taf.dbase.DBaseAccess">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
You never check the string parameter for null. I guess it fails with NPE.

inject/autowire datasource from spring when more than one datasource is specified

I am trying to inject from my context.xml file a datasource. I have this config file but the issue is, i have more than one datasource at anytime. When i try to inject any of them, I get an error that says there is a no matching bean.
Here is my code.
#Inject
#Named("dataSourceAccounts")
//#Autowired
#Override
public void setDataSource(DataSource dataSource) {
// TODO Auto-generated method stub
this.jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("jdbcTemplate is null? " + (jdbcTemplate == null));
}
//for the context.xml
<context:component-scan base-package="business" />
<context:component-scan base-package="middleware" />
<context:component-scan base-package="presentation" />
<context:annotation-config/>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
<bean id="txManagerProducts" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceProducts"/>
</bean >
<bean id="txManagerAccounts" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceAccounts"/>
</bean >
<bean id="dataSourceProducts" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/productsdb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/accountsdb" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
Thanks for your help in a advance.
If you want to specify the qualified name for the dependency that should be injected, the #Named should precede the param definition:
#Inject
#Override
public void setDataSource(#Named("dataSourceAccounts") DataSource dataSource) {
// TODO Auto-generated method stub
this.jdbcTemplate = new JdbcTemplate(dataSource);
System.out.println("jdbcTemplate is null? " + (jdbcTemplate == null));
}
or using spring annotations:
#Autowired
#Override
public void setDataSource(#Qualifier("dataSourceAccounts") DataSource dataSource)
this should work
#javax.annotation.Resource(name="dataSourceProducts")
public void setDataSource(DataSource dataSource) {
...
}
javax.annotation.Resource is in Java SE

Not routing DataSource with Spring 3 and MyBatis

I have a default database and sometimes I have to make a select in another database.
I've searched many blogs and questions here about this, but couldn't make it work.
Tried the http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/ way. Nothing.
Code for RouterDataSource class:
public class RouterDataSource extends AbstractRoutingDataSource {
#Override
protected DataSourceEnum determineCurrentLookupKey() {
return DataSourceContextHolder.getTargetDataSource();
}
}
Code for DataSourceContextHolder class:
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<DataSourceEnum>();
public static void setTargetDataSource(DataSourceEnum targetDataSource) {
Assert.notNull(targetDataSource, "Target data source cannot be null");
contextHolder.set(targetDataSource);
}
public static DataSourceEnum getTargetDataSource() {
if (contextHolder.get() != null)
return (DataSourceEnum) contextHolder.get();
else
return DataSourceEnum.DB1;
}
public static void resetDefaultDataSource() {
contextHolder.remove();
}
}
Code for the method calling to change the database:
#Override
public CodeHD getCategoryByCode(String code) throws BusinessException {
DataSourceContextHolder.setTargetDataSource(DataSourceEnum.DATABASE2);
return (CodeHD) persistency.getObject(GETOBJECT_BY_CODE, code);
}
Code for DatasourceEnum class:
public enum DataSourceEnum {
DB1,
DB2;
}
And finally the configuration on my applicationContext.xml:
<bean id="parentDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" abstract="true">
<property name="driverClass" value="oracle.jdbc.pool.OracleDataSource" />
<property name="acquireIncrement" value="10" />
<property name="idleConnectionTestPeriod" value="60" />
<property name="maxStatements" value="50" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="15" />
</bean>
<bean id="database1DS" parent="parentDataSource">
<property name="jdbcUrl" value="jdbc:oracle:thin:#database1:1521:xe" />
<property name="user" value="user" />
<property name="password" value="password" />
</bean>
<bean id="database2DS" parent="parentDataSource">
<property name="jdbcUrl" value="jdbc:oracle:thin:#database2:1521:xe" />
<property name="user" value="user" />
<property name="password" value="password" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="package.RouterDataSource">
<property name="defaultTargetDataSource" ref="database1DS"/>
<property name="targetDataSources">
<map key-type="package.DataSourceEnum">
<entry key="DB1" value-ref="database1DS"/>
<entry key="DB2" value-ref="database2DS"/>
</map>
</property>
</bean>
The problem is that when I set it to DB2 it won't change.
Can anyone help me?
Try to make both static method as non static and pass R reference if the context holder.
First make sure that database2DS is working correctly. Make the defaultTargetDatasource database2DS and verify that it is not using DB1 still and there are no other errors using database2DS as the default. If the AbstractRoutingDataSource fails to resolve a DataSource in the targetDataSources you cannot switch to it.
AbstractRoutingDataSource will only change the DataSource when getConnection is called. Whatever persistence framework you're using is probably caching the Connection and not calling getConnection in between persistency.getObject(). However you are getting your persistency object, try getting a new persistency object after you change the datasource in DataSourceContextHolder. If this solves your problem, try creating a class that maintains your persistency object and handles changing the datasource. That way when you change the datasource you can modify your persistency manager object in one spot.

Categories

Resources