/******************************************************************************* * Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH. * 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 ******************************************************************************/ package org.eclipse.ui.internal.progress; import java.lang.reflect.Field; import java.util.*; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.jobs.*; import org.eclipse.rwt.RWT; import org.eclipse.rwt.internal.service.ContextProvider; import org.eclipse.rwt.lifecycle.UICallBack; import org.eclipse.rwt.service.ISessionStore; import org.eclipse.swt.widgets.Display; public class JobManagerAdapter extends ProgressProvider implements IJobChangeListener { private static JobManagerAdapter _instance; private final Map jobs; private final ProgressManager defaultProgressManager; final Object lock; public static synchronized JobManagerAdapter getInstance() { if( _instance == null ) { _instance = new JobManagerAdapter(); } return _instance; } private JobManagerAdapter() { // To avoid deadlocks we have to use the same synchronisation lock. // If anyone has a better idea - you're welcome. IJobManager jobManager = Job.getJobManager(); Class clazz = jobManager.getClass(); try { Field jobManagerLock = clazz.getDeclaredField( "lock" ); jobManagerLock.setAccessible( true ); lock = jobManagerLock.get( jobManager ); } catch( final Throwable thr ) { String msg = "Could not initialize synchronization lock."; throw new IllegalStateException( msg ); } jobs = new HashMap(); defaultProgressManager = new ProgressManager(); Job.getJobManager().setProgressProvider( this ); Job.getJobManager().addJobChangeListener( this ); } /////////////////////////////// // ProgressProvider public IProgressMonitor createMonitor( final Job job ) { ProgressManager manager = findProgressManager( job ); return manager.createMonitor( job ); } public IProgressMonitor createMonitor( final Job job, final IProgressMonitor group, final int ticks ) { ProgressManager manager = findProgressManager( job ); return manager.createMonitor( job, group, ticks ); } public IProgressMonitor createProgressGroup() { return defaultProgressManager.createProgressGroup(); } /////////////////////////////// // interface IJobChangeListener public void aboutToRun( final IJobChangeEvent event ) { ProgressManager manager = findProgressManager( event.getJob() ); manager.changeListener.aboutToRun( event ); } public void awake( final IJobChangeEvent event ) { ProgressManager manager = findProgressManager( event.getJob() ); manager.changeListener.awake( event ); } public void done( final IJobChangeEvent event ) { final ProgressManager[] manager = new ProgressManager[ 1 ]; Display display = null; synchronized( lock ) { try { manager[ 0 ] = findProgressManager( event.getJob() ); display = ( Display )jobs.get( event.getJob() ); if( display != null ) { display.asyncExec( new Runnable() { public void run() { Job job = event.getJob(); String id = String.valueOf( job.hashCode() ); UICallBack.deactivate( id ); } } ); } } finally { Job job = event.getJob(); if( !job.shouldSchedule() ) { jobs.remove( job ); } } } if( display != null ) { display.asyncExec( new Runnable() { public void run() { manager[ 0 ].changeListener.done( event ); } } ); // } else { // manager[ 0 ].changeListener.done( event ); } } public void running( final IJobChangeEvent event ) { ProgressManager manager = findProgressManager( event.getJob() ); manager.changeListener.running( event ); } public void scheduled( final IJobChangeEvent event ) { ProgressManager manager; synchronized( lock ) { if( ContextProvider.hasContext() ) { jobs.put( event.getJob(), Display.getCurrent() ); bindToSession( event.getJob() ); String id = String.valueOf( event.getJob().hashCode() ); UICallBack.activate( id ); } manager = findProgressManager( event.getJob() ); } manager.changeListener.scheduled( event ); } public void sleeping( final IJobChangeEvent event ) { ProgressManager manager = findProgressManager( event.getJob() ); manager.changeListener.sleeping( event ); } ////////////////// // helping methods private ProgressManager findProgressManager( final Job job ) { synchronized( lock ) { final ProgressManager result[] = new ProgressManager[ 1 ]; Display display = ( Display )jobs.get( job ); if( display != null ) { UICallBack.runNonUIThreadWithFakeContext( display, new Runnable() { public void run() { result[ 0 ] = ProgressManager.getInstance(); } } ); if( result[ 0 ] == null ) { String msg = "ProgressManager must not be null."; throw new IllegalStateException( msg ); } } else { result[ 0 ] = defaultProgressManager; } return result[ 0 ]; } } private void bindToSession( final Object keyToRemove ) { ISessionStore session = RWT.getSessionStore(); HttpSessionBindingListener watchDog = new HttpSessionBindingListener() { public void valueBound( final HttpSessionBindingEvent event ) { } public void valueUnbound( final HttpSessionBindingEvent event ) { try { handleWatchDog( keyToRemove ); } finally { synchronized( lock ) { jobs.remove( keyToRemove ); } } } private void handleWatchDog( final Object keyToRemove ) { // //////////////////////////////////////////////////////////////////// // TODO [fappel]: Very ugly hack to avoid a memory leak. // As a job can not be removed from the // running set directly, I use reflection. Jobs // can be catched in the set on session timeouts. // Don't know a proper solution yet. // Note that this is still under investigation. if( keyToRemove instanceof Job ) { final Job jobToRemove = ( Job )keyToRemove; Display display = ( Display )jobs.get( jobToRemove ); if( display != null ) { UICallBack.runNonUIThreadWithFakeContext( display, new Runnable() { public void run() { jobToRemove.cancel(); jobToRemove.addJobChangeListener( new JobCanceler() ); } } ); } try { IJobManager jobManager = Job.getJobManager(); Class clazz = jobManager.getClass(); Field running = clazz.getDeclaredField( "running" ); running.setAccessible( true ); Set set = ( Set )running.get( jobManager ); synchronized( lock ) { set.remove( keyToRemove ); // still sometimes job get catched - use the job marker adapter // to check whether they can be eliminated Object[] runningJobs = set.toArray(); for( int i = 0; i < runningJobs.length; i++ ) { Job toCheck = ( Job )runningJobs[ i ]; IJobMarker marker = ( IJobMarker )toCheck.getAdapter( IJobMarker.class ); if( marker != null && marker.canBeRemoved() ) { set.remove( toCheck ); } } } } catch( final Throwable thr ) { // TODO [fappel]: exception handling thr.printStackTrace(); } } } }; session.setAttribute( String.valueOf( watchDog.hashCode() ), watchDog ); } }