Page 1 of 4 123 ... LastLast
Results 1 to 10 of 38

Thread: Hot Swapping a DataSource

  1. #1
    Join Date
    Mar 2005
    Posts
    25

    Default Hot Swapping a DataSource

    I have an application where I need to login to a database to obtain configuration information. The user is then presented with a list that they can choose from. The list give the user the option to recover data from different datasources.

    It is therefore necessary to change a datasource depending on what th user selects.

    After browsing the forum, I decided the way forward was to use HotSwappableTargetSource in order to change the datasource.

    However this changes the datsource for every user, not just the user logged into the current session.

    Can anyone advise me how to just change a datasource just for the logged in user?

    Relevant code is:

    <bean id="swappingDataSourceTargetSource"
    class="org.springframework.aop.target.HotSwappable TargetSource">
    <constructor-arg><ref bean="dataSourceConfig"/></constructor-arg>
    </bean>

    <bean id="dataSource"
    class="org.springframework.aop.framework.ProxyFact oryBean"
    >
    <property name="proxyInterfaces"><value>javax.sql.DataSource </value></property>
    <property name="targetSource" ref="swappingDataSourceTargetSource"/>

    </bean>

    <bean id="dataSourceConfig" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close"
    >
    <property name="driverClassName">
    <value>${database.driver}</value>
    </property>
    <property name="url">
    <value>${database.url}</value>
    </property>
    <property name="username">
    <value>${database.username}</value>
    </property>
    <property name="password">
    <value>${database.password}</value>
    </property>
    <property name="poolPreparedStatements">
    <value>true</value>
    </property>
    <property name="initialSize">
    <value>${database.initialpoolsize}</value>
    </property>
    <property name="maxActive">
    <value>${database.maxactiveconnections}</value>
    </property>
    <property name="maxIdle">
    <value>${database.maxidleconnections}</value>
    </property>
    <property name="maxWait">
    <value>1000</value>
    </property>
    <property name="defaultAutoCommit">
    <value>true</value>
    </property>
    </bean>


    and a typical useage of the datasource:


    <bean id="applicationDao" class="com.cyc.model.dao.application.jdbc.Applicat ionDAOJdbc">
    <property name="dataSource"><ref local="dataSource"/></property>
    </bean>

  2. #2
    Join Date
    Mar 2005
    Posts
    25

    Default

    Can anyone advise me on the best way forward with this one. I'm at a loss to know which way to go.

    Thanks,

  3. #3
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    Are all datasources known in advance?

    In that case you could do the following:
    - Define all datasources in the context
    - Create a wrapper class implementing DataSource which has a set of real datasources to choose from
    - Wire the datasources to your wrapper class in the context
    - Wire the wrapper datasource to your dao-beans

    Now you have to find a means to tell your wrapper which datasource to use. This could be done via a ThreadLocal (if you are in control of the executing thread).

    Hope that helps,
    Andreas

  4. #4
    Join Date
    Mar 2005
    Posts
    25

    Default

    Thanks for your reply Andreas.

    The DataSources are defined in a database table, and are therefore known at startup. I can read these datasources and create a BeanFactory holding them.

    I think I understand what you're saying about creating the wrapper around DataSource that can then choose the correct one.

    The bit I'm uncertain about is how to use ThreadLocal to tell the wrapper which datasource to use - I've searched the web and found some things about it, and one or two articles saying not to use ThreadLocal in a J2EE environment.

    Could you tell me a little more about this, or point me in the right direction to read up on it?

    Thanks

    Andy

  5. #5
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    It is true that usage of ThreadLocals in EJB apps is discouraged. That is because the container manages threads itself (usually using a thread-pool), and interfering here _might_ be dangerous if you don't know what you are doing (or what the container is doing). Nevertheless, somehow the deficiencies of EJB have to be overcome somehow :-)

    The important point if you use ThreadLocals is: don't forget to clear them appropriately. Otherwise another invocation might see data it is not intended to see or resources are referenced by a pooled thread and never released.

    Regards,
    Andreas

  6. #6
    Join Date
    Mar 2005
    Posts
    25

    Default

    Thanks - I'll give it try.

    My useage of it would be:

    1. A J2EE app user will select a datasource that they wish to connect to.
    2. The user will remain connected to the datasource until they change it again.

    Andy

  7. #7
    Join Date
    Feb 2005
    Location
    Boston, MA
    Posts
    1,142

    Default

    Here is a way to handle this without having to make your DAOs aware that they use multiple datasources. Basically you create an AOP proxy which determines from the arguments which datasource to use, and you use a TargetSource to do the actual lookup. Here is an abstract class which is actually both objects:

    Code:
    public abstract class AbstractMapBasedTargetSourceInterceptor implements MethodInterceptor, TargetSource, InitializingBean {
        private Map targetMapping;
        private final ThreadLocal targetInThread = new ThreadLocal();
        private Class targetClass;
    
        public void setTargetMapping(Map targetMapping) {
            this.targetMapping = targetMapping;
        }
    
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    
            Object key = getKey(methodInvocation);
            if (key == null) {
                throw new IllegalStateException("Key value cannot be null");
            }
    
            targetInThread.set(lookupTarget(key));
            try {
                return methodInvocation.proceed();
            } finally {
                targetInThread.set(null);
            }
        }
    
        protected abstract Object getKey(MethodInvocation methodInvocation);
    
        public void afterPropertiesSet() throws Exception {
            if (targetMapping == null) {
                throw new IllegalStateException("targetMapping is required");
            }
    
            if (targetClass == null) {
                throw new IllegalStateException("targetClass is required");
            }
        }
    
        public Class getTargetClass() {
            return targetClass;
        }
    
        public void setTargetClass(Class targetClass) {
            this.targetClass = targetClass;
        }
    
        public boolean isStatic() {
            return false;
        }
    
        public Object getTarget() throws Exception {
            return targetInThread.get();
        }
    
        private Object lookupTarget(Object key) {
            if (key == null) {
                throw new IllegalStateException("No key for look up");
            }
    
            Object value = targetMapping.get(key);
            if (value.getClass() != getTargetClass()) {
                throw new IllegalStateException("Mapped object isn't appropriate class.  Expecting: " + value.getClass().getName() + ". Actual: " + getTargetClass().getName());
            }
            return value;
        }
    
        public void releaseTarget(Object target) throws Exception {
            // no-op
        }
    }
    To use it you override it. In this example it always assumes every method uses the first argument as the datasource lookup:

    Code:
    public class DataSourceTargetSourceInterceptor extends AbstractMapBasedTargetSourceInterceptor {
        /**
         * Return first argument as the key
         * @param methodInvocation
         * @return
         */
        protected Object getKey(MethodInvocation methodInvocation) {
            return methodInvocation.getArguments()[0];
        }
    }
    And you need two proxies:
    • The dataSource as a targetSource. This will call getTarget around each dataSource method to get the current dataSource
    • The object which determines the dataSource (most likely the DAO). This will look at the method call, looks up and sets the datasource as a ThreadLocal


    The seqence of events is:
    • DAO proxy is invoked. From the method arguments sets the datasource as a ThreadLocal
    • DAO calls DataSource.getConnection() which uses the key to find the correct datasource


    Here is roughtly how it would be wired up. This is different from most targetSource examples because what influences the target is at a different call level then where the target is used. In object pooling the very act of getting the target is what triggers the pool. Here its the DAO call which determines which dataSource is going to be used.

    Code:
    <bean id="targetMapping" class="DataSourceLookup">
      <description>Creates a map of datasources</description>
    </bean>
    
    <bean id="dataSourceTarget" class="DataSourceTargetSourceInterceptor">
      <property name="targetClass" value="javax.sql.DataSource"/>
      <property name="targetMapping" ref="targetMapping"/>  
    </bean>
    
    <bean id="myDataSource" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="proxyInterfaces" value="javax.sql.DataSource"/>
      <property name="targetSource" ref="dataSourceTarget"/>
    </bean>
    
    <bean id="myDaoTarget" class="myDaoImpl">
      <property name="dataSource" ref="myDataSource"/>
    </bean>
    
    <bean id="myDao" class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="target" ref="myDaoTarget"/>
      <property name="interceptorNames"><idref local="dataSourceTarget"/></property>
    Although this is a lot of wiring and a little tricky your DAO can be written like any other DAO. It takes a datasource and invokes the datasource normally. You put the logic for determining which datasource to use outside of the DAO itself. Yet you can use your DAO as a collaborator to your business object. I had solved this problem differently using a DAO factory that required my business object to know about the DAO factory. Time to revisit my code. Thanks for the inspiration
    Bill

  8. #8
    Join Date
    Mar 2005
    Posts
    25

    Default

    Thanks Bill.

    I shall give this a try and let you know how I get on.

    Andy

  9. #9
    Join Date
    Mar 2005
    Posts
    25

    Default

    Quote Originally Posted by Andreas Senft
    - Define all datasources in the context
    - Create a wrapper class implementing DataSource which has a set of real datasources to choose from
    - Wire the datasources to your wrapper class in the context
    - Wire the wrapper datasource to your dao-beans
    Andreas

    I have implemented the first part of your suggestion, but am struggling with the second part:

    Quote Originally Posted by Andreas Senft
    Now you have to find a means to tell your wrapper which datasource to use. This could be done via a ThreadLocal (if you are in control of the executing thread).

    The users select a different datasource, which is processed via a Struts action class.

    The entire application should then continue to use this datasource for this session.

    How would I use ThreadLocal to do this ?

    Thanks for your help.

    Andy

  10. #10
    Join Date
    Aug 2004
    Posts
    2,715

    Default

    If you want to use it throughout the session, then the datasource identifier has to be stored somewhere in the session (a http session I guess). A ThreadLocal might not help in that case.

    So you could define some kind of DataSourceIdResolver interface and an implementing class. The interface just declares a method which returns the datasource identifier, which can be used for datasource selection.

    The implementing class should access an active HttpSession to retrieve the identifier stored there (in a defined place).

    An instance of the DataSourceIdResolver implementation might then be injected to the datasource wrapper and enables it to resolve the datasource identifier from the current http session. By using an interface, the wrapper itself is not bound to http session access.

    Hope that helps,
    Andreas

    P.S.: You don't have any remoting/ejb layer in between, do you?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •