You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/04/12 16:30:36 UTC

[isis] branch master updated: ISIS-1935 rework event-bus implementation to allow programmatic event-listener registration

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 43f4a41  ISIS-1935 rework event-bus implementation to allow programmatic event-listener registration
43f4a41 is described below

commit 43f4a41533d0ffcdd7bc6bb0a44b7167c23740a8
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Apr 12 18:30:30 2018 +0200

    ISIS-1935 rework event-bus implementation to allow programmatic
    event-listener registration
---
 .../services/eventbus/EventBusImplementation.java  |  31 ++++-
 .../applib/services/eventbus/EventBusService.java  |  27 ++++-
 .../QueryResultsCacheControlInternal.java}         |  35 ++++--
 core/plugins/eventbus-axon/pom.xml                 |  13 +++
 .../QueryResultsCacheControlUsingAxon.java         |  56 ---------
 .../EventBusImplementationForAxonSimple.java       |  84 ++++++++++++--
 .../QueryResultsCacheUsingAxonTest.java            |  34 ++++--
 .../EventBusServiceDefaultUsingAxonSimpleTest.java | 128 +++++++++++++++++++++
 .../adapter/EventBusImplementationForGuava.java    |  40 ++++++-
 .../QueryResultsCacheUsingGuavaTest.java           |  32 ++++--
 .../services/eventbus/EventBusServiceDefault.java  |   2 +-
 11 files changed, 366 insertions(+), 116 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusImplementation.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusImplementation.java
index 6423a49..ab6dcce 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusImplementation.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusImplementation.java
@@ -16,20 +16,19 @@
  */
 package org.apache.isis.applib.services.eventbus;
 
+import java.util.function.Consumer;
+
 import org.apache.isis.applib.internal.context._Plugin;
 
 /**
  * Common interface for all Event Bus implementations.
  *
  * <p>
- *     Defines an (non-pluggable, hard-coded) SPI to the 
+ *     Defines a plug-able SPI to the 
  *     {@link org.apache.isis.applib.services.eventbus.EventBusService},
  *     to allow alternative implementations of in-memory event bus to be used.
  * </p>
  *
- * <p>
- *     Currently, there are implementations based on Guava and on the Axon framework.
- * </p>
  */
 public interface EventBusImplementation {
 
@@ -50,8 +49,30 @@ public interface EventBusImplementation {
 	 * {@link org.apache.isis.applib.services.eventbus.EventBusService#post(Object)}.
 	 */
 	void post(Object event);
+	
+	/**
+	 * Programmatically adds an event listener (wrapping the specified Consumer {@code onEvent})
+	 * to the event-bus.
+	 * @param onEvent
+	 * @return an EventListener instance 
+	 * @since 2.0.0
+	 */
+	<T> EventListener<T> addEventListener(final Class<T> targetType, Consumer<T> onEvent);
+	
+	/**
+	 * Removes the {@code eventListener} from the event-bus.
+	 * @param eventListener
+	 * @since 2.0.0
+	 */
+	<T> void removeEventListener(EventListener<T> eventListener);
+	
+	// -- EVENT LISTENER
+	
+	public static interface EventListener<T> {
+    	public void on(T event);
+    }
 
-	// -- LOOKUP
+	// -- PLUGIN LOOKUP
 
 	public static EventBusImplementation get() {
 		return _Plugin.getOrElse(EventBusImplementation.class, 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
index 6d59f82..a506df0 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/eventbus/EventBusService.java
@@ -18,13 +18,16 @@ package org.apache.isis.applib.services.eventbus;
 
 import java.util.Collections;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Consumer;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.internal.collections._Sets;
+import org.apache.isis.applib.services.eventbus.EventBusImplementation.EventListener;
 
 /**
  * A service implementing an Event Bus, allowing arbitrary events to be posted and
@@ -56,7 +59,7 @@ public abstract class EventBusService {
         @Override
         public void post(Object event) {}
         @Override
-        protected EventBusImplementation getEventBusImplementation() {
+        public EventBusImplementation getEventBusImplementation() {
             return null;
         }
         @Override
@@ -212,21 +215,31 @@ public abstract class EventBusService {
         return this.eventBusImplementation != null;
     }
 
-    
-
-
     // -- getEventBus
 
     /**
      * Lazily populated in {@link #getEventBusImplementation()} as result of the first {@link #post(Object)}.
      */
     protected EventBusImplementation eventBusImplementation;
-
+    
+    public <T> EventListener<T> addEventListener(final Class<T> targetType, Consumer<T> onEvent) {
+    	return Optional.ofNullable(getEventBusImplementation())
+    			.map(impl->impl.addEventListener(targetType, onEvent))
+    			.orElse(null);
+	}
+
+    public <T> void removeEventListener(EventListener<T> eventListener) {
+    	// do not trigger setupEventBus() 
+    	if(eventBusImplementation!=null) {
+    		eventBusImplementation.removeEventListener(eventListener);
+    	}
+	}
+    
     /**
      * Lazily populates the event bus for the current {@link #getSubscribers() subscribers}.
      */
     @Programmatic
-    protected EventBusImplementation getEventBusImplementation() {
+    public EventBusImplementation getEventBusImplementation() {
         setupEventBus();
         return eventBusImplementation;
     }
@@ -300,6 +313,8 @@ public abstract class EventBusService {
         return false;
     }
 
+
+
     
 
 
diff --git a/core/plugins/eventbus-guava/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingGuava.java b/core/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlInternal.java
similarity index 69%
rename from core/plugins/eventbus-guava/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingGuava.java
rename to core/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlInternal.java
index 86d23ec..ecf34d9 100644
--- a/core/plugins/eventbus-guava/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingGuava.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlInternal.java
@@ -16,12 +16,15 @@
  */
 package org.apache.isis.applib.services.queryresultscache;
 
+import javax.annotation.PostConstruct;
+
 import org.apache.isis.applib.AbstractSubscriber;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.fixturescripts.events.FixturesInstalledEvent;
 import org.apache.isis.applib.fixturescripts.events.FixturesInstallingEvent;
+import org.apache.isis.applib.services.eventbus.EventBusImplementation;
 
 
 /**
@@ -31,23 +34,31 @@ import org.apache.isis.applib.fixturescripts.events.FixturesInstallingEvent;
 		nature = NatureOfService.DOMAIN,
 		menuOrder = "" + Integer.MAX_VALUE
 		)
-public class QueryResultsCacheControlUsingGuava extends AbstractSubscriber implements QueryResultCacheControl {
+public class QueryResultsCacheControlInternal extends AbstractSubscriber implements QueryResultCacheControl {
 
-	@Programmatic
-	@com.google.common.eventbus.Subscribe
-	//@org.axonframework.eventhandling.annotation.EventHandler
-	public void on(FixturesInstallingEvent ev) {
-		fixturesInstalling = true;
-	}
+	@PostConstruct
+	@Override
+	public void postConstruct() {
+
+		super.postConstruct();
+
+		final EventBusImplementation eventBus = eventBusService.getEventBusImplementation(); //TODO don't expose this
+		if(eventBus==null) {
+			return;
+		}
+
+		eventBusService.addEventListener(FixturesInstallingEvent.class, ev->{
+			fixturesInstalling = true;
+		});
+
+		eventBusService.addEventListener(FixturesInstalledEvent.class, ev->{
+			fixturesInstalling = false;
+		});
 
-	@Programmatic
-	@com.google.common.eventbus.Subscribe
-	//@org.axonframework.eventhandling.annotation.EventHandler
-	public void on(FixturesInstalledEvent ev) {
-		fixturesInstalling = false;
 	}
 
 	private boolean fixturesInstalling;
+
 	@Programmatic
 	@Override
 	public boolean isFixturesInstalling() {
diff --git a/core/plugins/eventbus-axon/pom.xml b/core/plugins/eventbus-axon/pom.xml
index 76e5005..b2d95c1 100644
--- a/core/plugins/eventbus-axon/pom.xml
+++ b/core/plugins/eventbus-axon/pom.xml
@@ -64,6 +64,19 @@
 			<artifactId>isis-core-runtime</artifactId>
 			<scope>compile</scope>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-core-runtime</artifactId>
+			<type>test-jar</type>
+			<scope>test</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.jdo</groupId>
+			<artifactId>jdo-api</artifactId>
+			<version>${jdo-api.version}</version>
+			<scope>test</scope>
+		</dependency>
 
 	</dependencies>
 
diff --git a/core/plugins/eventbus-axon/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingAxon.java b/core/plugins/eventbus-axon/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingAxon.java
deleted file mode 100644
index 687e455..0000000
--- a/core/plugins/eventbus-axon/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheControlUsingAxon.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.isis.applib.services.queryresultscache;
-
-import org.apache.isis.applib.AbstractSubscriber;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.fixturescripts.events.FixturesInstalledEvent;
-import org.apache.isis.applib.fixturescripts.events.FixturesInstallingEvent;
-
-
-/**
- * In separate class because {@link QueryResultsCache} itself is request-scoped
- */
-@DomainService(
-		nature = NatureOfService.DOMAIN,
-		menuOrder = "" + Integer.MAX_VALUE
-		)
-public class QueryResultsCacheControlUsingAxon extends AbstractSubscriber implements QueryResultCacheControl {
-
-	@Programmatic
-	//@com.google.common.eventbus.Subscribe
-	@org.axonframework.eventhandling.annotation.EventHandler
-	public void on(FixturesInstallingEvent ev) {
-		fixturesInstalling = true;
-	}
-
-	@Programmatic
-	//@com.google.common.eventbus.Subscribe
-	@org.axonframework.eventhandling.annotation.EventHandler
-	public void on(FixturesInstalledEvent ev) {
-		fixturesInstalling = false;
-	}
-
-	private boolean fixturesInstalling;
-	@Programmatic
-	@Override
-	public boolean isFixturesInstalling() {
-		return fixturesInstalling;
-	}
-}
diff --git a/core/plugins/eventbus-axon/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForAxonSimple.java b/core/plugins/eventbus-axon/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForAxonSimple.java
index 8468850..0d6c3bf 100644
--- a/core/plugins/eventbus-axon/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForAxonSimple.java
+++ b/core/plugins/eventbus-axon/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForAxonSimple.java
@@ -17,12 +17,16 @@
 package org.apache.isis.core.runtime.services.eventbus.adapter;
 
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 
 import org.apache.isis.applib.events.domain.AbstractDomainEvent;
+import org.apache.isis.applib.services.eventbus.EventBusImplementation;
 import org.apache.isis.core.runtime.services.eventbus.EventBusImplementationAbstract;
 import org.axonframework.domain.EventMessage;
 import org.axonframework.domain.GenericEventMessage;
+import org.axonframework.eventhandling.EventListenerProxy;
 import org.axonframework.eventhandling.SimpleEventBus;
 import org.axonframework.eventhandling.annotation.AnnotationEventListenerAdapter;
 
@@ -36,15 +40,6 @@ public class EventBusImplementationForAxonSimple extends EventBusImplementationA
 
     private Map<Object, AxonEventListenerAdapter> listenerAdapterByDomainService = new ConcurrentHashMap<>();
 
-    private AxonEventListenerAdapter adapterFor(final Object domainService) {
-        AxonEventListenerAdapter annotationEventListenerAdapter = listenerAdapterByDomainService.get(domainService);
-        if (annotationEventListenerAdapter == null) {
-            annotationEventListenerAdapter = new AxonEventListenerAdapter(domainService);
-            listenerAdapterByDomainService.put(domainService, annotationEventListenerAdapter);
-        }
-        return annotationEventListenerAdapter;
-    }
-
     @Override
     public void register(final Object domainService) {
         simpleEventBus.subscribe(adapterFor(domainService));
@@ -54,7 +49,6 @@ public class EventBusImplementationForAxonSimple extends EventBusImplementationA
     public void unregister(final Object domainService) {
         // Seems it's needed to be a no-op (See EventBusService).
         // AxonSimpleEventBusAdapter.simpleEventBus.unsubscribe(AxonSimpleEventBusAdapter.adapterFor(domainService));
-
     }
 
     /*
@@ -69,13 +63,30 @@ public class EventBusImplementationForAxonSimple extends EventBusImplementationA
         simpleEventBus.publish(GenericEventMessage.asEventMessage(event));
     }
 
+	@Override
+	public <T> EventBusImplementation.EventListener<T> addEventListener(
+			final Class<T> targetType, 
+			final Consumer<T> onEvent) {
+		
+		final AxonEventListener<T> eventListener = new AxonEventListener<T>(targetType, onEvent);
+		simpleEventBus.subscribe(eventListener.proxy());
+		return eventListener;
+	}
+
+	@Override
+	public <T> void removeEventListener(EventBusImplementation.EventListener<T> eventListener) {
+		if(eventListener instanceof AxonEventListener) {
+			simpleEventBus.unsubscribe(((AxonEventListener<T>)eventListener).proxy());	
+		}
+	}
 
     @Override
     protected AbstractDomainEvent<?> asDomainEvent(final Object event) {
         if(event instanceof GenericEventMessage) {
             // this seems to be the case on error
 
-            final GenericEventMessage genericEventMessage = (GenericEventMessage) event;
+            @SuppressWarnings("rawtypes")
+			final GenericEventMessage genericEventMessage = (GenericEventMessage) event;
             final Object payload = genericEventMessage.getPayload();
             return asDomainEventIfPossible(payload);
         }
@@ -84,6 +95,55 @@ public class EventBusImplementationForAxonSimple extends EventBusImplementationA
         return asDomainEventIfPossible(event);
     }
 
+    // -- HELPER
+    
+    /**
+     * Wraps a Consumer as EventBusImplementation.EventListener with the given targetType.
+     * @param <T>
+     * @since 2.0.0
+     */
+    static class AxonEventListener<T> implements EventBusImplementation.EventListener<T> {
+    	private final Consumer<T> eventConsumer;
+		private final EventListenerProxy proxy;
+    	private AxonEventListener(Class<T> targetType, Consumer<T> eventConsumer) {
+			this.eventConsumer = Objects.requireNonNull(eventConsumer);
+			this.proxy = new EventListenerProxy() {
+				@SuppressWarnings("unchecked")
+				@Override
+				public void handle(@SuppressWarnings("rawtypes") EventMessage event) {
+					final Object payload = event.getPayload();
+					if(payload==null) {
+						return;
+					}
+					if(targetType.isAssignableFrom(event.getPayloadType())){
+						on((T)event.getPayload());	
+					}
+				}
+				@Override
+				public Class<?> getTargetType() {
+					return targetType;
+				}
+			};
+		}
+		@Override
+    	public void on(T event) {
+    		eventConsumer.accept(event);
+    	}
+    	public EventListenerProxy proxy() {
+    		return proxy;
+    	}
+    	
+    }
+    
+    private AxonEventListenerAdapter adapterFor(final Object domainService) {
+        AxonEventListenerAdapter annotationEventListenerAdapter = listenerAdapterByDomainService.get(domainService);
+        if (annotationEventListenerAdapter == null) {
+            annotationEventListenerAdapter = new AxonEventListenerAdapter(domainService);
+            listenerAdapterByDomainService.put(domainService, annotationEventListenerAdapter);
+        }
+        return annotationEventListenerAdapter;
+    }
+    
     private AbstractDomainEvent<?> asDomainEventIfPossible(final Object event) {
         if (event instanceof AbstractDomainEvent)
             return (AbstractDomainEvent<?>) event;
@@ -110,4 +170,6 @@ public class EventBusImplementationForAxonSimple extends EventBusImplementationA
         }
     }
 
+
+
 }
\ No newline at end of file
diff --git a/core/plugins/eventbus-axon/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingAxonTest.java b/core/plugins/eventbus-axon/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingAxonTest.java
index 2731000..fb40738 100644
--- a/core/plugins/eventbus-axon/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingAxonTest.java
+++ b/core/plugins/eventbus-axon/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingAxonTest.java
@@ -16,27 +16,39 @@
  */
 package org.apache.isis.applib.services.queryresultscache;
 
-import java.util.concurrent.Callable;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
 
-import org.junit.Before;
-import org.junit.Test;
+import java.util.concurrent.Callable;
 
 import org.apache.isis.applib.fixturescripts.events.FixturesInstallingEvent;
 import org.apache.isis.applib.services.fixturespec.FixtureScriptsDefault;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault;
+import org.apache.isis.core.runtime.services.eventbus.adapter.EventBusImplementationForAxonSimple;
+import org.junit.Before;
+import org.junit.Test;
 
 public class QueryResultsCacheUsingAxonTest {
 
     private QueryResultsCacheInternal queryResultsCache;
-
-    QueryResultsCacheControlUsingAxon control;
+    private QueryResultsCacheControlInternal control;
+    private EventBusImplementationForAxonSimple eventBusImplementationAxon;
 
     @Before
     public void setUp() throws Exception {
         queryResultsCache = new QueryResultsCacheInternal();
-        control = new QueryResultsCacheControlUsingAxon();
+        control = new QueryResultsCacheControlInternal() {
+        	{
+        		eventBusService = new EventBusServiceDefault() {
+        			{
+        				allowLateRegistration = true;
+        				eventBusImplementation = eventBusImplementationAxon 
+        						= new EventBusImplementationForAxonSimple();
+        			}
+				};
+        	}
+        };
+        control.postConstruct();
         queryResultsCache.control = control;
     }
 
@@ -91,8 +103,8 @@ public class QueryResultsCacheUsingAxonTest {
     @Test
     public void cachingDisabled() {
 
-        // given fixtures installing, hence caching disabled
-        control.on(new FixturesInstallingEvent(new FixtureScriptsDefault()));
+    	// given fixtures installing, hence caching disabled
+    	eventBusImplementationAxon.post(new FixturesInstallingEvent(new FixtureScriptsDefault()));
 
         final int[] i = new int[]{0};
 
diff --git a/core/plugins/eventbus-axon/src/test/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefaultUsingAxonSimpleTest.java b/core/plugins/eventbus-axon/src/test/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefaultUsingAxonSimpleTest.java
new file mode 100644
index 0000000..1a070e1
--- /dev/null
+++ b/core/plugins/eventbus-axon/src/test/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefaultUsingAxonSimpleTest.java
@@ -0,0 +1,128 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.runtime.services.eventbus;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.apache.isis.applib.services.registry.ServiceRegistry;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableMap;
+
+public class EventBusServiceDefaultUsingAxonSimpleTest {
+
+    EventBusServiceDefault eventBusService;
+
+    @Before
+    public void setUp() throws Exception {
+        eventBusService = new EventBusServiceDefault() {
+        	{
+        		serviceRegistry = new ServiceRegistry() {
+					@Override public <T> Iterable<T> lookupServices(Class<T> service) { return null; }
+					@Override public <T> T lookupService(Class<T> service) { return null; }
+					@Override public <T> T injectServicesInto(T domainObject) {	return null; }
+					@Override public List<Object> getRegisteredServices() { return null; }
+				}; 
+        	}
+        };
+    }
+
+    public static class Post extends EventBusServiceDefaultTest {
+
+        public static class Subscriber {
+            Object obj;
+        	@org.axonframework.eventhandling.annotation.EventHandler
+            public void on(Object obj) {
+                this.obj = obj;
+            }
+        }
+
+        @Rule
+        public ExpectedException expectedException = ExpectedException.none();
+
+        Subscriber subscriber;
+
+        @Before
+        public void setUp() throws Exception {
+            super.setUp();
+            subscriber = new Subscriber();
+        }
+
+        @Test
+        public void allow_late_registration_means_can_register_after_post() throws Exception {
+            // given
+            eventBusService.init(ImmutableMap.of(
+                    EventBusServiceDefault.KEY_ALLOW_LATE_REGISTRATION, "true",
+                    EventBusServiceDefault.KEY_EVENT_BUS_IMPLEMENTATION, "axon"));
+            assertThat(eventBusService.isAllowLateRegistration(), is(true));
+            assertThat(eventBusService.getImplementation(), is("axon"));
+
+            eventBusService.post(new Object());
+
+            // when
+            eventBusService.register(subscriber);
+
+            // then
+            assertThat(subscriber.obj, is(nullValue()));
+        }
+
+        @Test
+        public void disallow_late_registration_means_cannot_register_after_post() throws Exception {
+            // given
+            eventBusService.init(ImmutableMap.of(
+                    EventBusServiceDefault.KEY_ALLOW_LATE_REGISTRATION, "false",
+                    EventBusServiceDefault.KEY_EVENT_BUS_IMPLEMENTATION, "axon"));
+            assertThat(eventBusService.isAllowLateRegistration(), is(false));
+            assertThat(eventBusService.getImplementation(), is("axon"));
+
+            eventBusService.post(new Object());
+
+            // expect
+            expectedException.expect(IllegalStateException.class);
+
+            // when
+            eventBusService.register(new Subscriber());
+        }
+
+        @Test
+        public void disallow_late_registration_means_can_register_before_post() throws Exception {
+            // given
+            eventBusService.init(ImmutableMap.of(
+                    EventBusServiceDefault.KEY_ALLOW_LATE_REGISTRATION, "false",
+                    EventBusServiceDefault.KEY_EVENT_BUS_IMPLEMENTATION, "axon"));
+            assertThat(eventBusService.isAllowLateRegistration(), is(false));
+            assertThat(eventBusService.getImplementation(), is("axon"));
+
+            eventBusService.register(subscriber);
+
+            // when
+            final Object event = new Object();
+            eventBusService.post(event);
+
+            // then
+            assertThat(subscriber.obj, is(event));
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/core/plugins/eventbus-guava/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForGuava.java b/core/plugins/eventbus-guava/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForGuava.java
index d4b0d46..e83066f 100644
--- a/core/plugins/eventbus-guava/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForGuava.java
+++ b/core/plugins/eventbus-guava/src/main/java/org/apache/isis/core/runtime/services/eventbus/adapter/EventBusImplementationForGuava.java
@@ -16,19 +16,24 @@
  */
 package org.apache.isis.core.runtime.services.eventbus.adapter;
 
-import com.google.common.eventbus.SubscriberExceptionContext;
-import com.google.common.eventbus.SubscriberExceptionHandler;
+import java.util.Objects;
+import java.util.function.Consumer;
 
 import org.apache.isis.applib.events.domain.AbstractDomainEvent;
+import org.apache.isis.applib.services.eventbus.EventBusImplementation;
 import org.apache.isis.core.runtime.services.eventbus.EventBusImplementationAbstract;
 
+import com.google.common.eventbus.SubscriberExceptionContext;
+import com.google.common.eventbus.SubscriberExceptionHandler;
+
 /**
  * A wrapper for a Guava {@link com.google.common.eventbus.EventBus},
  * allowing arbitrary events to be posted and subscribed to.
  */
 public class EventBusImplementationForGuava extends EventBusImplementationAbstract {
 
-    private final com.google.common.eventbus.EventBus eventBus = new com.google.common.eventbus.EventBus(newEventBusSubscriberExceptionHandler());
+    private final com.google.common.eventbus.EventBus eventBus = 
+    		new com.google.common.eventbus.EventBus(newEventBusSubscriberExceptionHandler());
 
     protected SubscriberExceptionHandler newEventBusSubscriberExceptionHandler() {
         return new SubscriberExceptionHandler() {
@@ -49,7 +54,7 @@ public class EventBusImplementationForGuava extends EventBusImplementationAbstra
 
     @Override
     public void unregister(final Object domainService) {
-        // Intentionally no-op.
+        // Intentionally no-op. //TODO [ahuber] why?
         // this.eventBus.unregister(domainService);
     }
 
@@ -65,5 +70,32 @@ public class EventBusImplementationForGuava extends EventBusImplementationAbstra
                 : null;
     }
 
+    @Override
+    public <T> EventListener<T> addEventListener(Class<T> targetType, Consumer<T> onEvent) {
+    	final EventListener<T> eventListener = new GuavaEventListener<>(onEvent);
+    	register(eventListener);
+    	return eventListener;
+    }
+
+    @Override
+    public <T> void removeEventListener(EventListener<T> eventListener) {
+    	this.eventBus.unregister(eventListener);
+    }
+
+	// -- HELPER
+	
+    private static class GuavaEventListener<T> implements EventBusImplementation.EventListener<T> {
+    	private final Consumer<T> eventConsumer;
+    	private GuavaEventListener(Consumer<T> eventConsumer) {
+			this.eventConsumer = Objects.requireNonNull(eventConsumer);
+		}
+		@com.google.common.eventbus.Subscribe
+		@Override
+    	public void on(T event) {
+    		eventConsumer.accept(event);
+    	}
+    }
+
+
 
 }
diff --git a/core/plugins/eventbus-guava/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingGuavaTest.java b/core/plugins/eventbus-guava/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingGuavaTest.java
index 9d6ddf1..9a44cf7 100644
--- a/core/plugins/eventbus-guava/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingGuavaTest.java
+++ b/core/plugins/eventbus-guava/src/test/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCacheUsingGuavaTest.java
@@ -16,27 +16,39 @@
  */
 package org.apache.isis.applib.services.queryresultscache;
 
-import java.util.concurrent.Callable;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
 
-import org.junit.Before;
-import org.junit.Test;
+import java.util.concurrent.Callable;
 
 import org.apache.isis.applib.fixturescripts.events.FixturesInstallingEvent;
 import org.apache.isis.applib.services.fixturespec.FixtureScriptsDefault;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import org.apache.isis.core.runtime.services.eventbus.EventBusServiceDefault;
+import org.apache.isis.core.runtime.services.eventbus.adapter.EventBusImplementationForGuava;
+import org.junit.Before;
+import org.junit.Test;
 
 public class QueryResultsCacheUsingGuavaTest {
 
     private QueryResultsCacheInternal queryResultsCache;
-
-    QueryResultsCacheControlUsingGuava control;
+    private QueryResultsCacheControlInternal control;
+    private EventBusImplementationForGuava eventBusImplementationGuava;
 
     @Before
     public void setUp() throws Exception {
         queryResultsCache = new QueryResultsCacheInternal();
-        control = new QueryResultsCacheControlUsingGuava();
+        control = new QueryResultsCacheControlInternal() {
+        	{
+        		eventBusService = new EventBusServiceDefault() {
+        			{
+        				allowLateRegistration = true;
+        				eventBusImplementation = eventBusImplementationGuava 
+        						= new EventBusImplementationForGuava();
+        			}
+				};
+        	}
+        };
+        control.postConstruct();
         queryResultsCache.control = control;
     }
 
@@ -92,7 +104,7 @@ public class QueryResultsCacheUsingGuavaTest {
     public void cachingDisabled() {
 
         // given fixtures installing, hence caching disabled
-        control.on(new FixturesInstallingEvent(new FixtureScriptsDefault()));
+    	eventBusImplementationGuava.post(new FixturesInstallingEvent(new FixtureScriptsDefault()));
 
         final int[] i = new int[]{0};
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
index 249d8a1..7509418 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/eventbus/EventBusServiceDefault.java
@@ -107,7 +107,7 @@ public abstract class EventBusServiceDefault extends EventBusService {
     }
     
 
-    private boolean allowLateRegistration;
+    protected boolean allowLateRegistration;
     boolean isAllowLateRegistration() {
         return allowLateRegistration;
     }

-- 
To stop receiving notification emails like this one, please contact
ahuber@apache.org.