Announcement Announcement Module
Collapse
No announcement yet.
ThreadPoolTaskExecutor with Runnable and Hibernate SessionFactory Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ThreadPoolTaskExecutor with Runnable and Hibernate SessionFactory

    Hello !

    My name is Marius van Zwijndregt, and i'm trying to build an application with Spring and Hibernate.

    I'm using Spring 3.5 together with Hibernate 3.

    Almost everything in my application is annotation driven, and so are the transactions, though one part, Runnables triggered via Spring's ThreadPoolTaskExecutor do not seem to get a proper Hibernate session attached to the thread.

    The code which triggers a new Runnable (in my case called a job):

    The following is triggered to queue a job 'handler':

    Code:
    MonitoringJobHandler job = new MonitoringJobHandler();
    
    BaseWorkerJobFactory.addJob(job);
    Inside the BaseWorkerJobFactory, the addJob does the following:

    Code:
    if (!jobIsRunning(job))
    {
    	getJobList().add(job);
    	executor.submit(job);
    }
    The executor is a ThreadPoolTaskExecutor extended object, constructed as static variable in the BaseWorkerJobFactory class.

    The job 'handlers' will determine new jobs to queue, which are also submitted to the same executor.

    This works, however, when trying to call getCurrentSession() inside the jobhandler, i'll get the following exception:

    Code:
    org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    	at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    	at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:685)
    	at core.models.objects.data.BaseDaoFactory.getSession(BaseDaoFactory.java:35)
    	at apps.bizlink.models.Network.getExpiredMonitoringJobs(Network.java:250)
    	at apps.bizlink.logic.scheduling.handlers.MonitoringJobHandler.runTask(MonitoringJobHandler.java:42)
    	at core.logic.scheduling.BaseWorkerJob.run(BaseWorkerJob.java:192)
    	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
    	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    	at java.lang.Thread.run(Thread.java:619)

    As sessionfactory i'm using a AnnotationSessionFactoryBean, together with HibernateTransactionManager.

    Im fairly new to Spring, but i guess this is because the instance is created outside of the Spring context.

    At the moment, i've created a custom annotation @JobHandler which is scanned at application startup though all classes.
    The list of JobHandler classes which is build up, are instantiated (via a quartz timer) and the instances are submitted to the executorService (In the above example i've made it a static instantation to clarify).
    Hopefully i can keep this way of logistics with a Spring solution.

    Now for my question; what is the proper way to create a Runnable instance via Spring, the way i'm using it now ?

    Thanks !

  • #2
    but i guess this is because the instance is created outside of the Spring context.
    What is created outside the context? can you post the configurations

    Comment


    • #3
      Well, the Runnable instance is created via the 'new' operator, so it wont be instantiated via Spring.

      My configuration is the following:

      Hibernate.xml

      Code:
      	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      		<property name="sessionFactory" ref="sessionFactory"/>
      	</bean>
      
      	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
      		<property name="configLocation" value="${hibernate.properties}"/>
      		<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
      
      		<property name="packagesToScan">
      			<list>
      				<value>${spring.packages.scan}</value>
      			</list>
      		</property>
      	</bean>
      
      	<tx:annotation-driven transaction-manager="transactionManager" />
      
      	<bean id="customUserDetailsService" class="${hibernate.userdetail.service}"></bean>
      
      	<bean id="initDAO" class="core.models.objects.data.BaseDaoFactory">
      		<property name="sessionFactory" ref="sessionFactory"/>
      	</bean>
      
      	<security:authentication-manager alias="authenticationManager">
      		<security:authentication-provider user-service-ref="customUserDetailsService">
      		</security:authentication-provider>
      	</security:authentication-manager>
      
      	<bean id="applicationInitListenerBean" class="${application.listener}"></bean>
      The placeholder variables are loaded from an hibernate.cfg.xml file:

      Code:
      <hibernate-configuration>
      <session-factory>
      <!-- JDBC connection settings -->
      <property name="connection.driver_class">org.postgresql.Driver</property>
      <property name="connection.url">jdbc:postgresql://localhost/my_database</property>
      <property name="connection.username">my_username</property>
      <property name="connection.password">my_password</property>
      
      <property name="transaction.auto_close_session">false</property>
      
      <!-- JDBC connection pool, use Hibernate internal connection pool -->
      <property name="connection.pool_size">20</property>
      
      <!-- Defines the SQL dialect used in Hiberante's application -->
      <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
      
      <!-- Disable the second-level cache  -->
      <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
      
      <!-- Display and format all executed SQL to stdout -->
      <property name="show_sql">false</property>
      <property name="format_sql">false</property>
      
      	<!-- Drop and re-create the database schema on startup -->
      <!--property name="hbm2ddl.auto">update</property-->
      
      <event type="save-update">
      	<listener class="core.logic.hibernate.HibernateFilterListener"/>
      </event>
      	
      
      <!-- Mapping to hibernate mapping files -->
      <!--mapping resource="org/kodejava/example/hibernate/app/Label.hbm.xml"/-->
      </session-factory>
      </hibernate-configuration>
      The web.xml:

      Code:
      	<filter>
      		<filter-name>hibernateFilter</filter-name>
      		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
      	</filter>
      
      	<filter-mapping>
      		<filter-name>hibernateFilter</filter-name>
      		<url-pattern>/*</url-pattern>
      	</filter-mapping>
      
      
      	<!-- Enables Spring Security -->
          <filter>
              <filter-name>springSecurityFilterChain</filter-name>
              <filter-class>
                  org.springframework.web.filter.DelegatingFilterProxy
              </filter-class>
          </filter>
      
          <filter-mapping>
              <filter-name>springSecurityFilterChain</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
      
          <servlet>
              <servlet-name>core</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      		<init-param><param-name>cleanupAfterInclude</param-name><param-value>false</param-value></init-param>
              <load-on-startup>1</load-on-startup>
      
          </servlet>
      
          <servlet-mapping>
              <servlet-name>core</servlet-name>
              <url-pattern>/</url-pattern>
          </servlet-mapping>
      
      	<listener>
      		<listener-class>
      			org.springframework.web.context.ContextLoaderListener
      		</listener-class>
      	</listener>
      Per subapplication i define an application.xml, containing the quartz timer:

      Code:
      <bean id="propertyPlaceholderConfigurer"
      		  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      		<property name="locations">
      			<list>
      				<value>/WEB-INF/classes/apps/bizlink/config/application.properties</value>
      			</list>
      		</property>
      		<property name="ignoreUnresolvablePlaceholders">
      			<value>true</value>
      		</property>
      	</bean>
      
      	<import resource="classpath:core/config/spring.xml"/>
      	<import resource="classpath:core/config/security-context.xml"/>
      	<import resource="classpath:core/config/hibernate.xml"/>
      	<import resource="classpath:core/config/i18n.xml"/>
      
      	<context:component-scan base-package="apps.bizlink"/>
      
      	<bean name="mainJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
      		<property name="jobClass" value="apps.bizlink.logic.scheduling.TimerScheduler"/>
      		<property name="jobDataAsMap">
      			<map>
      				<entry key="timeout" value="5"/>
      			</map>
      		</property>
      	</bean>
      
      	<bean name="mainJob" class="apps.bizlink.logic.scheduling.TimerScheduler"></bean>
      	<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
      		<property name="targetObject" ref="mainJob"/>
      		<property name="targetMethod" value="execute"/>
      		<property name="concurrent" value="false"/>
      	</bean>
      
      	<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
      		<property name="jobDetail" ref="mainJobDetail"/>
      		<property name="startDelay" value="2000"/>
      		<property name="repeatInterval" value="60000"/>
      	</bean>
      
      	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
      		<property name="triggers">
      			<list>
      				<ref bean="simpleTrigger"/>
      			</list>
      		</property>
      	</bean>
      Cheers !

      Comment


      • #4
        what is the proper way to create a Runnable instance via Spring
        Following configurations and code taken from http://static.springsource.org/sprin...tation-support

        Code:
        <task:annotation-driven executor="myExecutor" />
        
        <task:executor id="myExecutor" pool-size="5"/>
        Code:
        @Scheduled(fixedDelay=5000)
        public void doSomething() {
            // something that should execute periodically
        }
        I copied the annotation based configurations and code since your application is fully annotation driven

        http://static.springsource.org/sprin...eduling-quartz
        Last edited by amiladomingo; Jan 12th, 2011, 02:58 PM.

        Comment


        • #5
          Hi amiladomingo,

          Thanks for your reply.

          I'll implement the annotation declarative method from the documentation pages, but that the part of scheduling something is already working.

          The Runnable's that i try to wire via Spring are initiated dynamicly via the job handlers, so e.g. using the example code:

          Code:
          @Scheduled(fixedDelay=5000)
          public void doSomething() 
          {
          	Collection jobHandlers = this.getJobHandlersToRun();
          
          	for (Runnable jobHandler : jobHandlers)
          	{
          		executor.submit(jobHandler);
          	}
          }
          Inside the jobHandler, depending on the functionality of the jobHandler, new jobs are submitted to the executorService.

          The code above is pseudo code to clarify what i'm trying to get to. The 'jobs' can be multiple instances of the same class (e.g. monitor different websites using a MonitoringJob class), so using the @Scheduled annotation wouldnt be enough, since that would only spawn one thread without having a context (in my example the url to monitor).

          Comment


          • #6
            Originally posted by MariusZw View Post
            Hello !

            My name is Marius van Zwijndregt, and i'm trying to build an application with Spring and Hibernate.

            I'm using Spring 3.5 together with Hibernate 3.

            Almost everything in my application is annotation driven, and so are the transactions, though one part, Runnables triggered via Spring's ThreadPoolTaskExecutor do not seem to get a proper Hibernate session attached to the thread.

            The code which triggers a new Runnable (in my case called a job):

            The following is triggered to queue a job 'handler':

            Code:
            MonitoringJobHandler job = new MonitoringJobHandler();
            
            BaseWorkerJobFactory.addJob(job);
            Inside the BaseWorkerJobFactory, the addJob does the following:

            Code:
            if (!jobIsRunning(job))
            {
            	getJobList().add(job);
            	executor.submit(job);
            }
            The executor is a ThreadPoolTaskExecutor extended object, constructed as static variable in the BaseWorkerJobFactory class.

            The job 'handlers' will determine new jobs to queue, which are also submitted to the same executor.

            This works, however, when trying to call getCurrentSession() inside the jobhandler, i'll get the following exception:

            Code:
            org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
            	at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
            	at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:685)
            	at core.models.objects.data.BaseDaoFactory.getSession(BaseDaoFactory.java:35)
            	at apps.bizlink.models.Network.getExpiredMonitoringJobs(Network.java:250)
            	at apps.bizlink.logic.scheduling.handlers.MonitoringJobHandler.runTask(MonitoringJobHandler.java:42)
            	at core.logic.scheduling.BaseWorkerJob.run(BaseWorkerJob.java:192)
            	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
            	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
            	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
            	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
            	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
            	at java.lang.Thread.run(Thread.java:619)

            As sessionfactory i'm using a AnnotationSessionFactoryBean, together with HibernateTransactionManager.

            Im fairly new to Spring, but i guess this is because the instance is created outside of the Spring context.

            At the moment, i've created a custom annotation @JobHandler which is scanned at application startup though all classes.
            The list of JobHandler classes which is build up, are instantiated (via a quartz timer) and the instances are submitted to the executorService (In the above example i've made it a static instantation to clarify).
            Hopefully i can keep this way of logistics with a Spring solution.

            Now for my question; what is the proper way to create a Runnable instance via Spring, the way i'm using it now ?

            Thanks !
            Hey is this related to your problem http://forum.springsource.org/showthread.php?t=27009

            Comment


            • #7
              Hi Amila,

              Well roughly the problem looks the same.

              In my case, i do have a SessionFactory available from the Runnable instance, but no session gets attached to the thread.

              If i call an OpenSession on the sessionFactory from the Runnable, a Session is opened, but, after calling the OpenSession, and trying to do a getCurrentSession(), i will get the "No Hibernate Session bound to thread" message.

              So, as example code:

              Code:
              class MonitoringJob implements Runnable 
              {
              
              
              	private MonitorSite monitorSite = null;
              
              	public MonitorSite getMonitorSite()
              	{
              		return monitorSite;
              	}
              
              
              	@Transactional()
              	public void run() 
              	{
              		Session se = BaseDaoFactory.openSession();
              		try
              		{
              			// Session is available
              			MonitorSite s = this.getMonitorSite();
              			s.monitor();
              			
              		}
              		finally
              		{
              			se.close();
              		}
              
              
              	}
              }
              
              
              class MonitorSite
              {
              	private String url;
              
              	public void monitor()
              	{
              		// MZ: getCurrentSession is called on a static SessionFactory (is this problematic?)
              		// MZ: The next line will throw a "No Hibernate Session bound to thread" 
              		Session se = BaseDaoFactory.getCurrentSession();		
              		
              	}
              }
              So, the MonitoringJob is submitted to the executor, which will execute the job.

              Comment


              • #8
                Update on this issue:

                To get around my issue, i've added the following:

                Code:
                public static Session getSession()
                {
                	Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                	try
                	{
                		// MZ: Needed to place the hibernate session in context with the current thread (eg. Runnables)
                		TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
                	}
                	catch(Exception e)
                	{
                		Logger.getLog().debug("Session already bound");
                    }
                
                	// MZ: Forces hibernate to flush the session for each statement
                	session.setFlushMode(FlushMode.ALWAYS);
                	return session;
                }
                In my case, the sessionFactory is a static reference.

                After using the above to retrieve the current (or new) session, my Runnables can use the hibernate session inside the thread.

                Comment


                • #9
                  Originally posted by MariusZw View Post
                  Update on this issue:

                  To get around my issue, i've added the following:

                  Code:
                  public static Session getSession()
                  {
                  	Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                  	try
                  	{
                  		// MZ: Needed to place the hibernate session in context with the current thread (eg. Runnables)
                  		TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
                  	}
                  	catch(Exception e)
                  	{
                  		Logger.getLog().debug("Session already bound");
                      }
                  
                  	// MZ: Forces hibernate to flush the session for each statement
                  	session.setFlushMode(FlushMode.ALWAYS);
                  	return session;
                  }
                  In my case, the sessionFactory is a static reference.

                  After using the above to retrieve the current (or new) session, my Runnables can use the hibernate session inside the thread.
                  Hi Marius,

                  I have exactly the same problem...need Runnables to do database updates. Is the getSession method in your Runnable ?

                  Also, did you change the other bits of your code from what was listed in previous messages ? Please let me know if you
                  would be able to post/send the completed solution.

                  Comment


                  • #10
                    Originally posted by Slidewayz View Post
                    Hi Marius,

                    I have exactly the same problem...need Runnables to do database updates. Is the getSession method in your Runnable ?

                    Also, did you change the other bits of your code from what was listed in previous messages ? Please let me know if you
                    would be able to post/send the completed solution.
                    Hi Sidewayz,

                    In my case, im using one class which has several static helper functions. In it, the most important ones for the Runnables are the getSession() and closeSession(). The getSession is the one posted above, but the closeSession() also detaches the session;

                    Code:
                    	
                    public static void closeSession(Session se)
                    	{
                    		try
                    		{
                    			if (se != null && se.isOpen() && se.isConnected())
                    			{
                    				se.flush();
                    				se.clear();
                    				se.close();
                    				TransactionSynchronizationManager.unbindResource(sessionFactory);
                    			}
                    		}
                    		catch (HibernateException e)
                    		{
                    			Logger.getLog().error("Could not flush session", e);
                    		}
                    	}
                    My runnable has the following code:

                    Code:
                    	@Override
                    	public void runTask()
                    	{
                    
                    		Session se = BaseDaoFactory.getSession();
                    		try
                    		{
                    			this.setStatusLine("Processing image: " + this.getImage().getLabel());
                    
                    			// MZ: Create the scaled cached versions
                    			this.getImage().updateScaledCache(BaseApplication.getInstance().rootDirectory + "/images/", false);
                    
                    			// MZ: Save the updated state
                    			this.getImage().save();
                    
                    			this.setStatusLine("Finished image: " + this.getImage().getLabel());
                    		}
                    		finally
                    		{
                    			BaseDaoFactory.closeSession(se);
                    		}
                    	}
                    Let me know if you need more info !

                    Comment

                    Working...
                    X