Mark and dispatch async events in asynchronous way

Mark asynchronous events in RAPEventBroker#constructEvent with a
property "rap.async.event".
Use UISynchronize#asyncExec in
RAPUIEventObjectSupplier/RAPUIEventHandler for the marked events.

506316: [e4] "EventAdmin Async Event Dispatcher Thread" can be blocked
by a UI thread
https://bugs.eclipse.org/bugs/show_bug.cgi?id=506316

Change-Id: Id740102f65d5dc00ed0430f7e8bbca99f6ba180b
diff --git a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPEventBroker.java b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPEventBroker.java
index 0987dee..6fdfe1c 100644
--- a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPEventBroker.java
+++ b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPEventBroker.java
@@ -29,20 +29,23 @@
 import org.osgi.service.event.EventHandler;
 
 public class RAPEventBroker implements IEventBroker {
-	// TBD synchronization
+	    // TBD synchronization
+
+	    private static final String	ASYNC_EVENT = "rap.async.event";
+
 		private Map<EventHandler, Collection<ServiceRegistration<?>>> registrations = new HashMap<EventHandler, Collection<ServiceRegistration<?>>>();
 
 		@Inject
 		Logger logger;
-		
+
 		@Inject
 		@Optional
 		UISynchronize uiSync;
-		
+
 		@Inject
 		@Named(E4Application.INSTANCEID)
 		String instanceId;
-		
+
 		// This is a temporary code to ensure that bundle containing
 		// EventAdmin implementation is started. This code it to be removed once
 		// the proper method to start EventAdmin is added.
@@ -62,13 +65,13 @@
 				}
 			}
 		}
-		
+
 		public RAPEventBroker() {
 			// placeholder
 		}
 
 		public boolean send(String topic, Object data) {
-			Event event = constructEvent(topic, data);
+			Event event = constructEvent(topic, data, false);
 			EventAdmin eventAdmin = Activator.getDefault().getEventAdmin();
 			if (eventAdmin == null) {
 				logger.error(NLS.bind("No EventAdmin", event.toString()));
@@ -79,7 +82,7 @@
 		}
 
 		public boolean post(String topic, Object data) {
-			Event event = constructEvent(topic, data);
+			Event event = constructEvent(topic, data, true);
 			EventAdmin eventAdmin = Activator.getDefault().getEventAdmin();
 			if (eventAdmin == null) {
 				logger.error(NLS.bind("No EventAdmin", event.toString()));
@@ -90,18 +93,26 @@
 		}
 
 		@SuppressWarnings("unchecked")
-		private Event constructEvent(String topic, Object data) {
+		private Event constructEvent(String topic, Object data, boolean async) {
 			topic = rapifyTopic(instanceId, topic);
 			Event event;
 			if (data instanceof Dictionary<?,?>) {
-				event = new Event(topic, (Dictionary<String,?>)data);
+				Dictionary<String,Object> properties = (Dictionary<String,Object>)data;
+				if (async)
+					properties.put(ASYNC_EVENT, Boolean.TRUE);
+				event = new Event(topic, properties);
 			} else if (data instanceof Map<?,?>) {
-				event = new Event(topic, (Map<String,?>)data);
+				Map<String,Object> properties = (Map<String,Object>)data;
+				if (async)
+					properties.put(ASYNC_EVENT, Boolean.TRUE);
+				event = new Event(topic, properties);
 			} else {
-				Dictionary<String, Object> d = new Hashtable<String, Object>(2);
+				Dictionary<String, Object> d = new Hashtable<String, Object>(3);
 				d.put(EventConstants.EVENT_TOPIC, topic);
 				if (data != null)
 					d.put(IEventBroker.DATA, data);
+				if (async)
+					d.put(ASYNC_EVENT, Boolean.TRUE);
 				event = new Event(topic, d);
 			}
 			return event;
@@ -110,7 +121,7 @@
 		public boolean subscribe(String topic, EventHandler eventHandler) {
 			return subscribe(topic, null, eventHandler, false);
 		}
-		
+
 		public boolean subscribe(String topic, String filter, EventHandler eventHandler, boolean headless) {
 			topic = rapifyTopic(instanceId, topic);
 			BundleContext bundleContext = Activator.getDefault().getBundleContext();
@@ -146,7 +157,7 @@
 			}
 			return true;
 		}
-		
+
 		@PreDestroy
 		void dispose() {
 			Collection<Collection<ServiceRegistration<?>>> values = new ArrayList<Collection<ServiceRegistration<?>>>(
@@ -160,10 +171,14 @@
 				}
 			}
 		}
-		
+
 		public static String rapifyTopic(String instanceId, String topic) {
 			String rv = instanceId + "/" + topic;
 //			System.err.println("Original: " + topic + ", RAPified: " + rv);
 			return rv;
 		}
+
+		public static boolean isAsyncEvent(Event event) {
+			return Boolean.TRUE.equals(event.getProperty(ASYNC_EVENT));
+		}
 }
diff --git a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventHandler.java b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventHandler.java
index 3fcf07a..a62d958 100644
--- a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventHandler.java
+++ b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventHandler.java
@@ -4,7 +4,7 @@
  * 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:
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
@@ -19,15 +19,15 @@
  * The helper will properly place UI-aware consumers on the main thread.
  */
 public class RAPUIEventHandler implements EventHandler {
-	
+
 	final private EventHandler eventHandler;
 	final private UISynchronize uiSync;
-	
+
 	public RAPUIEventHandler(EventHandler eventHandler, UISynchronize uiSync) {
 		this.eventHandler = eventHandler;
 		this.uiSync = uiSync;
 	}
-	
+
 	/* (non-Javadoc)
 	 * @see org.osgi.service.event.EventHandler#handleEvent(org.osgi.service.event.Event)
 	 */
@@ -35,12 +35,19 @@
 		if (uiSync == null)
 			eventHandler.handleEvent(event);
 		else {
-			uiSync.syncExec(new Runnable() {
-				
-				public void run() {
-					eventHandler.handleEvent(event);
-				}
-			});
+			if(RAPEventBroker.isAsyncEvent(event)) {
+				uiSync.asyncExec(new Runnable() {
+					public void run() {
+						eventHandler.handleEvent(event);
+					}
+				});
+			} else {
+				uiSync.syncExec(new Runnable() {
+					public void run() {
+						eventHandler.handleEvent(event);
+					}
+				});
+			}
 		}
 	}
 }
diff --git a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventObjectSupplier.java b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventObjectSupplier.java
index 10d2b99..c1eb2cb 100644
--- a/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventObjectSupplier.java
+++ b/bundles/org.eclipse.rap.e4/src/org/eclipse/rap/e4/internal/RAPUIEventObjectSupplier.java
@@ -34,18 +34,26 @@
 					logger.log(Level.WARNING, "No realm found to process UI event " + event);
 				return;
 			} else {
-				uiSync.syncExec(new Runnable() {
-					public void run() {
-						requestor.execute();
-					}
-				});
+				if(RAPEventBroker.isAsyncEvent(event)) {
+					uiSync.asyncExec(new Runnable() {
+						public void run() {
+							requestor.execute();
+						}
+					});
+				} else {
+					uiSync.syncExec(new Runnable() {
+						public void run() {
+							requestor.execute();
+						}
+					});
+				}
 			}
-		}		
+		}
 	}
-	
+
 	@Inject
 	protected UISynchronize uiSync;
-	
+
 	@Inject @Optional
 	protected Logger logger;