Multiple improvements and error fixes
diff --git a/bundles/org.eclipse.rap.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.rap.ui.workbench/META-INF/MANIFEST.MF
index b1eb198..bfe5e15 100644
--- a/bundles/org.eclipse.rap.ui.workbench/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.rap.ui.workbench/META-INF/MANIFEST.MF
@@ -152,7 +152,7 @@
  org.eclipse.ui.views,
  org.eclipse.ui.wizards
 Bundle-Name: %pluginName
-Bundle-Version: 3.4.9.qualifier
+Bundle-Version: 3.4.10.qualifier
 Bundle-ClassPath: .
 Bundle-Localization: plugin
 Bundle-Activator: org.eclipse.ui.internal.WorkbenchPlugin
diff --git a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/rap/ui/partdnd/internal/WorkaroundDragPartSource.java b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/rap/ui/partdnd/internal/WorkaroundDragPartSource.java
index bc292a0..806c9f5 100644
--- a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/rap/ui/partdnd/internal/WorkaroundDragPartSource.java
+++ b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/rap/ui/partdnd/internal/WorkaroundDragPartSource.java
@@ -1,174 +1,183 @@
-/*******************************************************************************

- * Copyright (c) 2017 David Marina

- * 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:

- *   David Marina - initial API and implementation

- *******************************************************************************/

-package org.eclipse.rap.ui.partdnd.internal;

-

-import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.getRemoteObject;

-import static org.eclipse.swt.internal.dnd.DNDUtil.convertTransferTypes;

-

-import java.util.concurrent.ScheduledThreadPoolExecutor;

-import java.util.concurrent.TimeUnit;

-

-import org.eclipse.rap.json.JsonValue;

-import org.eclipse.swt.SWT;

-import org.eclipse.swt.custom.CTabFolder;

-import org.eclipse.swt.custom.CTabItem;

-import org.eclipse.swt.dnd.DND;

-import org.eclipse.swt.dnd.DragSource;

-import org.eclipse.swt.dnd.Transfer;

-import org.eclipse.swt.graphics.Point;

-import org.eclipse.swt.widgets.Control;

-import org.eclipse.swt.widgets.Display;

-import org.eclipse.swt.widgets.Event;

-import org.eclipse.swt.widgets.Listener;

-

-

-/**

- * This class is implemented as a workaround for the following issue:<br>

- * <br>

- * When the window is resized and the mouse is on the border of the part (not the part tab itself),

- * the CTabFolder is catching the event and corrupting the browser's window widget by sending a

- * wrong JSON message. After this occurs, the window becomes frozen and it is not possible to

- * restore its functionality. <br>

- * In order to avoid this situation, a mouse listener will decide if the widget under the cursor

- * really belongs to the CTabFolder (ignoring the part's borders).It is important to note that the

- * event sequence might cause that the DragSource event is triggered before the MouseDown event is

- * sent. If that happens, the next mouse events in a short period of time have to be ignored.

- * 

- * @author David Marina

- */

-public class WorkaroundDragPartSource extends DragSource {

-

-  /** The serialVersionUID of this WorkaroundDragPartSource.java */

-  private static final long serialVersionUID = 8801486978319382719L;

-  /** The reference to a empty Transfer array */

-  private static final Transfer[] EMPTY_TRANSFER = new Transfer[] {};

-  /** The reference to the {@link CTabFolder} */

-  private final CTabFolder ctf;

-  /** Flag to indicated that the drag has started (<code>true</code>) */

-  private boolean dragStarted = false;

-  /**

-   * Flag to ignore mouse events (with <code>true</code>) when the DragStar happens before the

-   * MouseDown

-   */

-  private boolean ignoreEvents = false;

-

-  /**

-   * Creates a new WorkaroundDragPartSource

-   * 

-   * @param control

-   * @param style

-   */

-  public WorkaroundDragPartSource( Control control, int style ) {

-    super( control, style );

-    this.ctf = ( CTabFolder )control;

-    Listener listener = new Listener() {

-

-      /** The serialVersionUID of this WorkaroundDragPartSource.java */

-      private static final long serialVersionUID = 7747075970781676002L;

-

-      /** {@inheritDoc} */

-      @Override

-      public void handleEvent( org.eclipse.swt.widgets.Event event ) {

-        if( event.type == SWT.MouseDown ) {

-          Point absolutePoint = new Point( event.x, event.y );

-          CTabItem item = WorkaroundDragPartSource.this.ctf.getItem( absolutePoint );

-          if( item == null ) {

-            setDragStarted( false );

-          }

-          // The mouse down event could arrive after the DragStart. In

-          // that case we should ignore the mouse event

-          else if( !WorkaroundDragPartSource.this.ignoreEvents ) {

-            setDragStarted( true );

-            startClientTransferRendering();

-          }

-        } else if( event.type == SWT.MouseUp ) {

-          setDragStarted( false );

-        }

-      }

-    };

-    if( this.ctf.getData( "DragWorkaroundListener" ) == null ) {

-      this.ctf.addListener( SWT.MouseDown, listener );

-      this.ctf.addListener( SWT.MouseUp, listener );

-      this.ctf.setData( "DragWorkaroundListener", listener );

-    }

-  }

-

-  /**

-   * Indicates with an argument that the drag started (<code>true</code>) or ended (

-   * <code>false</code>)

-   * 

-   * @param dragStarted a boolean indicating with <code>true</code> the drag start;

-   *          <code>false</code> to indicate the drag end

-   */

-  public void setDragStarted( boolean dragStarted ) {

-    // If there is no drag operation, set the empty transfer to avoid

-    // blocking the window

-    if( !dragStarted ) {

-      JsonValue renderValue = convertTransferTypes( EMPTY_TRANSFER );

-      getRemoteObject( this ).set( "transfer", renderValue );

-      this.ignoreEvents = false;

-    }

-    this.dragStarted = dragStarted;

-  }

-

-  /** {@inheritDoc} */

-  @Override

-  public Transfer[] getTransfer() {

-    Display display = getDisplay();

-    Point location = display.getCursorLocation();

-    Point relativePoint = display.map( null, this.ctf, location );

-    CTabItem item = this.ctf.getItem( relativePoint );

-    if( this.dragStarted || item != null ) {

-      return super.getTransfer();

-    } else {

-      return EMPTY_TRANSFER;

-    }

-  }

-

-  /** {@inheritDoc} */

-  @Override

-  public void notifyListeners( int eventType, Event event ) {

-    if( eventType == DND.DragStart && !this.dragStarted ) {

-      ignoreEvents();

-    }

-    super.notifyListeners( eventType, event );

-    if( event.doit == false || event.type == DND.DragEnd ) {

-      setDragStarted( false );

-      ignoreEvents();

-    }

-  }

-

-  /** {@inheritDoc} */

-  @Override

-  protected void checkSubclass() {

-    // Override to enable subclassing

-  }

-

-  /**

-   * Starts the client transfer rendering

-   */

-  public void startClientTransferRendering() {

-    JsonValue renderValue = convertTransferTypes( super.getTransfer() );

-    getRemoteObject( this ).set( "transfer", renderValue );

-  }

-

-  /**

-   * Indicates that the next mouse events shall be ignored for a short period of time

-   */

-  public void ignoreEvents() {

-    this.ignoreEvents = true;

-    final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1 );

-    executor.schedule( ( ) -> {

-      this.ignoreEvents = false;

-    }, 500, TimeUnit.MILLISECONDS );

-  }

-}

-// -----------------------------------------------------------------------------

+/*******************************************************************************
+ * Copyright (c) 2017 David Marina
+ * 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:
+ *   David Marina - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.rap.ui.partdnd.internal;
+
+import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.getRemoteObject;
+import static org.eclipse.swt.internal.dnd.DNDUtil.convertTransferTypes;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.rap.json.JsonValue;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSource;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+
+/**
+ * This class is implemented as a workaround for the following issue:<br>
+ * <br>
+ * When the window is resized and the mouse is on the border of the part (not the part tab itself),
+ * the CTabFolder is catching the event and corrupting the browser's window widget by sending a
+ * wrong JSON message. After this occurs, the window becomes frozen and it is not possible to
+ * restore its functionality. <br>
+ * In order to avoid this situation, a mouse listener will decide if the widget under the cursor
+ * really belongs to the CTabFolder (ignoring the part's borders).It is important to note that the
+ * event sequence might cause that the DragSource event is triggered before the MouseDown event is
+ * sent. If that happens, the next mouse events in a short period of time have to be ignored.
+ * 
+ * @author David Marina
+ */
+public class WorkaroundDragPartSource extends DragSource {
+
+  /** The serialVersionUID of this WorkaroundDragPartSource.java */
+  private static final long serialVersionUID = 8801486978319382719L;
+  /** The reference to a empty Transfer array */
+  private static final Transfer[] EMPTY_TRANSFER = new Transfer[] {};
+  /** The reference to the {@link CTabFolder} */
+  private final CTabFolder ctf;
+  /** Flag to indicated that the drag has started (<code>true</code>) */
+  private boolean dragStarted = false;
+  /**
+   * Flag to ignore mouse events (with <code>true</code>) when the DragStar happens before the
+   * MouseDown
+   */
+  private boolean ignoreEvents = false;
+
+  /**
+   * The reference to the thread pool executor
+   */
+  private ScheduledThreadPoolExecutor executor;
+  
+  /**
+   * Creates a new WorkaroundDragPartSource
+   * 
+   * @param control
+   * @param style
+   */
+  public WorkaroundDragPartSource( Control control, int style ) {
+    super( control, style );
+    this.ctf = ( CTabFolder )control;
+    Listener listener = new Listener() {
+
+      /** The serialVersionUID of this WorkaroundDragPartSource.java */
+      private static final long serialVersionUID = 7747075970781676002L;
+
+      /** {@inheritDoc} */
+      @Override
+      public void handleEvent( org.eclipse.swt.widgets.Event event ) {
+        if( event.type == SWT.MouseDown ) {
+          Point absolutePoint = new Point( event.x, event.y );
+          CTabItem item = WorkaroundDragPartSource.this.ctf.getItem( absolutePoint );
+          if( item == null ) {
+            setDragStarted( false );
+          }
+          // The mouse down event could arrive after the DragStart. In
+          // that case we should ignore the mouse event
+          else if( !WorkaroundDragPartSource.this.ignoreEvents ) {
+            setDragStarted( true );
+            startClientTransferRendering();
+          }
+        } else if( event.type == SWT.MouseUp ) {
+          setDragStarted( false );
+        }
+      }
+    };
+    if( this.ctf.getData( "DragWorkaroundListener" ) == null ) {
+      this.ctf.addListener( SWT.MouseDown, listener );
+      this.ctf.addListener( SWT.MouseUp, listener );
+      this.ctf.setData( "DragWorkaroundListener", listener );
+    }
+
+    this.executor = new ScheduledThreadPoolExecutor( 1 );
+    
+    this.ctf.getDisplay().disposeExec(() -> { this.executor.shutdown(); this.executor = null; });
+  }
+
+  /**
+   * Indicates with an argument that the drag started (<code>true</code>) or ended (
+   * <code>false</code>)
+   * 
+   * @param dragStarted a boolean indicating with <code>true</code> the drag start;
+   *          <code>false</code> to indicate the drag end
+   */
+  public void setDragStarted( boolean dragStarted ) {
+    // If there is no drag operation, set the empty transfer to avoid
+    // blocking the window
+    if( !dragStarted ) {
+      JsonValue renderValue = convertTransferTypes( EMPTY_TRANSFER );
+      getRemoteObject( this ).set( "transfer", renderValue );
+      this.ignoreEvents = false;
+    }
+    this.dragStarted = dragStarted;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Transfer[] getTransfer() {
+    Display display = getDisplay();
+    Point location = display.getCursorLocation();
+    Point relativePoint = display.map( null, this.ctf, location );
+    CTabItem item = this.ctf.getItem( relativePoint );
+    if( this.dragStarted || item != null ) {
+      return super.getTransfer();
+    } else {
+      return EMPTY_TRANSFER;
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void notifyListeners( int eventType, Event event ) {
+    if( eventType == DND.DragStart && !this.dragStarted ) {
+      ignoreEvents();
+    }
+    super.notifyListeners( eventType, event );
+    if( event.doit == false || event.type == DND.DragEnd ) {
+      setDragStarted( false );
+      ignoreEvents();
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected void checkSubclass() {
+    // Override to enable subclassing
+  }
+
+  /**
+   * Starts the client transfer rendering
+   */
+  public void startClientTransferRendering() {
+    JsonValue renderValue = convertTransferTypes( super.getTransfer() );
+    getRemoteObject( this ).set( "transfer", renderValue );
+  }
+
+  /**
+   * Indicates that the next mouse events shall be ignored for a short period of time
+   */
+  public void ignoreEvents() {
+    this.ignoreEvents = true;
+    
+    this.executor.schedule( ( ) -> {
+      this.ignoreEvents = false;
+    }, 500, TimeUnit.MILLISECONDS );
+  }
+}
+// -----------------------------------------------------------------------------
diff --git a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/Workbench.java b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/Workbench.java
index 22f7fbf..5c13671 100644
--- a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/Workbench.java
+++ b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/Workbench.java
@@ -1601,11 +1601,24 @@
 	 */
 	/* package */
 	boolean close(int returnCode, final boolean force) {
-		this.returnCode = returnCode;
-		final boolean[] ret = new boolean[1];
-		BusyIndicator.showWhile(null, () -> ret[0] = busyClose(force));
-		return ret[0];
-	}
+// RAP [fappel]: take care of the started flag
+//      this.returnCode = returnCode;
+//      final boolean[] ret = new boolean[1];
+//      BusyIndicator.showWhile(null, () -> ret[0] = busyClose(force));
+//      return ret[0];
+        try {
+            this.returnCode = returnCode;
+            final boolean[] ret = new boolean[1];
+            BusyIndicator.showWhile(null, new Runnable() {
+                public void run() {
+                    ret[0] = busyClose(force);
+                }
+            });
+            return ret[0];
+        } finally {
+            started = false;
+        }
+    }
 
 	@Override
 	public IWorkbenchWindow getActiveWorkbenchWindow() {
@@ -1772,6 +1785,8 @@
 	 * @return true if init succeeded.
 	 */
 	private boolean init() {
+	 // RAP [fappel]: take care of the started flag
+        started = true;
 		// setup debug mode if required.
 		if (WorkbenchPlugin.getDefault().isDebugging()) {
 			WorkbenchPlugin.DEBUG = true;
diff --git a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/handlers/DirtyStateTracker.java b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/handlers/DirtyStateTracker.java
index cc78343..7457a93 100644
--- a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/handlers/DirtyStateTracker.java
+++ b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/handlers/DirtyStateTracker.java
@@ -31,19 +31,21 @@
  */
 public class DirtyStateTracker implements IPartListener, IWindowListener, IPropertyListener {
 
-	private final IWorkbench workbench;
+// RAP [dm]:
+//  private final IWorkbench workbench;
 
-	public DirtyStateTracker() {
-		workbench = Workbench.getInstance();
-		workbench.addWindowListener(this);
-		IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
-		register(window);
-	}
+    public DirtyStateTracker() {
+//      workbench = Workbench.getInstance();
+        Workbench.getInstance().addWindowListener(this);
+        IWorkbenchWindow window = Workbench.getInstance().getActiveWorkbenchWindow();
+        register(window);
+    }
 
-	public void update() {
-		IEvaluationService service = workbench.getService(IEvaluationService.class);
-		service.requestEvaluation(ISources.ACTIVE_PART_NAME);
-	}
+    public void update() {
+        IEvaluationService service = Workbench.getInstance().getService(IEvaluationService.class);
+        service.requestEvaluation(ISources.ACTIVE_PART_NAME);
+    }
+// RAPEND: [dm]
 
 	private void register(IWorkbenchWindow window) {
 		if (window == null) {
diff --git a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/menus/LegacyActionPersistence.java b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/menus/LegacyActionPersistence.java
index 49c34c0..7427dac 100644
--- a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/menus/LegacyActionPersistence.java
+++ b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/menus/LegacyActionPersistence.java
@@ -131,24 +131,26 @@
 	 * the collection. This should be called before every read.
 	 */
 	private final void clearActivations() {
-		final IHandlerService service = window
-				.getService(IHandlerService.class);
-		if (service == null) {
-			handlerActivations.clear();
-			return;
-		}
-		service.deactivateHandlers(handlerActivations);
-		final Iterator activationItr = handlerActivations.iterator();
-		while (activationItr.hasNext()) {
-			final IHandlerActivation activation = (IHandlerActivation) activationItr
-					.next();
-			final IHandler handler = activation.getHandler();
-			if (handler != null) {
-				handler.dispose();
-			}
-		}
-		handlerActivations.clear();
-	}
+	 // RAP [dm]:
+//      final IHandlerService service = window
+//              .getService(IHandlerService.class);
+//      if (service == null) {
+            handlerActivations.clear();
+            return;
+//      }
+//      service.deactivateHandlers(handlerActivations);
+//      final Iterator activationItr = handlerActivations.iterator();
+//      while (activationItr.hasNext()) {
+//          final IHandlerActivation activation = (IHandlerActivation) activationItr
+//                  .next();
+//          final IHandler handler = activation.getHandler();
+//          if (handler != null) {
+//              handler.dispose();
+//          }
+//      }
+//      handlerActivations.clear();
+        // RAPEND: [dm]
+    }
 
 	/**
 	 * Removes all of the image bindings made by this class, and then clears the
diff --git a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/progress/ProgressViewUpdater.java b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/progress/ProgressViewUpdater.java
index 97a7db1..fbbe12b 100644
--- a/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/progress/ProgressViewUpdater.java
+++ b/bundles/org.eclipse.rap.ui.workbench/src/org/eclipse/ui/internal/progress/ProgressViewUpdater.java
@@ -14,87 +14,111 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.eclipse.rap.rwt.SingletonUtil;
+import org.eclipse.rap.rwt.internal.lifecycle.LifeCycleUtil;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IWorkbenchPreferenceConstants;
-import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.internal.util.PrefUtil;
 
 /**
  * The ProgressViewUpdater is the singleton that updates viewers.
  */
-class ProgressViewUpdater implements IJobProgressManagerListener {
+//RAP [fappel]: ProgressViewUpdater needs to be a singleton per session
+//class ProgressViewUpdater implements IJobProgressManagerListener {
+//
+// private static ProgressViewUpdater singleton;
+public class ProgressViewUpdater implements IJobProgressManagerListener {
 
-    private static ProgressViewUpdater singleton;
+	// RAP [fappel]: This class will be instanciated using the
+	// SessionSingletonUtil#getInstance(class) method to have a
+	// replacement for the class variable holding the singleton in
+	// RCP.
+	public final static class ProgressViewUpdaterHolder {
+		private ProgressViewUpdaterHolder() {
+			// prevent from instance creation
+		}
 
-    private IProgressUpdateCollector[] collectors;
+		// this is the reference to the actual session singleton instance
+		public ProgressViewUpdater singleton;
+	}
+
+	private Set<IProgressUpdateCollector> collectors;
 
     UpdatesInfo currentInfo = new UpdatesInfo();
 
     boolean debug;
 
-	Throttler throttledUpdate = new Throttler(PlatformUI.getWorkbench().getDisplay(), Duration.ofMillis(100),
-			this::update);
+    // RAP [fappel]:
+    private Display display;
+
+	Throttler throttledUpdate;
 
     /**
      * The UpdatesInfo is a private class for keeping track of the updates
      * required.
      */
-    class UpdatesInfo {
+	static class UpdatesInfo {
 
-        Collection additions = new HashSet();
+		Collection<JobTreeElement> additions = new LinkedHashSet<>();
 
-        Collection deletions = new HashSet();
+		Collection<JobTreeElement> deletions = new LinkedHashSet<>();
 
-        Collection refreshes = new HashSet();
+		Collection<JobTreeElement> refreshes = new LinkedHashSet<>();
 
-        boolean updateAll = false;
+		volatile boolean updateAll;
 
         private UpdatesInfo() {
             //Create a new instance of the info
         }
 
         /**
-         * Add an add update
-         *
-         * @param addition
-         */
-        void add(JobTreeElement addition) {
+		 * Add an add update
+		 *
+		 * @param addition
+		 */
+		synchronized void add(JobTreeElement addition) {
             additions.add(addition);
         }
 
         /**
-         * Add a remove update
-         *
-         * @param removal
-         */
-        void remove(JobTreeElement removal) {
+		 * Add a remove update
+		 *
+		 * @param removal
+		 */
+		synchronized void remove(JobTreeElement removal) {
             deletions.add(removal);
         }
 
         /**
-         * Add a refresh update
-         *
-         * @param refresh
-         */
-        void refresh(JobTreeElement refresh) {
+		 * Add a refresh update
+		 *
+		 * @param refresh
+		 */
+		synchronized void refresh(JobTreeElement refresh) {
             refreshes.add(refresh);
         }
 
         /**
          * Reset the caches after completion of an update.
          */
-        void reset() {
+		synchronized void reset() {
             additions.clear();
             deletions.clear();
             refreshes.clear();
             updateAll = false;
         }
 
-        void processForUpdate() {
-            HashSet staleAdditions = new HashSet();
+		/**
+		 * @return array containing updated, added and deleted items
+		 */
+		synchronized JobTreeElement[][] processForUpdate() {
+			HashSet<JobTreeElement> staleAdditions = new HashSet<>();
 
-            Iterator additionsIterator = additions.iterator();
+			Iterator<JobTreeElement> additionsIterator = additions.iterator();
             while (additionsIterator.hasNext()) {
-                JobTreeElement treeElement = (JobTreeElement) additionsIterator
+				JobTreeElement treeElement = additionsIterator
                         .next();
                 if (!treeElement.isActive()) {
                     if (deletions.contains(treeElement)) {
@@ -105,11 +129,8 @@
 
             additions.removeAll(staleAdditions);
 
-            HashSet obsoleteRefresh = new HashSet();
-            Iterator refreshIterator = refreshes.iterator();
-            while (refreshIterator.hasNext()) {
-                JobTreeElement treeElement = (JobTreeElement) refreshIterator
-                        .next();
+			HashSet<JobTreeElement> obsoleteRefresh = new HashSet<>();
+			for (JobTreeElement treeElement : refreshes) {
                 if (deletions.contains(treeElement)
                         || additions.contains(treeElement)) {
 					obsoleteRefresh.add(treeElement);
@@ -131,7 +152,12 @@
 
             refreshes.removeAll(obsoleteRefresh);
 
-        }
+			JobTreeElement[] updateItems = refreshes.toArray(new JobTreeElement[0]);
+			JobTreeElement[] additionItems = additions.toArray(new JobTreeElement[0]);
+			JobTreeElement[] deletionItems = deletions.toArray(new JobTreeElement[0]);
+			return new JobTreeElement[][] { updateItems, additionItems, deletionItems };
+		}
+
     }
 
     /**
@@ -139,79 +165,88 @@
      *
      * @return ProgressViewUpdater
      */
-   static ProgressViewUpdater getSingleton() {
-        if (singleton == null) {
-			singleton = new ProgressViewUpdater();
-		}
-        return singleton;
+    static ProgressViewUpdater getSingleton() {
+// RAP [fappel]: session aware implementation
+//        if (singleton == null) {
+//			singleton = new ProgressViewUpdater();
+//		}
+//        return singleton;
+      ProgressViewUpdaterHolder singletonHolder = getSingletonHolder();
+      if( singletonHolder.singleton == null ) {
+        singletonHolder.singleton = new ProgressViewUpdater();
+        singletonHolder.singleton.display = LifeCycleUtil.getSessionDisplay();
+			singletonHolder.singleton.throttledUpdate = new Throttler(getSingleton().display, Duration.ofMillis(100),
+					singletonHolder.singleton::update);
+      }
+      return singletonHolder.singleton;
+    }
+
+    private static ProgressViewUpdaterHolder getSingletonHolder() {
+		return SingletonUtil.getSessionInstance(ProgressViewUpdaterHolder.class);
     }
 
     /**
-     * Return whether or not there is a singleton for updates to avoid creating
-     * extra listeners.
-     *
-     * @return boolean <code>true</code> if there is already
-     * a singleton
-     */
+	 * Return whether or not there is a singleton for updates to avoid creating
+	 * extra listeners.
+	 *
+	 * @return boolean <code>true</code> if there is already a singleton
+	 */
     static boolean hasSingleton() {
-        return singleton != null;
+// RAP [fappel]:
+//      return singleton != null;
+		return getSingletonHolder().singleton != null;
     }
 
     static void clearSingleton() {
-        if (singleton != null) {
-			ProgressManager.getInstance().removeListener(singleton);
-		}
-        singleton = null;
+// RAP [fappel]:
+//        if (singleton != null) {
+//			ProgressManager.getInstance().removeListener(singleton);
+//		}
+//        singleton = null;
+      if( hasSingleton() ) {
+        ProgressManager.getInstance().removeListener(getSingleton());
+        getSingletonHolder().singleton.display = null;
+      }
+      getSingletonHolder().singleton = null;
     }
 
     /**
      * Create a new instance of the receiver.
      */
     private ProgressViewUpdater() {
-        collectors = new IProgressUpdateCollector[0];
+		collectors = new LinkedHashSet<>();
         ProgressManager.getInstance().addListener(this);
-        debug =
+		debug =
         	PrefUtil.getAPIPreferenceStore().
         		getBoolean(IWorkbenchPreferenceConstants.SHOW_SYSTEM_JOBS);
     }
 
     /**
-     * Add the new collector to the list of collectors.
-     *
-     * @param newCollector
-     */
+	 * Add the new collector to the list of collectors.
+	 *
+	 * @param newCollector
+	 */
     void addCollector(IProgressUpdateCollector newCollector) {
-        IProgressUpdateCollector[] newCollectors = new IProgressUpdateCollector[collectors.length + 1];
-        System.arraycopy(collectors, 0, newCollectors, 0, collectors.length);
-        newCollectors[collectors.length] = newCollector;
-        collectors = newCollectors;
+		collectors.add(newCollector);
     }
 
     /**
-     * Remove the collector from the list of collectors.
-     *
-     * @param provider
-     */
+	 * Remove the collector from the list of collectors.
+	 *
+	 * @param provider
+	 */
     void removeCollector(IProgressUpdateCollector provider) {
-        HashSet newCollectors = new HashSet();
-        for (int i = 0; i < collectors.length; i++) {
-            if (!collectors[i].equals(provider)) {
-				newCollectors.add(collectors[i]);
-			}
-        }
-        IProgressUpdateCollector[] newArray = new IProgressUpdateCollector[newCollectors
-                .size()];
-        newCollectors.toArray(newArray);
-        collectors = newArray;
+		collectors.remove(provider);
         //Remove ourselves if there is nothing to update
-        if (collectors.length == 0) {
+		if (collectors.isEmpty()) {
 			clearSingleton();
 		}
     }
 
+	/** Running in UI thread by throttledUpdate */
 	private void update() {
 		// Abort the update if there isn't anything
-		if (collectors.length == 0) {
+		if (collectors.isEmpty()) {
 			return;
 		}
 
@@ -222,15 +257,11 @@
 			}
 
 		} else {
-			// Lock while getting local copies of the caches.
-			Object[] updateItems;
-			Object[] additionItems;
-			Object[] deletionItems;
-			currentInfo.processForUpdate();
+			JobTreeElement[][] elements = currentInfo.processForUpdate();
 
-			updateItems = currentInfo.refreshes.toArray();
-			additionItems = currentInfo.additions.toArray();
-			deletionItems = currentInfo.deletions.toArray();
+			JobTreeElement[] updateItems = elements[0];
+			JobTreeElement[] additionItems = elements[1];
+			JobTreeElement[] deletionItems = elements[2];
 
 			currentInfo.reset();
 
@@ -248,31 +279,7 @@
 		}
 	}
 
-    /**
-     * Get the updates info that we are using in the receiver.
-     *
-     * @return Returns the currentInfo.
-     */
-    UpdatesInfo getCurrentInfo() {
-        return currentInfo;
-    }
-
-    /**
-     * Refresh the supplied JobInfo.
-     * @param info
-     */
-    public void refresh(JobInfo info) {
-		currentInfo.refresh(info);
-		GroupInfo group = info.getGroupInfo();
-		if (group != null) {
-			currentInfo.refresh(group);
-		}
-        //Add in a 100ms delay so as to keep priority low
-		throttledUpdate.throttledExec();
-
-    }
-
-    @Override
+	@Override
 	public void refreshJobInfo(JobInfo info) {
 		currentInfo.refresh(info);
         //Add in a 100ms delay so as to keep priority low
@@ -280,33 +287,28 @@
 
     }
 
-    @Override
+	@Override
 	public void refreshGroup(GroupInfo info) {
 		currentInfo.refresh(info);
         //Add in a 100ms delay so as to keep priority low
 		throttledUpdate.throttledExec();
-
     }
 
-    @Override
+	@Override
 	public void addGroup(GroupInfo info) {
-
 		currentInfo.add(info);
 		throttledUpdate.throttledExec();
-
     }
 
-    @Override
+	@Override
 	public void refreshAll() {
-
 		currentInfo.updateAll = true;
 
         //Add in a 100ms delay so as to keep priority low
 		throttledUpdate.throttledExec();
-
     }
 
-    @Override
+	@Override
 	public void addJob(JobInfo info) {
 		GroupInfo group = info.getGroupInfo();
 
@@ -316,10 +318,9 @@
 			currentInfo.refresh(group);
         }
 		throttledUpdate.throttledExec();
-
     }
 
-    @Override
+	@Override
 	public void removeJob(JobInfo info) {
 		GroupInfo group = info.getGroupInfo();
 		if (group == null) {
@@ -330,14 +331,13 @@
 		throttledUpdate.throttledExec();
     }
 
-    @Override
+	@Override
 	public void removeGroup(GroupInfo group) {
 		currentInfo.remove(group);
 		throttledUpdate.throttledExec();
-
     }
 
-    @Override
+	@Override
 	public boolean showsDebug() {
         return debug;
     }