Menu

dynamic datasources

2004-11-03
2013-04-11
  • Nobody/Anonymous

    My application needs a dynamic datasource because we need the user name to get other information from database. So we cannot have the static user name/password set in properties file. Is there a way in Ibatis to set username/password dynamicly? or I have to produce a different properties file every time after user login? Thanks,

     
    • Brandon Goodin

      Brandon Goodin - 2004-11-04

      There is not an easy way to accomplish this in any persistence framework without some customization  beyond the framework.

      You would need to pass along the username/password from you application first. This would require using ThreadLocal to place username/password information on so that it is thread specific.

      You could initialize that information in a Filter if you are in a web environment.

      Then create your implementation of Datasource that uses proxool for maintaining the connection pool underneath.

      If you are curious about how this could be done take a look into the ibatis cvs at the "contributed" module and you will find ThreadUserDatasource written by Larry Meadors. It does exactly what you want. But, it is not heavily tested. It is a "use at your own risk" piece of code.

      http://cvs.sourceforge.net/viewcvs.py/ibatisdb/contributed/ibatis-dbl-2/threaduserdatasource/

      Brandon

       
    • Nobody/Anonymous

      Brandon,
      Thanks for your notes. I read the posted ThreadUserDatasource. But I still have questions about customized datasource. In the package.html, the author says,

      Finally, some code to the the DataSource:<pre><code>
      public DataSource getDataSource() throws Exception {
          Context initCtx = new InitialContext();
          Context envCtx = (Context) initCtx.lookup("java:comp/env");
          User user = new SimpleUserImpl("someUser", "aR34llyGre4tPa55w0rd");
          ThreadUser.setUser(user);
          DataSource ds = (DataSource) envCtx.lookup("jdbc/tut");
          System.out.println("ds = " + ds);
          return ds;

      I don;t really know where we should add this part? to which java file? Because in Ibatis, we don't need to write our own java file to get datasource, we directly call teh mapped statment. If we create a file to get datasource, how are we going to use it in Ibatis? or the author means we need to modify the Ibatis source file ? which one is it? the Problems is when I downlaod the source from Ibatis, I actually cannot get all the source. part of them are still hidden. I don't know why is that. Thanks,

       
      • Larry Meadors

        Larry Meadors - 2004-11-08

        Huh? What is missing here? I am real sure I checked everything in - are you getting this from CVS, or are you looking in the distribution? I am pretty sure it is not in any distribution, but equally sure it is all in CVS.

        If there is anything missing, please let me know and I will get it committed.

         
    • Nobody/Anonymous

      Larry,
      Thanks for replaying. When I say I didnot get all source, I mean I cannot see all Ibatis source, not your thread datasource. I look at it from CVS. My question is, in your package.html, you say Finally, some code to the the DataSource:<pre><code>
      public DataSource getDataSource() throws Exception {
      Context initCtx = new InitialContext();
      Context envCtx = (Context) initCtx.lookup("java:comp/env");
      User user = new SimpleUserImpl("someUser", "aR34llyGre4tPa55w0rd");
      ThreadUser.setUser(user);
      DataSource ds = (DataSource) envCtx.lookup("jdbc/tut");
      System.out.println("ds = " + ds);
      return ds;

      I just don't know this function public DataSource getDataSource()  will be added to which file to make it work. It will be added to some Ibatis file or some file I have to create?

       
    • Larry Meadors

      Larry Meadors - 2004-11-09

      Hmm, if I understand your question correctly, you need to set up the datasource as a jndi resource. Then configure ibatis to use that datasource.

      Then, before calling iBATIS functions, call

      ThreadUser.setUser(user);

      That *should* do it.

      Disclaimer: As was stated earlier, this is still very experimental, and not heavily tested. You should go into it knowing that if you have probelms, I may or not be able to help you resolve them.

      Larry

       
    • Nobody/Anonymous

      I know how to set up datasource as jndi. But I don't know how to connect this jndi to iBatis using SqlMapStatement. Because when I try to use SqlMapStatement, I use like this,

      Reader reader = Resources.getResourceAsReader(resource);
           sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
      .....
      sqlMap.startTransaction();
                  list = sqlMap.queryForList(statementName, parameterObject);
                  sqlMap.commitTransaction();

      Here the satementName is already set up in each mapped statement file, and the connection is set up in database.properties. The problem is the properties file is statis in regard as User name and password. If I want to use your ThreadUser to dynamically pass in my user name and password, how could I do that? Is there any file in iBatis that I could extend then override a function like getDataSource so when I call sqlMap.queryForList() , it will know to use this dynamic datasource?

      in your package.html, you say"
      Finally, some code to the the DataSource:<pre><code>
      public DataSource getDataSource() throws Exception {
      Context initCtx = new InitialContext();
      Context envCtx = (Context) initCtx.lookup("java:comp/env");
      User user = new SimpleUserImpl("someUser", "aR34llyGre4tPa55w0rd");
      ThreadUser.setUser(user);
      DataSource ds = (DataSource) envCtx.lookup("jdbc/tut");
      System.out.println("ds = " + ds);
      return ds;
      "

      and I asked you what file will this getDataSource()  be added to?

      what I need to know is the place I could set a dynamic datasource in iBatis after I get username/password from web.
                

       
    • Nobody/Anonymous

      I know how to set up datasource as jndi. But I don't know how to connect this jndi to iBatis using SqlMapStatement. Because when I try to use SqlMapStatement, I use like this,

      Reader reader = Resources.getResourceAsReader(resource);
           sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
      .....
      sqlMap.startTransaction();
                  list = sqlMap.queryForList(statementName, parameterObject);
                  sqlMap.commitTransaction();

      Here the satementName is already set up in each mapped statement file, and the connection is set up in database.properties. The problem is the properties file is statis in regard as User name and password. If I want to use your ThreadUser to dynamically pass in my user name and password, how could I do that? Is there any file in iBatis that I could extend then override a function like getDataSource so when I call sqlMap.queryForList() , it will know to use this dynamic datasource?

      in your package.html, you say"
      Finally, some code to the the DataSource:<pre><code>
      public DataSource getDataSource() throws Exception {
      Context initCtx = new InitialContext();
      Context envCtx = (Context) initCtx.lookup("java:comp/env");
      User user = new SimpleUserImpl("someUser", "aR34llyGre4tPa55w0rd");
      ThreadUser.setUser(user);
      DataSource ds = (DataSource) envCtx.lookup("jdbc/tut");
      System.out.println("ds = " + ds);
      return ds;
      "

      and I asked you what file will this getDataSource()  be added to?

      what I need to know is the place I could set a dynamic datasource in iBatis after I get username/password from web.
                

       
      • Nobody/Anonymous

        I hope you are not saying that you perform this code everty time you call ibatis. This would be a tremendous amount of overhead.

        Reader reader = Resources.getResourceAsReader(resource);
        sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
        .....
        sqlMap.startTransaction();
        list = sqlMap.queryForList(statementName, parameterObject);
        sqlMap.commitTransaction();

        You should call the following only once and persist it in a base DAO class or some other kind of manner of persisting a the sqlmap instance.

        ...
        Reader reader = Resources.getResourceAsReader(resource);
        sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
        ...

        Then in you DAO class you can call

        ...
        sqlMap.startTransaction();
        list = sqlMap.queryForList(statementName, parameterObject);
        sqlMap.commitTransaction();
        ...

        As for how to use JNDI with SQLMaps... it's all in the manual. You could set the ThreadUser values in a filter perhaps. It uses ThreadLocal therefore it is specific to the Thread. Are you familiar with ThreadLocal?

        Brandon

         
    • Nobody/Anonymous

       
    • Nobody/Anonymous

      yes, I call Reader reader = Resources.getResourceAsReader(resource); 
      only once in the base DAO class. I read the manual about jndi. but my question is how to pass in user name/password without using a statis properties file.

      because when I call list = sqlMap.queryForList(statementName, parameterObject);  , the connection is hidden from API. and it is actually set in the properties file with static username/password and driver/url. if I want to use dynamic username from web (or filter), how can I pass in?
      thanks,

       
      • Brandon Goodin

        Brandon Goodin - 2004-11-11

        If you configure your JNDI datasource in tomcat to use the ThreadUserDataSourceFactory then all you need to do is to call the following code (with relevant user and pass). You can put it in a Filter to make sure you add the User to the thread and clean it up when it is done.

        ...

        User user = new SimpleUserImpl("someUser", "aR34llyGre4tPa55w0rd"); 
        ThreadUser.setUser(user);

        ...

        That's all you need to do. It attaches ThreadUser to ThreadLocal. So, you do not need direct access to the datasource.

        Are you familiar with ThreadLocal? Maybe, this is where the confusion is happening.

        Brandon

         
    • Nobody/Anonymous

      I think you answered my question. I will try to use it to see how it works. Thanks a lot.
      By the way, do we need to put proxool-0.8.3.jar  under classpath when we use this ThreadUserDataSourceFactory ? it is placed in Larry
      's CVS/lib, but from this jar I  do not see any file related to the thread user datasource.

       
    • Nobody/Anonymous

      I copy all the files from CVS and compile , also put proxool-0.8.3.jar on classpath. but when I call
      ThreadUser.setUser(user);

              DataSource ds = (DataSource) envCtx.lookup("sth");

      I got
      java.lang.ExceptionInInitializerError
          com.ibatis.jdbc.threaduserdatasource.ThreadUserDataSourceFactory.initialize(ThreadUserDataSourceFactory.java:59)
          com.ibatis.jdbc.threaduserdatasource.ThreadUserDataSourceFactory.getObjectInstance(ThreadUserDataSourceFactory.java:81)

      I also use the jndi and the part of web.xml in package.html. why is that? I also got naming exception before I added more exception throw to the code.

       
    • Nobody/Anonymous

      I found out the problem is the proxool.zip. We need to make to be proxool.jar to work under tomat5.

      another problem I have is every time after I execute some sqlMapstatement, it gives the right result then after that it will have an exception like this:
      ERROR [Shutdown Hook] - Problem calling "get cause" on IllegalStateException.
      java.lang.reflect.InvocationTargetException
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
          at java.lang.reflect.Method.invoke(Unknown Source)
          at org.logicalcobwebs.proxool.ShutdownHook.remove(ShutdownHook.java:40)
          at org.logicalcobwebs.proxool.ProxoolFacade.shutdown(ProxoolFacade.java:238)
          at org.logicalcobwebs.proxool.ProxoolFacade.shutdown(ProxoolFacade.java:220)
          at org.logicalcobwebs.proxool.ShutdownHook.run(ShutdownHook.java:99)
          at java.lang.Thread.run(Unknown Source)
      Caused by: java.lang.IllegalStateException: Shutdown in progress
          at java.lang.Shutdown.remove(Unknown Source)
          at java.lang.Runtime.removeShutdownHook(Unknown Source)
          ... 9 more

      and I use a baseDAo for every action such as getList(String statementName, Object parameterObject),
      I will close transaction like this:
      try {
                  sqlMap.startTransaction();
                  list = sqlMap.queryForList(statementName, parameterObject);
                  sqlMap.commitTransaction();
              } catch (SQLException e) {
                  try {
                      sqlMap.endTransaction();
                  } catch (SQLException ex) {
                      throw new DaoException(ex.fillInStackTrace());
                  }
                  throw new DaoException(e.fillInStackTrace());
              } finally {
                  try {
                      sqlMap.endTransaction();
                  } catch (SQLException e) {
                      throw new DaoException(e.fillInStackTrace());
                  }
              }
      anything wrong with this?

       

Log in to post a comment.

MongoDB Logo MongoDB