Announcement Announcement Module
Collapse
No announcement yet.
Get a bean after post processing Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Get a bean after post processing

    Hi guys,

    Let me explain the issues i am facing.

    I have 3-tiers application whereby all the beans are Spring beans
    and loaded as ApplicationContext using
    Code:
     
    org.springframework.web.context.ContextLoaderServlet
    There are 3 application context files which are
    applicationContext-web.xml, applicationContext-service.xml and
    applicationContext-dao.xml.

    I also have separated context for defining dataSource, sqlMapClient and
    transactionManager and transactionTemplate (file dataSourceContext.xml) with the content like this
    Code:
            <bean id="dataSource" destroy-method="close"
          	class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"/>
    		<property name="url" value="jdbc:db2e:xxx/${jdbc.username}/"/>
    		<property name="username" value="${jdbc.username}"/>
    		<property name="password" value="${jdbc.password}"/>
    	</bean>
    
    	<bean id="placeholderConfig" 
    		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    	<property name="systemPropertiesModeName">
    		<value>SYSTEM_PROPERTIES_MODE_FALLBACK</value>
    	</property>
    	<property name="ignoreUnresolvablePlaceholders">
    		<value>true</value>
    	</property>
    	<property name="location">
    		<value>file:xxx</value>
    	</property>
    	</bean>
    
            <bean id="transactionManager" 
        		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      		<property name="dataSource">
        		<ref bean="dataSource"/>
      		</property>
    	</bean>
      		
      	<bean id="transactionTemplate" 
      		class="org.springframework.transaction.support.TransactionTemplate">
    		<property name="transactionManager">
    			<ref bean="transactionManager"/>
    		</property>
    	</bean>
      	
      	<bean id="sqlMapClient"
    		class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    		<property name="configLocation">
    			<value>classpath:com/xxx/sql-map-config.xml</value>
    		</property>
    		<property name="dataSource">
    			<ref bean="dataSource"/>
    		</property>
    	</bean>
    	
    	<bean id="ibatisDBUtil"
    		class="com.xxx.IbatisDBUtil">
    		<property name="dataSource">
    			<ref bean="dataSource"/>
    		</property>
    		<property name="sqlMapClient">
    			<ref bean="sqlMapClient"/>
    		</property>
    		<property name="transactionTemplate">
    			<ref bean="transactionTemplate"/>
    		</property>
    	</bean>
    As you can see above i am using PropertyPlaceHolderConfigurer to
    replace some properties in the dataSource definition.

    The sequence of beans loading is like this:
    1 - At startup time, I use ContextLoaderServlet to load applicationContext-web.xml, applicationContext-service.xml and
    applicationContext-dao.xml.
    2 - After login, I set the System properties for jdbc.username and jdbc.password
    3 - Then, I initialize above dataSourceContext.xml with this call
    Code:
    void initDataSource()
    {
         XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource(
    		"conf/dataSourceContext.xml"));
         PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
         cfg.setLocation(new ClassPathResource("conf/jdbc.properties"));
         cfg.postProcessBeanFactory(factory);
    }
    My question is the step no.3 above is enough to get the bean id "ibatisDBUtil" ? Do i need to rebind it to ApplicationContext ?

    I am asking this because I want to get that bean from ApplicationContext by using this call
    Code:
    WebApplicationContextUtils.getWebApplicationContext(ServletContext)
    .getBean ("ibatisDBUtil");
    Thank you very much for any hint.

    Best Regards,
    frasyad

  • #2
    I don't think what are you trying to do will work: you'll have there two bean factories - the one for web application context and a separate one (XmlBeanFactory).

    Just an ideea - try something like:
    Code:
    AbstractRefreshableWebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(ServletContext);
    
    PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
    cfg.setLocation(new ClassPathResource("conf/jdbc.properties"));
    cfg.postProcessBeanFactory(context.getBeanFactory());
    set lazy-init="true" on dataSource bean and load dataSourceContext.xml first time together with all other context files.


    If the password and username is all that you want to change, take a look at these threads, might help: http://forum.springframework.org/showthread.php?t=24767
    http://forum.springframework.org/showthread.php?t=24300
    Last edited by Andrei Stefan; Apr 18th, 2007, 04:11 AM.

    Comment


    • #3
      It would be interesting to know what you are trying to achieve with step #3. I'm not sure why you are trying to do that. Any chance you could explain what problem you are trying to solve?

      Comment


      • #4
        Hi guys,

        Thanks for the feedbacks.

        Actually what i want to achieve is that I need all the beans from
        applicationContext-web.xml, applicationContext-service.xml,
        applicationContext-dao.xml and dataSourceContext.xml is available
        on the ApplicationContext so that I can get it through

        Code:
        WebApplicationContextUtils.getWebApplicationContext(ServletContext)
        However, the dataSource object will only be initialized after login
        because I need the logged in user id and password to be used to connect
        to datasource and to construct the JDBC url.

        Therefore, I will load dataSourceContext.xml only after login process,
        by using separate factory which is XmlBeanFactory.

        This post is related with:
        http://forum.springframework.org/showthread.php?t=37378

        I have tried this approach and it is working fine.
        But, everytime I need to do CRUD operations, I have to call this method:

        Code:
        public IbatisDBUtil getDBWrapper()
        {
             XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource(
        		"conf/dataSourceContext.xml"));
             PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
             cfg.setLocation(new ClassPathResource("conf/jdbc.properties"));
             cfg.postProcessBeanFactory(factory);
             return (IbatisDBUtil) factory.getBean ("ibatisDBUtil");
        }
        To me, this is not a good solution, since it seems it will always create new
        IbatisDBUtil object and its referenced objects such as
        transactionTemplate, dataSource and sqlMapClient.

        And what i am afraid of is whether the transaction works fine since
        it might get different connection for different calls.

        As you can see below is the debug message after I use the current approach
        Code:
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Fetching JDBC Connection from DataSource 
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Fetching JDBC Connection from DataSource 
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Registering transaction synchronization for JDBC Connection
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Registering transaction synchronization for JDBC Connection
         95969 [http-8081-2] DEBUG java.sql.Connection  - {conn-100042} Connection
         95969 [http-8081-2] DEBUG org.springframework.orm.ibatis.SqlMapClientTemplate  - Obtained JDBC Connection [[email protected]] for iBATIS operation
         95969 [http-8081-2] DEBUG java.sql.Connection  - {conn-100042} Preparing Statement:           <thequery>   
         95969 [http-8081-2] DEBUG java.sql.PreparedStatement  - {pstm-100043} Executing Statement:    <thequery>       
         95969 [http-8081-2] DEBUG java.sql.PreparedStatement  - {pstm-100043} Parameters: <result>
         95969 [http-8081-2] DEBUG java.sql.PreparedStatement  - {pstm-100043} Types: [java.lang.Integer, java.lang.Integer]
         95969 [http-8081-2] DEBUG java.sql.ResultSet  - {rset-100044} ResultSet
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  - Triggering beforeCommit synchronization
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  - Triggering beforeCompletion synchronization
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Returning JDBC Connection to DataSource 
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Returning JDBC Connection to DataSource
        One thing that makes me curious is that these lines:

        Code:
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Registering transaction synchronization for JDBC Connection
         95969 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceUtils  - Registering transaction synchronization for JDBC Connection
        I dunno if those lines means the transaction is successfully synchronized
        for the 2 datasource or not, which means they will be in the same
        transaction block ?

        That is actually what i want to achieve.

        Sorry if this posting is not in the correct forum.

        Thanks a lot in advance.
        frasyad
        Last edited by frasyad; Apr 18th, 2007, 11:26 PM.

        Comment


        • #5
          I'm not sure why you need to re-post the problem! From what I remember from the other thread, you wanted a new dataSource everytime e.g. for each user login. So why do you load the same jdbc.properties for each login? If you keep loading applicationContext.xml files then you are going to keep creating more of the contained beans.

          Comment


          • #6
            Hi,

            So then could you please me tell how to load the beans only once,
            without keep loading jdbc.properties?

            Bcos I dont want to keep creating more of the contained beans.

            Please do help, since I am still very new on this matter.

            Thanks for the help.
            frasyad

            Comment


            • #7
              i'm curious why i always see people using multiple datasources for the same application for different users. i don't understand that. can't you put everything in one datasource and change your object model appropriately?

              what you are trying to do here is very bizzare i don't think you're going to find much help. if I had this problem and was dead set on using multiple databases, i would try building the jdbc url from the properties file, pulling out a prototype scoped datasource bean from spring, and setting the url into it at runtime. something like that. or maybe build a factory spring bean like

              Code:
              public class DataSourceFactory  {
              
                  public DataSource newDataSource(User user)  {
                         
              
                  }
              }

              Comment


              • #8
                You wouldn't use multiple dataSources, you'd use UserCredentialsDataSourceAdapter. If you refer to this previous thread though this wasn't possible. Ahhhh two threads....... If you could set out exactly in very simple terms what you want to do, I'm sure people will try and help!

                Comment


                • #9
                  Hi guys,

                  I am really sorry if I had made confusion about this.
                  I simply want to have 1 datasource for all users, that's it.
                  However, the main problem is that each user will have its own database.

                  Why? Because I am using DB2e database, and each database is specific
                  only for 1 (single) user. And fyi, DB2e is local encrypted database used
                  for mobile devices.

                  In our machine, for testing purpose, we have more than one database
                  for the application. The main problem is that to connect to database
                  is based on user login id and password. Also the jdbc url is based on user
                  login id.

                  I can't use UserCredentialsDataSourceAdapter since DB2e does not support it.

                  Does anybody know how should I load and initialize the datasource only once
                  after user login? Should i use Scoped bean then?

                  If yes, please give me more info about it.

                  I really appreciate any help from all of you guys.

                  Thank you very much,
                  frasyad

                  Comment


                  • #10
                    As I said on the previous post I think this might be the way to go. The reference manual has information on scoped beans, have a read of that and see if it helps.
                    I think the problem here is that the dataSource is actually scoped for the users login. As the UserCredentialsDataSourceAdapter doesn't work for you, you could look at scoped beans. This doesn't seem ideal though and would be more expensive.
                    http://www.springframework.org/docs/...factory-scopes

                    Comment


                    • #11
                      Hi Karldmoore,

                      Thanks for the hint. I have tried it with scoped bean with scope = session.
                      But it does not work.

                      My Context definition is like this:
                      Code:
                              <bean id="dataSource" 
                                      class="com.ibm.db2e.jdbc.DB2eDataSource" 
                                      scope="session">
                      		<property name="sharedDatabaseAccess" value="true"/>
                                   </bean>
                      
                      	
                              <bean id="transactionManager" 
                          		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                        		<property name="dataSource">
                          		<ref bean="dataSource"/>
                        		</property>
                      	</bean>
                        		
                        	<bean id="transactionTemplate" 
                        		class="org.springframework.transaction.support.TransactionTemplate">
                      		<property name="transactionManager">
                      			<ref bean="transactionManager"/>
                      		</property>
                      	</bean>
                        	
                        	<bean id="sqlMapClient"
                      		class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
                      		<property name="configLocation">
                      			<value>classpath:xxx/sql-map-config.xml</value>
                      		</property>
                      		<property name="dataSource">
                      			<ref bean="dataSource"/>
                      		</property>
                      	</bean>
                      	
                      	<bean id="ibatisDBUtil"
                      		class="xxx.IbatisDBUtil">
                      		<property name="dataSource">
                      			<ref bean="dataSource"/>
                      		</property>
                      		<property name="sqlMapClient">
                      			<ref bean="sqlMapClient"/>
                      		</property>
                      		<property name="transactionTemplate">
                      			<ref bean="transactionTemplate"/>
                      		</property>
                      	</bean>
                      This context is loaded at startup with all other contexts using
                      ContextLoaderServlet.

                      And then in the code, after login I set this:
                      Code:
                          System.setProperty("jdbc.username", "theUser");
                          System.setProperty("jdbc.password", "thePassword");
                      After that I want to get the DB wrapper (IbatisDBUtil)
                      Code:
                      protected IbatisDBUtil getDBUtil()
                      {
                           ApplicationContext appContext =  
                                   WebApplicationContextUtils.getWebApplicationContext(getServletContext());
                           IbatisDBUtil dbUtil = (IbatisDBUtil) appContext.getBean("ibatisDBUtil");
                           DB2eDataSource ds = (DB2eDataSource) dbUtil.getDataSource();
                           ds.setUrl("theUrl/" + System.getProperty("jdbc.username")+"/");
                           ds.setUser(System.getProperty("jdbc.username"));
                           ds.setPassword(System.getProperty("jdbc.password"));
                           dbUtil.setDataSource(ds);
                           return dbUtil;
                      }
                      The error log is like this:
                      Code:
                      Caused by: java.lang.IllegalArgumentException: Connection must not be null
                      	at org.springframework.util.Assert.notNull(Assert.java:113)
                      	at org.springframework.jdbc.datasource.SimpleConnectionHandle.<init>(SimpleConnectionHandle.java:40)
                      	at org.springframework.jdbc.datasource.ConnectionHolder.<init>(ConnectionHolder.java:72)
                      	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:186)
                      	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:315)
                      	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:131)
                      Please do help on this.
                      I dunno why the connection is set to null.

                      Rgrds,
                      frasyad

                      Comment


                      • #12
                        It would be useful to see the complete stacktrace.

                        Comment


                        • #13
                          This the the complete logfile:

                          Code:
                           25557 [http-8081-2] DEBUG org.foo.project.planner.service.impl.DailyCalendarService  - IbatisDBUtil retrieved: [email protected]
                           25557 [http-8081-2] DEBUG org.foo.project.planner.service.impl.DailyCalendarService  - Datasource retrieved: [email protected]
                           25557 [http-8081-2] INFO  org.springframework.jdbc.datasource.JdbcTransactionObjectSupport  - JDBC 3.0 Savepoint class is available
                           25557 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  - Using transaction object [org.springframework.jdbc.data[email protected]1afbbe3]
                           25557 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  - Creating new transaction with name [null]
                           25557 [http-8081-2] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager  - Acquired Connection [null] for JDBC transaction
                           25573 [http-8081-2] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/sfa_my].[Faces Servlet]  - Servlet.service() for servlet Faces Servlet threw exception
                           javax.faces.FacesException: Error calling action method of component with id dailyCalendar:dailyCalendarInitButton
                          	at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:72)
                          	at javax.faces.component.UICommand.broadcast(UICommand.java:109)
                          	at javax.faces.component.UIViewRoot._broadcastForPhase(UIViewRoot.java:97)
                          	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:171)
                          	at org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:32)
                          	at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:95)
                          	at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:70)
                          	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:139)
                          	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
                          	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                          	at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)
                          	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
                          	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
                          	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
                          	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
                          	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
                          	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
                          	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
                          	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
                          	at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:831)
                          	at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:639)
                          	at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1196)
                          	at java.lang.Thread.run(Unknown Source)
                          Caused by: javax.faces.el.EvaluationException: Exception while invoking expression #{dailyCalendarWebController.init}
                          	at org.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:156)
                          	at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:61)
                          	... 22 more
                          Caused by: java.lang.IllegalArgumentException: Connection must not be null
                          	at org.springframework.util.Assert.notNull(Assert.java:113)
                          	at org.springframework.jdbc.datasource.SimpleConnectionHandle.<init>(SimpleConnectionHandle.java:40)
                          	at org.springframework.jdbc.datasource.ConnectionHolder.<init>(ConnectionHolder.java:72)
                          	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:186)
                          	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:315)
                          	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:131)
                          	at org.foo.project.service.AbstractService.process(AbstractService.java:96)
                          	at org.foo.project.web.AbstractWebController.execute(AbstractWebController.java:272)
                          	at org.foo.project.web.AbstractWebController.process(AbstractWebController.java:132)
                          	at org.foo.project.planner.web.impl.DailyCalendarWebController.specifyDaily(DailyCalendarWebController.java:47)
                          	at org.foo.project.planner.web.impl.DailyCalendarWebController.init(DailyCalendarWebController.java:55)
                          	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.apache.myfaces.el.MethodBindingImpl.invoke(MethodBindingImpl.java:132)
                          	... 23 more
                          Any ideas ?

                          Comment


                          • #14
                            I guess this is probably related to the way in which you are working with the dataSource. It's quite hard to see the problem with out debugging it and seeing what is going on. That's what I'd do.

                            Comment

                            Working...
                            X