Changeset 1031
- Timestamp:
- 10/11/06 11:16:07 (2 years ago)
- 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 65 65 <constructor-arg ref="sessionFactory"/> 66 66 </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 68 75 <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> 69 76 <description> -
trunk/components/server/resources/ome/services/out-of-container.xml
r921 r1031 59 59 60 60 <bean id="transactionManager" lazy-init="true" 61 class="org.springframework. orm.hibernate3.HibernateTransactionManager">61 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 62 62 <description> 63 63 Scope: private 64 64 </description> 65 <property name="dataSource" ref="dataSource"/> 66 <property name="sessionFactory" ref="sessionFactory"/> 65 <constructor-arg ref="dataSource"/> 67 66 </bean> 68 67 -
trunk/components/server/resources/ome/services/service-ome.api.RawPixelsStore.xml
r903 r1031 48 48 </bean> 49 49 50 <bean id="managed:ome.api.RawPixelsStore" parent="managedS ervice">50 <bean id="managed:ome.api.RawPixelsStore" parent="managedStatefulService"> 51 51 <property name="proxyInterfaces" value="ome.api.RawPixelsStore"/> 52 52 <property name="target" ref="internal:ome.api.RawPixelsStore"/> -
trunk/components/server/resources/ome/services/service-omeis.providers.re.RenderingEngine.xml
r1024 r1031 44 44 </bean> 45 45 46 <bean id="managed:omeis.providers.re.RenderingEngine" parent="managedS ervice">46 <bean id="managed:omeis.providers.re.RenderingEngine" parent="managedStatefulService"> 47 47 <property name="proxyInterfaces" value="omeis.providers.re.RenderingEngine"/> 48 48 <property name="target" ref="internal:omeis.providers.re.RenderingEngine"/> -
trunk/components/server/resources/ome/services/services.xml
r903 r1031 57 57 <value>proxyHandler</value> 58 58 <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> 59 71 <value>sessionHandler</value> 60 72 <value>eventHandler</value> 61 73 </list> 62 74 </property> 63 </bean> 64 75 </bean> 65 76 <bean 66 77 id="serviceHandler" … … 72 83 Service Levels 73 84 ======================================================================= 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. 74 91 --> 75 92 <bean id="bean" abstract="true"> -
trunk/components/server/src/ome/tools/hibernate/SessionHandler.java
r837 r1031 115 115 .synchronizedMap(new WeakHashMap<Object, SessionStatus>()); 116 116 117 private HibernateInterceptor delegate;118 119 117 private DataSource dataSource; 120 118 … … 131 129 public SessionHandler(DataSource dataSource, SessionFactory factory) 132 130 { 133 if ( dataSource == null | factory == null )131 if ( dataSource == null || factory == null ) 134 132 { 135 133 throw new ApiUsageException(CTOR_MSG); … … 137 135 138 136 this.dataSource = dataSource; 139 this.delegate = new HibernateInterceptor();140 137 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 164 140 /** 165 141 * delegates to {@link HibernateInterceptor} or manages sessions internally, 166 142 * based on the type of service. 167 143 */ 168 public Object invoke( MethodInvocation invocation) throws Throwable144 public Object invoke(final MethodInvocation invocation) throws Throwable 169 145 { 170 146 // Stateless; normal semantics. … … 172 148 invocation.getThis().getClass())) 173 149 { 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 178 154 // Stateful; let's get to work. 179 155 debug("Performing action in stateful session."); … … 224 200 } 225 201 226 private voidnewOrRestoredSession(MethodInvocation invocation)202 private SessionStatus newOrRestoredSession(MethodInvocation invocation) 227 203 throws HibernateException 228 204 { 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 } 229 241 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 { 259 244 debug("Binding and reconnecting session."); 260 245 bindSession(status.session); … … 264 249 // It's ready to be used. Increment. 265 250 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 269 330 private void closeSession() throws Exception 270 331 { … … 278 339 session.connection().commit(); 279 340 sessionBoundToThread().close(); 280 } catch (Exception e) 281 { 282 throw e; 283 } finally 341 } 342 343 finally 284 344 { 285 345 resetThreadSession(); … … 291 351 292 352 } 293 294 // ~ SESSIONS295 // =========================================================================296 297 private boolean isCloseSession(MethodInvocation invocation)298 {299 return "destroy".equals(invocation.getMethod().getName());300 }301 302 private Session acquireBindAndConfigureSession() throws HibernateException303 {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 HibernateException345 {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 HibernateException357 {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 }368 353 369 354 private void debug(String message) -
trunk/components/server/test/ome/server/utests/handlers/SessionHandlerMockHibernateTest.java
r960 r1031 47 47 import org.jmock.Mock; 48 48 import org.jmock.MockObjectTestCase; 49 import org.jmock.builder. MatchBuilder;49 import org.jmock.builder.ArgumentsMatchBuilder; 50 50 import org.jmock.core.Invocation; 51 51 import org.jmock.core.InvocationMatcher; 52 52 import org.jmock.core.Stub; 53 53 import org.jmock.core.stub.DefaultResultStub; 54 import org.springframework.orm.hibernate3.HibernateInterceptor;55 54 import org.springframework.orm.hibernate3.SessionHolder; 56 55 import org.springframework.transaction.support.TransactionSynchronizationManager; … … 70 69 * @since Omero 2.0 71 70 */ 72 @Test( groups = {" ignore","sessions","hibernate","priority"} )71 @Test( groups = {"hibernate","stateful","ticket:326"} ) 73 72 public class SessionHandlerMockHibernateTest extends MockObjectTestCase 74 73 { … … 108 107 // Things should always be cleaned up by handler/interceptor 109 108 assertFalse( TransactionSynchronizationManager.hasResource(factory) ); 109 if (!TransactionSynchronizationManager.isSynchronizationActive()) 110 TransactionSynchronizationManager.initSynchronization(); 110 111 } 111 112 … … 113 114 protected void tearDown() throws Exception 114 115 { 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(); 115 125 super.tearDown(); 126 if (TransactionSynchronizationManager.isSynchronizationActive()) 127 TransactionSynchronizationManager.clearSynchronization(); 116 128 } 117 129 … … 120 132 121 133 @Test 134 @ExpectedExceptions( InternalException.class ) 122 135 public void testStatelessInvocation() throws Throwable 123 136 { 124 137 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 ); 135 139 handler.invoke( invocation ); 136 140 super.verify(); … … 138 142 139 143 @Test 140 public void testStatefulInvocation () throws Throwable144 public void testStatefulInvocationGetsNewSession() throws Throwable 141 145 { 142 146 newStatefulReadInvocation(); 143 147 opensSession(); 148 setsFlushMode(); 144 149 beginsTransaction(1); 145 150 checksSessionIsOpen(); 146 151 getsFactoryFromSession(); 147 152 getsAutoFlushMode(); 153 // invocation here 148 154 checksSessionIsConnected(); 149 155 disconnectsSession(); … … 153 159 154 160 @Test 155 public void test TwoStatefulInvocations() throws Throwable161 public void testSecondStatefulInvocationsReusesSession() throws Throwable 156 162 { 157 163 newStatefulReadInvocation(); 158 164 opensSession(); 165 setsFlushMode(); 159 166 beginsTransaction(2); 160 167 checksSessionIsOpen(); 161 168 getsFactoryFromSession(); 162 169 getsAutoFlushMode(); 163 checksSessionIsConnected(); 164 disconnectsSession(); 165 handler.invoke( invocation ); 170 // invocation here 171 checksSessionIsConnected(); 172 disconnectsSession(); 173 handler.invoke( invocation ); 174 166 175 // And a second call should just work. 167 176 newStatefulReadInvocation(); 177 // invocation here 178 checksSessionIsConnected(); 179 disconnectsSession(); 168 180 handler.invoke( invocation ); 169 181 super.verify(); … … 171 183 172 184 @Test 173 @ExpectedExceptions( InternalException.class)185 @ExpectedExceptions( InternalException.class ) 174 186 public void testStatefulInvocationWithExistingSession() throws Throwable 175 187 { 176 // setup Session 177 prepareThread(); 188 prepareThreadWithSession(); 178 189 179 190 newStatefulReadInvocation(); 180 191 checksSessionIsOpen(); 181 //checksSessionIsConnected(); 182 getsFactoryFromSession(); 192 checksSessionIsConnected(); 193 getsFactoryFromSession(); 194 getsAutoFlushMode(); 183 195 disconnectsSession(); 184 196 closesSession(); … … 188 200 189 201 @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 190 222 public void testStatefulInvocationWithSessionThenClosed() throws Throwable 191 223 { … … 194 226 getsFactoryFromSession(); 195 227 getsAutoFlushMode(); 228 setsFlushMode(); 196 229 opensSession(); 230 // setsFlushMode(); TODO huh? 197 231 beginsTransaction(1); 198 232 getsSessionsConnection(); … … 204 238 205 239 @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();
