From 0fd76f075b668c576f22dc66e0f64126f0da6f27 Mon Sep 17 00:00:00 2001 From: "Lincoln Baxter, III" Date: Fri, 2 Aug 2013 15:38:11 -0400 Subject: [PATCH] Implement EventManager API --- .../events/CrossContainerObserverMethod.java | 109 +++++++++++++----- .../cdi/events/EventManagerImpl.java | 44 +++++++ .../cdi/events/EventManagerProducer.java | 22 ++++ .../container/cdi/events/InboundEvent.java | 69 +++++++++++ .../cdi/impl/ContainerBeanRegistrant.java | 2 + .../cdi/impl/ServiceRegistryProducer.java | 2 +- .../lifecycle/WeldAddonLifecycleProvider.java | 24 +++- .../furnace/events/NullEventManagerTest.java | 75 ++++++++++++ .../services/NullAddonLifecycleProvider.java | 7 ++ 9 files changed, 318 insertions(+), 36 deletions(-) create mode 100644 impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerImpl.java create mode 100644 impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerProducer.java create mode 100644 impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/InboundEvent.java create mode 100644 tests/src/test/java/test/org/jboss/forge/furnace/events/NullEventManagerTest.java diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/CrossContainerObserverMethod.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/CrossContainerObserverMethod.java index d322ebe..156093d 100644 --- a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/CrossContainerObserverMethod.java +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/CrossContainerObserverMethod.java @@ -7,72 +7,119 @@ package org.jboss.forge.furnace.container.cdi.events; import java.lang.annotation.Annotation; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Set; import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.enterprise.inject.spi.BeanManager; -import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.EventMetadata; +import javax.inject.Singleton; import org.jboss.forge.furnace.addons.Addon; import org.jboss.forge.furnace.addons.AddonRegistry; +import org.jboss.forge.furnace.container.cdi.impl.AddonProducer; +import org.jboss.forge.furnace.container.cdi.util.BeanManagerUtils; +import org.jboss.forge.furnace.event.EventManager; import org.jboss.forge.furnace.exception.ContainerException; import org.jboss.forge.furnace.services.Exported; -import org.jboss.forge.furnace.spi.ExportedInstance; -import org.jboss.forge.furnace.spi.ServiceRegistry; import org.jboss.forge.furnace.util.AddonFilters; import org.jboss.forge.furnace.util.Annotations; /** * @author Lincoln Baxter, III */ +@Singleton public class CrossContainerObserverMethod { - public void handleEvent(@Observes @Any Object event, EventMetadata metadata) + private ThreadLocal> stack; + + public void handleEvent(@Observes @Any Object event, EventMetadata metadata, BeanManager manager) { - if (Annotations.isAnnotationPresent(event.getClass(), Exported.class)) + try { - Set qualifiers = metadata.getQualifiers(); - try + initStack(); + + Addon self = BeanManagerUtils.getContextualInstance(manager, AddonProducer.class).produceCurrentAddon(); + if (self != null && Annotations.isAnnotationPresent(event.getClass(), Exported.class)) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - ClassLoader eventClassLoader = event.getClass().getClassLoader(); - if (contextClassLoader.equals(eventClassLoader)) - { - AddonRegistry addonRegistry = CDI.current().select(AddonRegistry.class).get(); - for (Addon addon : addonRegistry.getAddons(AddonFilters.allStarted())) + Set qualifiers = metadata.getQualifiers(); + if (!onStack(event, qualifiers)) + try { - // Events should not be fired back into the container from which they originated - ClassLoader addonClassLoader = addon.getClassLoader(); - if (!(event.getClass().getClassLoader().equals(addonClassLoader) - || contextClassLoader.equals(addonClassLoader) - || ClassLoader.getSystemClassLoader().equals(eventClassLoader))) + AddonRegistry addonRegistry = BeanManagerUtils.getContextualInstance(manager, AddonRegistry.class); + for (Addon addon : addonRegistry.getAddons(AddonFilters.allStarted())) { - ServiceRegistry addonServiceRegistry = addon.getServiceRegistry(); - if (addonServiceRegistry != null) + if (!self.getId().equals(addon.getId())) { - ExportedInstance exportedInstance = addonServiceRegistry - .getExportedInstance(BeanManager.class); - if (exportedInstance != null) + EventManager remoteEventManager = addon.getEventManager(); + if (remoteEventManager != null) { - BeanManager manager = exportedInstance.get(); - manager.fireEvent(event, qualifiers.toArray(new Annotation[] {})); + remoteEventManager.fireEvent(event, qualifiers.toArray(new Annotation[] {})); } } } } - } + catch (Exception e) + { + throw new ContainerException("Problems encountered during propagation of event [" + event + + "] with qualifiers [" + qualifiers + "]", e); + } } - catch (Exception e) + else if (event instanceof InboundEvent) { - throw new ContainerException("Problems encountered during propagation of event [" + event - + "] with qualifiers [" + qualifiers + "]", e); + try + { + push((InboundEvent) event); + manager.fireEvent(((InboundEvent) event).getEvent(), ((InboundEvent) event).getQualifiers()); + } + finally + { + pop((InboundEvent) event); + } } } - else + finally { - // do not propagate non-remote org.jboss.forge.furnace.container.cdi.events to other containers. + cleanupStack(); } } + + private boolean onStack(Object event, Set qualifiers) + { + InboundEvent peek = peek(); + if (peek != null && peek.equals(new InboundEvent(event, qualifiers.toArray(new Annotation[] {})))) + return true; + return false; + } + + private void cleanupStack() + { + if (stack != null && stack.get() != null && stack.get().isEmpty()) + stack.remove(); + } + + private void initStack() + { + if (stack == null) + stack = new ThreadLocal>(); + if (stack.get() == null) + stack.set(new ArrayDeque()); + } + + private InboundEvent peek() + { + return this.stack.get().peek(); + } + + private InboundEvent pop(InboundEvent event) + { + return this.stack.get().pop(); + } + + private void push(InboundEvent event) + { + this.stack.get().push(event); + } } diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerImpl.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerImpl.java new file mode 100644 index 0000000..bafda0c --- /dev/null +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Eclipse Public License version 1.0, available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.jboss.forge.furnace.container.cdi.events; + +import java.lang.annotation.Annotation; + +import javax.enterprise.inject.spi.BeanManager; + +import org.jboss.forge.furnace.addons.Addon; +import org.jboss.forge.furnace.event.EventException; +import org.jboss.forge.furnace.event.EventManager; + +/** + * @author Lincoln Baxter, III + * + */ +public class EventManagerImpl implements EventManager +{ + private Addon addon; + private BeanManager manager; + + public EventManagerImpl(Addon addon, BeanManager manager) + { + this.addon = addon; + this.manager = manager; + } + + @Override + public void fireEvent(Object event, Annotation... qualifiers) throws EventException + { + try + { + manager.fireEvent(new InboundEvent(event, qualifiers)); + } + catch (Throwable e) + { + throw new EventException("Could not propagate event to addon [" + addon + "]", e); + } + } +} diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerProducer.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerProducer.java new file mode 100644 index 0000000..054ca7a --- /dev/null +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/EventManagerProducer.java @@ -0,0 +1,22 @@ +package org.jboss.forge.furnace.container.cdi.events; + +import javax.enterprise.inject.Produces; +import javax.inject.Singleton; + +@Singleton +public class EventManagerProducer +{ + private EventManagerImpl manager; + + @Produces + @Singleton + public EventManagerImpl produce() + { + return manager; + } + + public void setEventManager(EventManagerImpl manager) + { + this.manager = manager; + } +} diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/InboundEvent.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/InboundEvent.java new file mode 100644 index 0000000..0f37666 --- /dev/null +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/events/InboundEvent.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Eclipse Public License version 1.0, available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.jboss.forge.furnace.container.cdi.events; + +import java.lang.annotation.Annotation; +import java.util.Arrays; + +/** + * @author Lincoln Baxter, III + * + */ +public class InboundEvent +{ + private Object event; + private Annotation[] qualifiers; + + public InboundEvent(Object event, Annotation[] qualifiers) + { + this.event = event; + this.qualifiers = qualifiers; + } + + public Object getEvent() + { + return event; + } + + public Annotation[] getQualifiers() + { + return qualifiers; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((event == null) ? 0 : event.hashCode()); + result = prime * result + Arrays.hashCode(qualifiers); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + InboundEvent other = (InboundEvent) obj; + if (event == null) + { + if (other.event != null) + return false; + } + else if (!event.equals(other.event)) + return false; + if (!Arrays.equals(qualifiers, other.qualifiers)) + return false; + return true; + } + +} diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ContainerBeanRegistrant.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ContainerBeanRegistrant.java index f8ede1a..bdbdc94 100644 --- a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ContainerBeanRegistrant.java +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ContainerBeanRegistrant.java @@ -6,6 +6,7 @@ import javax.enterprise.inject.spi.Extension; import org.jboss.forge.furnace.container.cdi.events.CrossContainerObserverMethod; +import org.jboss.forge.furnace.container.cdi.events.EventManagerProducer; public class ContainerBeanRegistrant implements Extension { @@ -15,6 +16,7 @@ public void registerWeldSEBeans(@Observes BeforeBeanDiscovery event, BeanManager event.addAnnotatedType(manager.createAnnotatedType(AddonRegistryProducer.class)); event.addAnnotatedType(manager.createAnnotatedType(AddonRepositoryProducer.class)); event.addAnnotatedType(manager.createAnnotatedType(CrossContainerObserverMethod.class)); + event.addAnnotatedType(manager.createAnnotatedType(EventManagerProducer.class)); event.addAnnotatedType(manager.createAnnotatedType(FurnaceProducer.class)); event.addAnnotatedType(manager.createAnnotatedType(ImportedProducer.class)); event.addAnnotatedType(manager.createAnnotatedType(ServiceRegistryProducer.class)); diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ServiceRegistryProducer.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ServiceRegistryProducer.java index ba99caf..b4be0b3 100644 --- a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ServiceRegistryProducer.java +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/impl/ServiceRegistryProducer.java @@ -12,7 +12,7 @@ public class ServiceRegistryProducer @Produces @Singleton - public ServiceRegistry produceGlobalAddonRepository() + public ServiceRegistry produce() { return registry; } diff --git a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/lifecycle/WeldAddonLifecycleProvider.java b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/lifecycle/WeldAddonLifecycleProvider.java index 762b9d6..4f1da32 100644 --- a/impl/src/main/java/org/jboss/forge/furnace/container/cdi/lifecycle/WeldAddonLifecycleProvider.java +++ b/impl/src/main/java/org/jboss/forge/furnace/container/cdi/lifecycle/WeldAddonLifecycleProvider.java @@ -5,6 +5,8 @@ import org.jboss.forge.furnace.Furnace; import org.jboss.forge.furnace.addons.Addon; import org.jboss.forge.furnace.addons.AddonRegistry; +import org.jboss.forge.furnace.container.cdi.events.EventManagerImpl; +import org.jboss.forge.furnace.container.cdi.events.EventManagerProducer; import org.jboss.forge.furnace.container.cdi.impl.AddonProducer; import org.jboss.forge.furnace.container.cdi.impl.AddonRegistryProducer; import org.jboss.forge.furnace.container.cdi.impl.AddonRepositoryProducer; @@ -17,6 +19,7 @@ import org.jboss.forge.furnace.container.cdi.weld.ModularURLScanner; import org.jboss.forge.furnace.container.cdi.weld.ModularWeld; import org.jboss.forge.furnace.container.cdi.weld.ModuleScanResult; +import org.jboss.forge.furnace.event.EventManager; import org.jboss.forge.furnace.event.PostStartup; import org.jboss.forge.furnace.event.PreShutdown; import org.jboss.forge.furnace.lifecycle.AddonLifecycleProvider; @@ -34,6 +37,7 @@ public class WeldAddonLifecycleProvider implements AddonLifecycleProvider private ServiceRegistry serviceRegistry; private BeanManager manager; private ModularWeld weld; + private EventManagerImpl eventManager; @Override public void initialize(Furnace furnace, AddonRegistry registry, Addon self) @@ -73,13 +77,20 @@ public void start(Addon addon) throws Exception ContainerServiceExtension extension = BeanManagerUtils.getContextualInstance(manager, ContainerServiceExtension.class); + ServiceRegistryProducer serviceRegistryProducer = BeanManagerUtils.getContextualInstance(manager, ServiceRegistryProducer.class); serviceRegistry = new ServiceRegistryImpl(furnace.getLockManager(), addon, manager, extension); serviceRegistryProducer.setServiceRegistry(serviceRegistry); - - ServiceRegistry registry = BeanManagerUtils.getContextualInstance(manager, ServiceRegistry.class); - Assert.notNull(registry, "Service registry was null."); + Assert.notNull(BeanManagerUtils.getContextualInstance(manager, ServiceRegistry.class), + "InboundEvent registry was null."); + + EventManagerProducer eventManagerProducer = BeanManagerUtils.getContextualInstance(manager, + EventManagerProducer.class); + eventManager = new EventManagerImpl(addon, manager); + eventManagerProducer.setEventManager(eventManager); + Assert.notNull(BeanManagerUtils.getContextualInstance(manager, EventManager.class), + "InboundEvent registry was null."); } } @@ -104,6 +115,12 @@ public void stop(Addon addon) weld.shutdown(); } + @Override + public EventManager getEventManager(Addon addon) + { + return eventManager; + } + @Override public ServiceRegistry getServiceRegistry(Addon addon) { @@ -115,5 +132,4 @@ public ControlType getControlType() { return ControlType.DEPENDENTS; } - } \ No newline at end of file diff --git a/tests/src/test/java/test/org/jboss/forge/furnace/events/NullEventManagerTest.java b/tests/src/test/java/test/org/jboss/forge/furnace/events/NullEventManagerTest.java new file mode 100644 index 0000000..ccc0fce --- /dev/null +++ b/tests/src/test/java/test/org/jboss/forge/furnace/events/NullEventManagerTest.java @@ -0,0 +1,75 @@ +package test.org.jboss.forge.furnace.events; + +import javax.inject.Inject; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.forge.arquillian.AddonDependency; +import org.jboss.forge.arquillian.Dependencies; +import org.jboss.forge.arquillian.archive.ForgeArchive; +import org.jboss.forge.furnace.addons.Addon; +import org.jboss.forge.furnace.lifecycle.AddonLifecycleProvider; +import org.jboss.forge.furnace.repositories.AddonDependencyEntry; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.junit.Test; +import org.junit.runner.RunWith; + +import test.org.jboss.forge.furnace.mocks.services.PublishedService; +import test.org.jboss.forge.furnace.services.NullAddonLifecycleProvider; + +/** + * @author Lincoln Baxter, III + */ +@RunWith(Arquillian.class) +public class NullEventManagerTest +{ + @Deployment(order = 2) + @Dependencies({ + @AddonDependency(name = "org.jboss.forge.furnace.container:cdi", version = "2.0.0-SNAPSHOT") + }) + public static ForgeArchive getDeployment() + { + ForgeArchive archive = ShrinkWrap + .create(ForgeArchive.class) + .addBeansXML() + .addAsAddonDependencies( + AddonDependencyEntry.create("org.jboss.forge.furnace.container:cdi", "2.0.0-SNAPSHOT"), + AddonDependencyEntry.create("dependency", "1") + ); + + return archive; + } + + @Deployment(name = "dependency,1", testable = false, order = 1) + public static ForgeArchive getDependencyDeployment() + { + ForgeArchive archive = ShrinkWrap.create(ForgeArchive.class) + .addClasses(PublishedService.class) + .addBeansXML() + .addAsAddonDependencies( + AddonDependencyEntry.create("nullcontainer", "1") + ); + + return archive; + } + + @Deployment(name = "nullcontainer,1", testable = false, order = 1) + public static ForgeArchive getContainerDeployment() + { + ForgeArchive archive = ShrinkWrap.create(ForgeArchive.class) + .addClass(NullAddonLifecycleProvider.class) + .addAsServiceProvider(AddonLifecycleProvider.class, NullAddonLifecycleProvider.class); + + return archive; + } + + @Inject + private Addon addon; + + @Test + public void testPublishEventWorksIfEventManagerFromProviderWasNull() throws Exception + { + addon.getEventManager().fireEvent(addon); + } + +} \ No newline at end of file diff --git a/tests/src/test/java/test/org/jboss/forge/furnace/services/NullAddonLifecycleProvider.java b/tests/src/test/java/test/org/jboss/forge/furnace/services/NullAddonLifecycleProvider.java index b6488dd..18f3122 100644 --- a/tests/src/test/java/test/org/jboss/forge/furnace/services/NullAddonLifecycleProvider.java +++ b/tests/src/test/java/test/org/jboss/forge/furnace/services/NullAddonLifecycleProvider.java @@ -9,6 +9,7 @@ import org.jboss.forge.furnace.Furnace; import org.jboss.forge.furnace.addons.Addon; import org.jboss.forge.furnace.addons.AddonRegistry; +import org.jboss.forge.furnace.event.EventManager; import org.jboss.forge.furnace.lifecycle.AddonLifecycleProvider; import org.jboss.forge.furnace.lifecycle.ControlType; import org.jboss.forge.furnace.spi.ServiceRegistry; @@ -42,6 +43,12 @@ public ServiceRegistry getServiceRegistry(Addon addon) throws Exception return null; } + @Override + public EventManager getEventManager(Addon addon) + { + return null; + } + @Override public void postStartup(Addon addon) throws Exception {