• Views
  • Iteration Report
  • My Iteration Report
  •  
OMERO.server
  • Login
  • Help/Guide
  • About Trac
  • Preferences
  • Wiki
  • Timeline
  • Roadmap
  • Browse Source
  • View Tickets
  • Search

Context Navigation

  • ← Previous Changeset
  • Next Changeset →

Changeset 1031

Show
Ignore:
Timestamp:
10/11/06 11:16:07 (2 years ago)
Author:
jmoore
Message:

#326 Reworking of SessionHandler?

Location:
trunk/components/server
Files:
7 modified

  • resources/ome/services/hibernate.xml (modified) (1 diff)
  • resources/ome/services/out-of-container.xml (modified) (1 diff)
  • resources/ome/services/service-ome.api.RawPixelsStore.xml (modified) (1 diff)
  • resources/ome/services/service-omeis.providers.re.RenderingEngine.xml (modified) (1 diff)
  • resources/ome/services/services.xml (modified) (2 diffs)
  • src/ome/tools/hibernate/SessionHandler.java (modified) (8 diffs)
  • test/ome/server/utests/handlers/SessionHandlerMockHibernateTest.java (modified) (19 diffs)

Legend:

Unmodified
Added
Removed
  • trunk/components/server/resources/ome/services/hibernate.xml

    r952 r1031  
    6565    <constructor-arg ref="sessionFactory"/>     
    6666  </bean> 
    67      
     67 
     68  <bean id="hibernateHandler" class="org.springframework.orm.hibernate3.HibernateInterceptor"> 
     69    <description> 
     70    Scope: private 
     71    </description> 
     72    <property name="sessionFactory" ref="sessionFactory"/> 
     73  </bean> 
     74 
    6875  <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> 
    6976    <description> 
  • trunk/components/server/resources/ome/services/out-of-container.xml

    r921 r1031  
    5959   
    6060  <bean id="transactionManager" lazy-init="true" 
    61         class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
     61        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    6262    <description> 
    6363    Scope: private 
    6464    </description> 
    65     <property name="dataSource" ref="dataSource"/> 
    66     <property name="sessionFactory" ref="sessionFactory"/> 
     65    <constructor-arg ref="dataSource"/> 
    6766  </bean> 
    6867 
  • trunk/components/server/resources/ome/services/service-ome.api.RawPixelsStore.xml

    r903 r1031  
    4848  </bean> 
    4949 
    50   <bean id="managed:ome.api.RawPixelsStore" parent="managedService"> 
     50  <bean id="managed:ome.api.RawPixelsStore" parent="managedStatefulService"> 
    5151    <property name="proxyInterfaces" value="ome.api.RawPixelsStore"/> 
    5252    <property name="target" ref="internal:ome.api.RawPixelsStore"/> 
  • trunk/components/server/resources/ome/services/service-omeis.providers.re.RenderingEngine.xml

    r1024 r1031  
    4444  </bean> 
    4545 
    46   <bean id="managed:omeis.providers.re.RenderingEngine" parent="managedService"> 
     46  <bean id="managed:omeis.providers.re.RenderingEngine" parent="managedStatefulService"> 
    4747    <property name="proxyInterfaces" value="omeis.providers.re.RenderingEngine"/> 
    4848    <property name="target" ref="internal:omeis.providers.re.RenderingEngine"/> 
  • trunk/components/server/resources/ome/services/services.xml

    r903 r1031  
    5757                                <value>proxyHandler</value> 
    5858                                <value>transactionHandler</value> 
     59                                <value>hibernateHandler</value> 
     60                                <value>eventHandler</value> 
     61                        </list> 
     62                </property> 
     63        </bean> 
     64        <bean id="managedStatefulService" abstract="true" 
     65                class="org.springframework.aop.framework.ProxyFactoryBean"> 
     66                <property name="interceptorNames"> 
     67                        <list> 
     68                                <value>serviceHandler</value> 
     69                                <value>proxyHandler</value> 
     70                                <value>transactionHandler</value> 
    5971                                <value>sessionHandler</value> 
    6072                                <value>eventHandler</value> 
    6173                        </list> 
    6274                </property> 
    63         </bean> 
    64    
     75        </bean>   
    6576        <bean  
    6677                       id="serviceHandler"  
    … …  
    7283  Service Levels 
    7384  ======================================================================= 
     85  Abstract definitions of services which can be used when configuring 
     86  concrete beans. Note: these Spring definitions can have a possibly  
     87  separate inheritance hierarchy to that defined in Java. When defining 
     88  a parent bean for a concrete service, be sure that all required properties 
     89  are (A) filled by the abstract Spring definition and (B) available on the 
     90  Java class.   
    7491--> 
    7592  <bean id="bean" abstract="true"> 
  • trunk/components/server/src/ome/tools/hibernate/SessionHandler.java

    r837 r1031  
    115115                                                        .synchronizedMap(new WeakHashMap<Object, SessionStatus>()); 
    116116 
    117     private HibernateInterceptor       delegate; 
    118  
    119117    private DataSource                 dataSource; 
    120118 
    … …  
    131129    public SessionHandler(DataSource dataSource, SessionFactory factory) 
    132130    { 
    133         if ( dataSource == null | factory == null ) 
     131        if ( dataSource == null || factory == null ) 
    134132        { 
    135133            throw new ApiUsageException(CTOR_MSG); 
    … …  
    137135 
    138136        this.dataSource = dataSource; 
    139         this.delegate = new HibernateInterceptor(); 
    140137        this.factory = factory; 
    141  
    142         this.delegate.setSessionFactory(factory); 
    143     } 
    144      
    145     /** constructor taking a {@link DataSource} and a {@link HibernateInterceptor} 
    146      * as arguments. The needed {@link SessionFactory} will be taken from the 
    147      * interceptor's {@link HibernateInterceptor#getSessionFactory() getSessionFactory} 
    148      * method.   
    149      * @param dataSource Not null. 
    150      * @param interceptor Not null. 
    151      */ 
    152     public SessionHandler(DataSource dataSource, HibernateInterceptor interceptor) 
    153     { 
    154         if ( dataSource == null | interceptor == null ) 
    155         { 
    156             throw new ApiUsageException(CTOR_MSG); 
    157         } 
    158          
    159         this.dataSource = dataSource; 
    160         this.delegate = interceptor; 
    161         this.factory = interceptor.getSessionFactory(); 
    162     } 
    163  
     138    } 
     139     
    164140    /** 
    165141     * delegates to {@link HibernateInterceptor} or manages sessions internally, 
    166142     * based on the type of service. 
    167143     */ 
    168     public Object invoke(MethodInvocation invocation) throws Throwable 
     144    public Object invoke(final MethodInvocation invocation) throws Throwable 
    169145    { 
    170146        // Stateless; normal semantics. 
    … …  
    172148                invocation.getThis().getClass())) 
    173149        { 
    174             debug("Delegating session creation to HibernateInterceptor."); 
    175             return delegate.invoke(invocation); 
    176         } 
    177  
     150                throw new InternalException(  
     151                                "Stateless service configured as stateful." ); 
     152        } 
     153         
    178154        // Stateful; let's get to work. 
    179155        debug("Performing action in stateful session."); 
    … …  
    224200    } 
    225201 
    226     private void newOrRestoredSession(MethodInvocation invocation) 
     202    private SessionStatus newOrRestoredSession(MethodInvocation invocation) 
    227203            throws HibernateException 
    228204    { 
     205 
     206        SessionStatus status = sessions.get(invocation.getThis()); 
     207        Session previousSession = ! isSessionBoundToThread() ? null :  
     208                sessionBoundToThread(); 
     209 
     210        // a session is currently running. 
     211        // something has gone wrong (e.g. with cleanup) abort! 
     212        if ( previousSession != null ) 
     213        { 
     214                        String msg = "Dirty Hibernate Session " 
     215                                + sessionBoundToThread() + " found in Thread " 
     216                                + Thread.currentThread(); 
     217 
     218                        sessionBoundToThread().close(); 
     219                        resetThreadSession(); 
     220                        throw new InternalException(msg); 
     221        }  
     222         
     223        // we may or may not be in a session, but if we haven't yet bound  
     224        // it to This, then we need to. 
     225        else if (status == null || !status.session.isOpen()) 
     226        { 
     227            Session currentSession = acquireBindAndConfigureSession(); 
     228            status = new SessionStatus( currentSession ); 
     229            sessions.put(invocation.getThis(), status); 
     230        }  
     231 
     232        // the session bound to This is already currently being called. abort! 
     233        else if (status.calls > 1) 
     234        { 
     235            throw new InternalException( 
     236                    "Hibernate session is not re-entrant.\n" + 
     237                    "Either you have two threads operating on the same " + 
     238                    "stateful object (don't do this)\n or you have a " + 
     239                    "recursive call (recurse on the unwrapped object). "); 
     240        } 
    229241         
    230         if (isSessionBoundToThread()) 
    231         { 
    232  
    233             String msg = "Dirty Hibernate Session " 
    234                     + sessionBoundToThread() + " found in Thread " 
    235                     + Thread.currentThread(); 
    236  
    237             sessionBoundToThread().close(); 
    238             resetThreadSession(); 
    239             throw new InternalException(msg); 
    240         } 
    241          
    242         SessionStatus status = sessions.get(invocation.getThis()); 
    243  
    244         if (status == null || !status.session.isOpen()) 
    245         { 
    246              
    247             debug("Replacing null or closed session."); 
    248             status = new SessionStatus(acquireBindAndConfigureSession()); 
    249             sessions.put(invocation.getThis(), status); 
    250         } else 
    251         { 
    252             if (status.calls > 1) 
    253                 throw new InternalException( 
    254                         "Hibernate session is not re-entrant.\n" + 
    255                         "Either you have two threads operating on the same " + 
    256                         "stateful object (don't do this)\n or you have a " + 
    257                         "recursive call (recurse on the unwrapped object). "); 
    258  
     242        // all is fine. 
     243        else { 
    259244            debug("Binding and reconnecting session."); 
    260245            bindSession(status.session); 
    … …  
    264249        // It's ready to be used. Increment. 
    265250        status.calls++; 
    266  
    267     } 
    268  
     251        return status; 
     252 
     253    } 
     254 
     255    // ~ SESSIONS 
     256    // ========================================================================= 
     257 
     258    private boolean isCloseSession(MethodInvocation invocation) 
     259    { 
     260        return "destroy".equals(invocation.getMethod().getName()); 
     261    } 
     262 
     263    private Session acquireBindAndConfigureSession() throws HibernateException 
     264    { 
     265        debug("Opening and binding session."); 
     266        Session session = factory.openSession(); 
     267        session.setFlushMode( FlushMode.COMMIT ); 
     268        bindSession(session); 
     269        return session; 
     270    } 
     271 
     272    private void bindSession(Session session)  
     273    { 
     274        debug("Binding session to thread."); 
     275        SessionHolder sessionHolder = new SessionHolder(session); 
     276        sessionHolder.setTransaction(sessionHolder.getSession() 
     277                .beginTransaction()); 
     278        TransactionSynchronizationManager.bindResource(factory, sessionHolder); 
     279        if ( ! TransactionSynchronizationManager.isSynchronizationActive()) 
     280                throw new InternalException( "Synchronization not active for " + 
     281                                "TransactionSynchronizationManager"); 
     282    } 
     283     
     284    private Session sessionBoundToThread() 
     285    { 
     286        return SessionFactoryUtils.getSession(factory, false); 
     287    } 
     288 
     289    private boolean isSessionBoundToThread() 
     290    { 
     291        return TransactionSynchronizationManager.hasResource(factory) 
     292                && sessionBoundToThread() != null; 
     293    } 
     294 
     295    private void resetThreadSession() 
     296    { 
     297        if (isSessionBoundToThread()) 
     298        { 
     299            debug("Session bound to thread. Reseting."); 
     300            TransactionSynchronizationManager.unbindResource(factory); 
     301        } else { 
     302            debug("Session not bound to thread. No need to reset."); 
     303        } 
     304    } 
     305 
     306    private void reconnectSession(Session session) throws HibernateException 
     307    { 
     308        if (!session.isConnected()) 
     309        { 
     310            debug("Session not connected. Connecting."); 
     311            Connection connection = DataSourceUtils.getConnection(dataSource); 
     312            session.reconnect(connection); 
     313        } else { 
     314            debug("Session already connected. Not reconnecting."); 
     315        } 
     316    } 
     317 
     318    private void disconnectSession() throws HibernateException 
     319    { 
     320        if (isSessionBoundToThread() && sessionBoundToThread().isConnected()) 
     321        { 
     322            debug("Session bound to thread. Disconnecting."); 
     323            sessionBoundToThread().disconnect(); 
     324        } else { 
     325            debug("No session bound to thread. Can't disconnect."); 
     326        } 
     327         
     328    } 
     329     
    269330    private void closeSession() throws Exception 
    270331    { 
    … …  
    278339                session.connection().commit(); 
    279340                sessionBoundToThread().close(); 
    280             } catch (Exception e) 
    281             { 
    282                 throw e; 
    283             } finally 
     341            }  
     342             
     343            finally 
    284344            { 
    285345                resetThreadSession(); 
    … …  
    291351 
    292352    } 
    293  
    294     // ~ SESSIONS 
    295     // ========================================================================= 
    296  
    297     private boolean isCloseSession(MethodInvocation invocation) 
    298     { 
    299         return "destroy".equals(invocation.getMethod().getName()); 
    300     } 
    301  
    302     private Session acquireBindAndConfigureSession() throws HibernateException 
    303     { 
    304         debug("Opening and binding session."); 
    305         Session session = factory.openSession(); 
    306         session.setFlushMode( FlushMode.COMMIT ); 
    307         bindSession(session); 
    308         return session; 
    309     } 
    310  
    311     private void bindSession(Session session)  
    312     { 
    313         debug("Binding session to thread."); 
    314         SessionHolder sessionHolder = new SessionHolder(session); 
    315         sessionHolder.setTransaction(sessionHolder.getSession() 
    316                 .beginTransaction()); 
    317         TransactionSynchronizationManager.bindResource(factory, sessionHolder); 
    318         TransactionSynchronizationManager.initSynchronization(); 
    319     } 
    320      
    321     private Session sessionBoundToThread() 
    322     { 
    323         return SessionFactoryUtils.getSession(factory, false); 
    324     } 
    325  
    326     private boolean isSessionBoundToThread() 
    327     { 
    328         return TransactionSynchronizationManager.hasResource(factory) 
    329                 && sessionBoundToThread() != null; 
    330     } 
    331  
    332     private void resetThreadSession() 
    333     { 
    334         if (isSessionBoundToThread()) 
    335         { 
    336             debug("Session bound to thread. Reseting."); 
    337             TransactionSynchronizationManager.unbindResource(factory); 
    338             TransactionSynchronizationManager.clearSynchronization(); 
    339         } else { 
    340             debug("Session not bound to thread. No need to reset."); 
    341         } 
    342     } 
    343  
    344     private void reconnectSession(Session session) throws HibernateException 
    345     { 
    346         if (!session.isConnected()) 
    347         { 
    348             debug("Session not connected. Connecting."); 
    349             Connection connection = DataSourceUtils.getConnection(dataSource); 
    350             session.reconnect(connection); 
    351         } else { 
    352             debug("Session already connected. Not reconnecting."); 
    353         } 
    354     } 
    355  
    356     private void disconnectSession() throws HibernateException 
    357     { 
    358         if (isSessionBoundToThread() 
    359                 && SessionFactoryUtils.getSession(factory, false).isConnected()) 
    360         { 
    361             debug("Session bound to thread. Disconnecting."); 
    362             sessionBoundToThread().disconnect(); 
    363         } else { 
    364             debug("No session bound to thread. Can't disconnect."); 
    365         } 
    366          
    367     } 
    368353     
    369354    private void debug(String message) 
  • trunk/components/server/test/ome/server/utests/handlers/SessionHandlerMockHibernateTest.java

    r960 r1031  
    4747import org.jmock.Mock; 
    4848import org.jmock.MockObjectTestCase; 
    49 import org.jmock.builder.MatchBuilder; 
     49import org.jmock.builder.ArgumentsMatchBuilder; 
    5050import org.jmock.core.Invocation; 
    5151import org.jmock.core.InvocationMatcher; 
    5252import org.jmock.core.Stub; 
    5353import org.jmock.core.stub.DefaultResultStub; 
    54 import org.springframework.orm.hibernate3.HibernateInterceptor; 
    5554import org.springframework.orm.hibernate3.SessionHolder; 
    5655import org.springframework.transaction.support.TransactionSynchronizationManager; 
    … …  
    7069 * @since Omero 2.0 
    7170 */ 
    72 @Test( groups = {"ignore","sessions","hibernate","priority"} ) 
     71@Test( groups = {"hibernate","stateful","ticket:326"} ) 
    7372public class SessionHandlerMockHibernateTest extends MockObjectTestCase 
    7473{ 
    … …  
    108107        // Things should always be cleaned up by handler/interceptor 
    109108        assertFalse( TransactionSynchronizationManager.hasResource(factory) ); 
     109        if (!TransactionSynchronizationManager.isSynchronizationActive()) 
     110                TransactionSynchronizationManager.initSynchronization(); 
    110111    } 
    111112 
    … …  
    113114    protected void tearDown() throws Exception 
    114115    { 
     116        session = null; 
     117        mockStateful.reset(); 
     118        mockStateless.reset(); 
     119        mockSession.reset(); 
     120        mockFactory.reset(); 
     121        mockTransaction.reset(); 
     122        mockDataSource.reset(); 
     123        mockConnection.reset(); 
     124        mockInvocation.reset(); 
    115125        super.tearDown(); 
     126        if (TransactionSynchronizationManager.isSynchronizationActive()) 
     127                TransactionSynchronizationManager.clearSynchronization(); 
    116128    } 
    117129 
    … …  
    120132 
    121133    @Test 
     134    @ExpectedExceptions( InternalException.class ) 
    122135    public void testStatelessInvocation() throws Throwable 
    123136    { 
    124137        newStatelessInvocation(); 
    125         HibernateInterceptor interceptor = new HibernateInterceptor() { 
    126             @Override 
    127             public Object invoke(MethodInvocation methodInvocation) throws Throwable 
    128             { 
    129                 return null; 
    130             } 
    131         }; 
    132         interceptor.setSessionFactory( factory ); 
    133         // for testing stateless, we need control over the interceptor. 
    134         handler = new SessionHandler( dataSource, interceptor ); 
     138        handler = new SessionHandler( dataSource, factory ); 
    135139        handler.invoke( invocation ); 
    136140        super.verify(); 
    … …  
    138142 
    139143    @Test 
    140     public void testStatefulInvocation() throws Throwable 
     144    public void testStatefulInvocationGetsNewSession() throws Throwable 
    141145    { 
    142146        newStatefulReadInvocation(); 
    143147        opensSession(); 
     148        setsFlushMode(); 
    144149        beginsTransaction(1); 
    145150        checksSessionIsOpen(); 
    146151        getsFactoryFromSession(); 
    147152        getsAutoFlushMode(); 
     153        // invocation here 
    148154        checksSessionIsConnected(); 
    149155        disconnectsSession(); 
    … …  
    153159 
    154160    @Test 
    155     public void testTwoStatefulInvocations() throws Throwable 
     161    public void testSecondStatefulInvocationsReusesSession() throws Throwable 
    156162    { 
    157163        newStatefulReadInvocation(); 
    158164        opensSession(); 
     165        setsFlushMode(); 
    159166        beginsTransaction(2); 
    160167        checksSessionIsOpen(); 
    161168        getsFactoryFromSession(); 
    162169        getsAutoFlushMode(); 
    163         checksSessionIsConnected(); 
    164         disconnectsSession(); 
    165         handler.invoke( invocation ); 
     170        // invocation here 
     171        checksSessionIsConnected(); 
     172        disconnectsSession(); 
     173        handler.invoke( invocation ); 
     174         
    166175        // And a second call should just work. 
    167176        newStatefulReadInvocation(); 
     177        // invocation here 
     178        checksSessionIsConnected(); 
     179        disconnectsSession(); 
    168180        handler.invoke( invocation ); 
    169181        super.verify(); 
    … …  
    171183     
    172184    @Test 
    173     @ExpectedExceptions(InternalException.class) 
     185    @ExpectedExceptions( InternalException.class ) 
    174186    public void testStatefulInvocationWithExistingSession() throws Throwable 
    175187    { 
    176         // setup Session 
    177         prepareThread(); 
     188        prepareThreadWithSession(); 
    178189         
    179190        newStatefulReadInvocation(); 
    180191        checksSessionIsOpen(); 
    181         //checksSessionIsConnected(); 
    182         getsFactoryFromSession(); 
     192        checksSessionIsConnected(); 
     193        getsFactoryFromSession(); 
     194        getsAutoFlushMode(); 
    183195        disconnectsSession(); 
    184196        closesSession(); 
    … …  
    188200     
    189201    @Test 
     202    public void testClosedOnException() throws Throwable 
     203    { 
     204        prepareThreadWithSession(); 
     205 
     206        try { 
     207        newStatefulReadInvocationThrows(); 
     208        checksSessionIsOpen(); 
     209        checksSessionIsConnected(); 
     210        getsFactoryFromSession(); 
     211        getsAutoFlushMode(); 
     212        // here it throws 
     213        disconnectsSession(); 
     214        closesSession(); 
     215        handler.invoke( invocation ); 
     216        fail("Should have thrown."); 
     217        } catch (Exception e) 
     218        {} 
     219    } 
     220     
     221    @Test 
    190222    public void testStatefulInvocationWithSessionThenClosed() throws Throwable 
    191223    { 
    … …  
    194226        getsFactoryFromSession(); 
    195227        getsAutoFlushMode(); 
     228        setsFlushMode(); 
    196229        opensSession(); 
     230//        setsFlushMode(); TODO huh? 
    197231        beginsTransaction(1); 
    198232        getsSessionsConnection(); 
    … …  
    204238     
    205239    @Test 
    206     public void testSyncResetEvenOnException() throws Throwable 
    207     { 
    208         // setup Session 
    209         prepareThread(); 
    210  
    211         try { 
    212         newStatefulReadInvocation(); 
    213         checksSessionIsOpen(); 
    214         //checksSessionIsConnected(); 
    215         getsFactoryFromSession(); 
     240    @ExpectedExceptions( InternalException.class ) 
     241    public void testStatefulReentrantCallThrows() throws Throwable 
     242    { 
     243        Method method = RenderingEngine.class.getMethod("getDefaultZ"); 
     244        newStatefulInvocation( method, new Stub() { 
     245                public Object invoke(Invocation dummy) throws Throwable { 
     246                        handler.invoke( invocation ); 
     247                        return null; 
     248                } 
     249                public StringBuffer describeTo(StringBuffer buffer) { 
     250                        return buffer.append(" reentrant call "); 
     251                } 
     252        }); 
     253        opensSession(); 
     254        setsFlushMode(); 
     255        beginsTransaction(2); 
     256        checksSessionIsOpen(); 
     257        getsFactoryFromSession(); 
     258        getsAutoFlushMode(); 
     259        // invocation here 
     260        checksSessionIsConnected();