views:

318

answers:

2

I would like to make a "First Access Database Setup Process" in my spring application and the only thing I can imagine as a solution would be to initialize the DataSource bean programatically.

My current bean is defined as:

<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/catalog" />
    <property name="username" value="condominium" />
    <property name="password" value="password" />
    <property name="validationQuery" value="SELECT 1" />
    <property name="testOnBorrow" value="true" />
    <property name="defaultAutoCommit" value="false" />
    <property name="maxWait" value="5000" />
</bean>

but the ideal thing was to load it by myself in whenever I need it and with the parameter I define.

The scenario is that the user (administrator) comes to the application at the first time and I ask him the server, port and catalog to connect. I store it in a embeeded db and next time application start, a bean can check if the parameter are set on the embedded db and load it again.

Is it possible?

EDT: per @axtavt sugestion I'm goin with a proxy...

<bean id="dataSource" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="swappableDataSource"/>
</bean>
<bean name="swappableDataSource" class="org.springframework.aop.target.HotSwappableTargetSource">
    <constructor-arg ref="dummyDataSource"/>
</bean>
<bean id="dummyDataSource" factory-bean="dataSourceFactory" factory-method="createDataSource" destroy-method="close"/>
<bean id="dataSourceFactory" class="com.condominium.spring.factory.DataSourceFactory"/>

once with a Proxy implemented how do I cast it to the Original Bean?

Object o = ctx.getBean("dataSource");
BasicDataSource ds = (BasicDataSource)o;

will throw a java.lang.ClassCastException: $Proxy4 cannot be cast to org.apache.commons.dbcp.BasicDataSource

A: 

You can externalize the database connection properties:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    <property name="validationQuery" value="SELECT 1" />
    <property name="testOnBorrow" value="true" />
    <property name="defaultAutoCommit" value="false" />
    <property name="maxWait" value="5000" />
</bean>

Now Spring will get them from a .properties file that is loaded on startup.

Another thing you'll want to think about if you're using a Java EE app server is JNDI connection pool. You'll have the JNDI name externalized, and the connection parameters are managed by the admin of the app server. I would say that's the preferred way to do it. Right now you still have a password in plain text - a bad idea.

duffymo
Its a point, but in my case I need (the user needs) to define it at run time. I will never know where the user wants to connect to. I'm using tomcat.
Ruben Trancoso
@duffymo could you please explain where the name of the property file is configured
stacker
Tomcat supports JNDI named data sources. You'll have to know something. No matter what, you'll have to bounce the server to get the changes, because Spring reads the app context on startup.
duffymo
It's the Spring PropertyPlaceholderConfigurer: http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.html. You put it in WEB-INF/classes in this case.
duffymo
A: 

You can create a factory to produce a DataSource like you want:

<bean id = "dataSourceFactory" class = "MyFactory" />

<bean id = "dataSource" factory-bean = "dataSourceFactory" factory-method = "createDataSource" />

-

public class MyFactory {
    public DataSource createDataSource() {
        ... // produce a DataSource
    }
}
axtavt
this way I should create EntityManger from a factory as well right? Because now, it's defined in the .xml and depends on datasource
Ruben Trancoso
Since the factory is defined in the context will we not fall in the same problem?
Ruben Trancoso
@Ruben: No, you shouldn't. This snippet creates a bean named `dataSource`. This bean can be used like any other bean. The only difference is that Spring will call your own factory code to instantiate it.
axtavt
Thanks @axtavt, but the app will create it on startup. Next time I need to change it's parameter at runtime how do you do to pass is to entitymanger that still has the first one?
Ruben Trancoso
@Ruben: If you want to change it at runtime, then you need to create a proxy.
axtavt
@axtavt Will accept it as the answer, looks like its the best option but I still get not it solver since I will create and swap to the new DataSource at run time and the Proxy do not allow me to cast to the original object. I need to understant it better. thanks!
Ruben Trancoso