| /******************************************************************************* |
| * Copyright (c) 2008, 2013 Innoopract Informationssysteme GmbH and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Innoopract Informationssysteme GmbH - initial API and implementation |
| * EclipseSource - ongoing development |
| ******************************************************************************/ |
| package org.eclipse.rap.rwt.internal.lifecycle; |
| |
| import org.eclipse.rap.rwt.internal.application.ApplicationContextImpl; |
| import org.eclipse.rap.rwt.internal.service.ContextProvider; |
| import org.eclipse.rap.rwt.internal.service.ServiceContext; |
| import org.eclipse.rap.rwt.internal.service.ServletLog; |
| import org.eclipse.rap.rwt.internal.service.UISessionImpl; |
| import org.eclipse.rap.rwt.lifecycle.PhaseId; |
| import org.eclipse.rap.rwt.service.UISession; |
| import org.eclipse.swt.widgets.Display; |
| |
| |
| final class UIThread extends Thread implements IUIThreadHolder, ISessionShutdownAdapter { |
| |
| static final class UIThreadTerminatedError extends ThreadDeath { |
| private static final long serialVersionUID = 1L; |
| } |
| |
| private ServiceContext serviceContext; |
| private UISession uiSession; |
| private Runnable shutdownCallback; |
| private volatile boolean uiThreadTerminating; |
| |
| public UIThread( Runnable runnable ) { |
| super( runnable ); |
| } |
| |
| ////////////////////////// |
| // interface IThreadHolder |
| |
| public void setServiceContext( ServiceContext serviceContext ) { |
| this.serviceContext = serviceContext; |
| } |
| |
| public void updateServiceContext() { |
| if( ContextProvider.hasContext() ) { |
| ContextProvider.releaseContextHolder(); |
| } |
| ContextProvider.setContext( serviceContext ); |
| } |
| |
| public void switchThread() { |
| Object lock = getLock(); |
| synchronized( lock ) { |
| checkAndReportTerminatedUIThread(); |
| lock.notifyAll(); |
| boolean done = false; |
| while( !done ) { |
| try { |
| lock.wait(); |
| done = true; |
| } catch( InterruptedException e ) { |
| handleInterruptInSwitchThread( e ); |
| } |
| } |
| } |
| } |
| |
| private void checkAndReportTerminatedUIThread() { |
| // [rh] While working on bug 284202, there was the suspicion that a |
| // request thread might wait infinitely on an already terminated UIThread. |
| // To investigate this problem, we print to sys-err if this happens. |
| if( !getThread().isAlive() ) { |
| String msg |
| = "Thread '" |
| + Thread.currentThread() |
| + "' is waiting for already terminated UIThread"; |
| ServletLog.log( "", new RuntimeException( msg ) ); |
| } |
| } |
| |
| private void handleInterruptInSwitchThread( InterruptedException e ) |
| throws UIThreadTerminatedError |
| { |
| Thread.interrupted(); |
| if( uiThreadTerminating ) { |
| // Equip the UI thread that is continuing its execution with a |
| // service context and the proper phase (see terminateThread). |
| updateServiceContext(); |
| CurrentPhase.set( PhaseId.PROCESS_ACTION ); |
| uiThreadTerminating = false; |
| throw new UIThreadTerminatedError(); |
| } |
| if( Thread.currentThread() != getThread() ) { |
| String msg = "Received InterruptedException on request thread"; |
| ServletLog.log( msg, e ); |
| } |
| } |
| |
| @Override |
| public void run() { |
| try { |
| super.run(); |
| } finally { |
| // TODO [rh] call lock.notifyAll()? |
| } |
| } |
| |
| public void terminateThread() { |
| // Prepare a service context to be used by the UI thread that may continue |
| // to run as a result of the interrupt call |
| ServiceContext serviceContext = ContextUtil.createFakeContext( uiSession ); |
| setServiceContext( serviceContext ); |
| uiThreadTerminating = true; |
| // interrupt the UI thread that is expected to wait in switchThread or already be terminated |
| synchronized( getLock() ) { |
| getThread().interrupt(); |
| } |
| try { |
| getThread().join(); |
| } catch( InterruptedException e ) { |
| String msg = "Received InterruptedException while terminating UIThread"; |
| ServletLog.log( msg, e ); |
| } |
| uiThreadTerminating = false; |
| } |
| |
| public Thread getThread() { |
| return this; |
| } |
| |
| public Object getLock() { |
| // TODO [rh] use a distinct (final) lock object instead of 'this' |
| return this; |
| } |
| |
| |
| //////////////////////////////////// |
| // interface ISessionShutdownAdapter |
| |
| public void setUISession( UISession uiSession ) { |
| this.uiSession = uiSession; |
| } |
| |
| public void setShutdownCallback( Runnable shutdownCallback ) { |
| this.shutdownCallback = shutdownCallback; |
| } |
| |
| public void interceptShutdown() { |
| terminateThread(); |
| } |
| |
| public void processShutdown() { |
| updateServiceContext(); |
| try { |
| // Simulate PROCESS_ACTION phase if the session times out |
| CurrentPhase.set( PhaseId.PROCESS_ACTION ); |
| // TODO [rh] find a better decoupled way to dispose of the display |
| Display display = LifeCycleUtil.getSessionDisplay( uiSession ); |
| // TODO [fappel]: Think about a better solution: isActivated() checks whether |
| // the applicationContext is still activated before starting |
| // cleanup. This is due to the missing possibility of OSGi HttpService |
| // to shutdown HttpContext instances. Therefore sessions will survive the |
| // deactivation of ApplicationContext instances. In case the HttpService |
| // gets halted the corresponding ApplicationContext instances have already |
| // been deactivated and this will cause a NPE. |
| if( isApplicationContextActive() && display != null ) { |
| display.dispose(); |
| } |
| shutdownCallback.run(); |
| } finally { |
| ContextProvider.disposeContext(); |
| } |
| } |
| |
| private boolean isApplicationContextActive() { |
| ApplicationContextImpl applicationContext = ( ( UISessionImpl )uiSession ).getApplicationContext(); |
| return applicationContext != null && applicationContext.isActive(); |
| } |
| |
| } |