From de5bee4947e545c912bb9c4a1f1c6ace2a1bb73a Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 21 Jun 2019 09:29:06 +0200 Subject: [PATCH] refactor listener modules --- .../main/java/sonia/scm/CloseableModule.java | 9 ++ .../java/sonia/scm/EagerSingletonModule.java | 104 +++++---------- .../java/sonia/scm/ScmEventBusModule.java | 61 ++++----- .../java/sonia/scm/ScmInitializerModule.java | 118 +++--------------- .../sonia/scm/EagerSingletonModuleTest.java | 47 +++++++ .../java/sonia/scm/ScmEventBusModuleTest.java | 35 ++++++ .../sonia/scm/ScmInitializerModuleTest.java | 29 +++++ 7 files changed, 195 insertions(+), 208 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/EagerSingletonModuleTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/ScmEventBusModuleTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/ScmInitializerModuleTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/CloseableModule.java b/scm-webapp/src/main/java/sonia/scm/CloseableModule.java index 9c4054a24a..dd03ff3e30 100644 --- a/scm-webapp/src/main/java/sonia/scm/CloseableModule.java +++ b/scm-webapp/src/main/java/sonia/scm/CloseableModule.java @@ -14,6 +14,12 @@ import java.lang.ref.WeakReference; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; +/** + * Guice module which captures all classes which are implementing the {@link Closeable}. These classes can be later + * closed, by injecting the {@link CloseableModule} and calling {@link #closeAll()}. + * + * @author Sebastian Sdorra + */ public final class CloseableModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(CloseableModule.class); @@ -36,6 +42,9 @@ public final class CloseableModule extends AbstractModule { bind(CloseableModule.class).toInstance(this); } + /** + * Closes all captured instances. + */ public void closeAll() { LOG.debug("close all registered closeables"); WeakReference reference = closeables.poll(); diff --git a/scm-webapp/src/main/java/sonia/scm/EagerSingletonModule.java b/scm-webapp/src/main/java/sonia/scm/EagerSingletonModule.java index fd519beb31..3540006061 100644 --- a/scm-webapp/src/main/java/sonia/scm/EagerSingletonModule.java +++ b/scm-webapp/src/main/java/sonia/scm/EagerSingletonModule.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2010, Sebastian Sdorra * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + *

* 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,9 +24,8 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + *

* http://bitbucket.org/sdorra/scm-manager - * */ @@ -38,96 +37,51 @@ import com.google.common.collect.Sets; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.TypeLiteral; -import com.google.inject.matcher.AbstractMatcher; -import com.google.inject.matcher.Matcher; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -//~--- JDK imports ------------------------------------------------------------ - -import java.lang.annotation.Annotation; - import java.util.Set; +//~--- JDK imports ------------------------------------------------------------ + /** + * Guice module which captures all classes which are annotated with {@link EagerSingleton}. These classes can be later + * initialized. * * @author Sebastian Sdorra */ -public class EagerSingletonModule extends AbstractModule -{ +public class EagerSingletonModule extends AbstractModule { + + private static final Logger LOG = LoggerFactory.getLogger(EagerSingletonModule.class); + + private final Set> eagerSingletons = Sets.newHashSet(); /** - * the logger for EagerSingletonModule - */ - private static final Logger logger = - LoggerFactory.getLogger(EagerSingletonModule.class); - - //~--- methods -------------------------------------------------------------- - - /** - * Method description + * Initialize all captured classes. * - * - * @param injector + * @param injector injector for initialization */ - void initialize(Injector injector) - { - for (Class clazz : eagerSingletons) - { - logger.info("initialize eager singleton {}", clazz.getName()); + public void initialize(Injector injector) { + for (Class clazz : eagerSingletons) { + LOG.info("initialize eager singleton {}", clazz.getName()); injector.getInstance(clazz); } } - /** - * Method description - * - */ @Override - protected void configure() - { - bindListener(isAnnotatedWith(EagerSingleton.class), new TypeListener() - { - + protected void configure() { + bindListener(MoreMatchers.isAnnotatedWith(EagerSingleton.class), new TypeListener() { @Override - public void hear(TypeLiteral type, TypeEncounter encounter) - { - eagerSingletons.add(type.getRawType()); + public void hear(TypeLiteral type, TypeEncounter encounter) { + Class rawType = type.getRawType(); + LOG.trace("register eager singleton {}", rawType); + eagerSingletons.add(rawType); } }); bind(EagerSingletonModule.class).toInstance(this); } - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * - * @param annotation - * - * @return - */ - private Matcher> isAnnotatedWith( - final Class annotation) - { - return new AbstractMatcher>() - { - @Override - public boolean matches(TypeLiteral type) - { - return type.getRawType().isAnnotationPresent(annotation); - } - }; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final Set> eagerSingletons = Sets.newHashSet(); } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmEventBusModule.java b/scm-webapp/src/main/java/sonia/scm/ScmEventBusModule.java index 4f1d4ca8ab..c231e8ccfd 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmEventBusModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmEventBusModule.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2010, Sebastian Sdorra * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + *

* 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,56 +24,49 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + *

* http://bitbucket.org/sdorra/scm-manager - * */ - package sonia.scm; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.annotations.VisibleForTesting; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; import com.google.inject.matcher.Matchers; import com.google.inject.spi.InjectionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; - import sonia.scm.event.ScmEventBus; /** + * Registers every instance to the scm-manager event bus. * * @author Sebastian Sdorra */ -public class ScmEventBusModule extends AbstractModule -{ +public class ScmEventBusModule extends AbstractModule { + + private final ScmEventBus eventBus; + + public ScmEventBusModule() { + this(ScmEventBus.getInstance()); + } + + @VisibleForTesting + ScmEventBusModule(ScmEventBus eventBus) { + this.eventBus = eventBus; + } - /** - * Method description - * - */ @Override - protected void configure() - { - bindListener(Matchers.any(), new TypeListener() - { - + protected void configure() { + bindListener(Matchers.any(), new TypeListener() { @Override - public void hear(TypeLiteral type, TypeEncounter encounter) - { - encounter.register(new InjectionListener() - { - @Override - public void afterInjection(Object object) - { - ScmEventBus.getInstance().register(object); - } - }); + public void hear(TypeLiteral type, TypeEncounter encounter) { + encounter.register((InjectionListener) object -> eventBus.register(object)); } - }); } } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmInitializerModule.java b/scm-webapp/src/main/java/sonia/scm/ScmInitializerModule.java index 16675749ac..458dadf1b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmInitializerModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmInitializerModule.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2010, Sebastian Sdorra * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + *

* 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + *

* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -24,9 +24,8 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + *

* http://bitbucket.org/sdorra/scm-manager - * */ @@ -36,114 +35,35 @@ package sonia.scm; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; -import com.google.inject.matcher.AbstractMatcher; -import com.google.inject.matcher.Matcher; import com.google.inject.spi.InjectionListener; import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + * Initializes all instances which are implementing the {@link Initable} interface. * * @author Sebastian Sdorra */ -public class ScmInitializerModule extends AbstractModule -{ +public class ScmInitializerModule extends AbstractModule { - /** - * the logger for ScmInitializerModule - */ - private static final Logger logger = - LoggerFactory.getLogger(ScmInitializerModule.class); + private static final Logger LOG = LoggerFactory.getLogger(ScmInitializerModule.class); - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - */ @Override - protected void configure() - { - bindListener(isSubtypeOf(Initable.class), new TypeListener() - { - + protected void configure() { + bindListener(MoreMatchers.isSubtypeOf(Initable.class), new TypeListener() { @Override - public void hear(TypeLiteral type, TypeEncounter encounter) - { - encounter.register(new InjectionListener() - { - @Override - public void afterInjection(Object i) - { - if (logger.isTraceEnabled()) - { - logger.trace("initialize initable {}", i.getClass()); - } + public void hear(TypeLiteral type, TypeEncounter encounter) { + encounter.register((InjectionListener) i -> { + LOG.trace("initialize initable {}", i.getClass()); - Initable initable = (Initable) i; + Initable initable = (Initable) i; - initable.init(SCMContext.getContext()); - } + initable.init(SCMContext.getContext()); }); } }); } - /** - * Method description - * - * - * @param subtype - * @param supertype - * - * @return - */ - private boolean typeIsSubtypeOf(TypeLiteral subtype, - TypeLiteral supertype) - { - - // First check that raw types are compatible - // Then check that generic types are compatible! HOW???? - return (subtype.equals(supertype) - || (supertype.getRawType().isAssignableFrom(subtype.getRawType()) - && supertype.equals(subtype.getSupertype(supertype.getRawType())))); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param supertype - * - * @return - */ - private Matcher> isSubtypeOf(final Class supertype) - { - return isSubtypeOf(TypeLiteral.get(supertype)); - } - - /** - * Method description - * - * - * @param supertype - * - * @return - */ - private Matcher> isSubtypeOf(final TypeLiteral supertype) - { - return new AbstractMatcher>() - { - @Override - public boolean matches(TypeLiteral type) - { - return typeIsSubtypeOf(type, supertype); - } - }; - } } diff --git a/scm-webapp/src/test/java/sonia/scm/EagerSingletonModuleTest.java b/scm-webapp/src/test/java/sonia/scm/EagerSingletonModuleTest.java new file mode 100644 index 0000000000..820016fbbd --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/EagerSingletonModuleTest.java @@ -0,0 +1,47 @@ +package sonia.scm; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.junit.jupiter.api.Test; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import static org.assertj.core.api.Assertions.assertThat; + +class EagerSingletonModuleTest { + + @Test + void shouldInitializeEagerSingletons() { + Injector injector = Guice.createInjector(new EagerSingletonModule(), new EagerTestModule()); + injector.getInstance(EagerSingletonModule.class).initialize(injector); + + Capturer capturer = injector.getInstance(Capturer.class); + assertThat(capturer.value).isEqualTo("eager!"); + } + + public static class EagerTestModule extends AbstractModule { + + @Override + protected void configure() { + bind(Capturer.class); + bind(Eager.class); + } + } + + @Singleton + public static class Capturer { + private String value; + } + + @EagerSingleton + public static class Eager { + + @Inject + public Eager(Capturer capturer) { + capturer.value = "eager!"; + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/ScmEventBusModuleTest.java b/scm-webapp/src/test/java/sonia/scm/ScmEventBusModuleTest.java new file mode 100644 index 0000000000..951a299248 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/ScmEventBusModuleTest.java @@ -0,0 +1,35 @@ +package sonia.scm; + +import com.github.legman.Subscribe; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.junit.jupiter.api.Test; +import sonia.scm.event.LegmanScmEventBus; + +import static org.assertj.core.api.Assertions.assertThat; + +class ScmEventBusModuleTest { + + @Test + void shouldRegisterInstance() { + LegmanScmEventBus eventBus = new LegmanScmEventBus(); + + Injector injector = Guice.createInjector(new ScmEventBusModule(eventBus)); + Listener listener = injector.getInstance(Listener.class); + + eventBus.post("hello"); + + assertThat(listener.message).isEqualTo("hello"); + } + + public static class Listener { + + private String message; + + @Subscribe(async = false) + public void receive(String message) { + this.message = message; + } + + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/ScmInitializerModuleTest.java b/scm-webapp/src/test/java/sonia/scm/ScmInitializerModuleTest.java new file mode 100644 index 0000000000..2491c5c43c --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/ScmInitializerModuleTest.java @@ -0,0 +1,29 @@ +package sonia.scm; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ScmInitializerModuleTest { + + @Test + void shouldInitializeInstances() { + Injector injector = Guice.createInjector(new ScmInitializerModule()); + InitializeMe instance = injector.getInstance(InitializeMe.class); + + assertThat(instance.initialized).isTrue(); + } + + public static class InitializeMe implements Initable { + + private boolean initialized = false; + + @Override + public void init(SCMContextProvider context) { + this.initialized = true; + } + } + +}