JAMES-1738 Import Onami lifeCycle and its tests
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c1f94a46 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c1f94a46 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c1f94a46 Branch: refs/heads/master Commit: c1f94a46d5fcd5e63c6eac8f64997b7ee371f4da Parents: 91d3ebf Author: Benoit Tellier <[email protected]> Authored: Wed Oct 5 11:16:46 2016 +0200 Committer: Benoit Tellier <[email protected]> Committed: Wed Oct 5 14:48:52 2016 +0200 ---------------------------------------------------------------------- server/container/guice/onami/pom.xml | 198 +++++++++++++++++ .../onami/lifecycle/AbstractBasicStageable.java | 50 +++++ .../lifecycle/AbstractMethodTypeListener.java | 112 ++++++++++ .../onami/lifecycle/AbstractStageable.java | 60 +++++ .../james/onami/lifecycle/DefaultStager.java | 218 +++++++++++++++++++ .../james/onami/lifecycle/DisposingStager.java | 50 +++++ .../james/onami/lifecycle/LifeCycleModule.java | 117 ++++++++++ .../onami/lifecycle/LifeCycleStageModule.java | 200 +++++++++++++++++ .../james/onami/lifecycle/NoOpStageHandler.java | 47 ++++ .../lifecycle/NoOpStageableTypeMapper.java | 34 +++ .../james/onami/lifecycle/PreDestroyModule.java | 52 +++++ .../james/onami/lifecycle/StageHandler.java | 43 ++++ .../apache/james/onami/lifecycle/Stageable.java | 41 ++++ .../james/onami/lifecycle/StageableMethod.java | 86 ++++++++ .../onami/lifecycle/StageableTypeMapper.java | 39 ++++ .../apache/james/onami/lifecycle/Stager.java | 64 ++++++ .../onami/lifecycle/DefaultStagerTestCase.java | 105 +++++++++ .../onami/lifecycle/MultiLifeCycleObject.java | 76 +++++++ .../onami/lifecycle/MultiLifeCycleTestCase.java | 144 ++++++++++++ .../james/onami/lifecycle/StageObject1.java | 45 ++++ .../james/onami/lifecycle/StageObject2.java | 45 ++++ .../onami/lifecycle/StagingOrderTestCase.java | 85 ++++++++ .../james/onami/lifecycle/TestAnnotationA.java | 33 +++ .../james/onami/lifecycle/TestAnnotationB.java | 33 +++ .../james/onami/lifecycle/TestAnnotationC.java | 33 +++ server/container/guice/pom.xml | 1 + 26 files changed, 2011 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/pom.xml ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/pom.xml b/server/container/guice/onami/pom.xml new file mode 100644 index 0000000..d7433d6 --- /dev/null +++ b/server/container/guice/onami/pom.xml @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>james-server-guice</artifactId> + <groupId>org.apache.james</groupId> + <version>3.0.0-beta5-SNAPSHOT</version> + </parent> + + <artifactId>james-server-onami</artifactId> + <packaging>jar</packaging> + + <name>Apache James :: Server :: Onami</name> + <description>Guice tooling from Onami project</description> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>disable-build-for-older-jdk</id> + <activation> + <jdk>(,1.8)</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <phase>none</phase> + </execution> + <execution> + <id>jar</id> + <phase>none</phase> + </execution> + <execution> + <id>test-jar</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <executions> + <execution> + <id>default-install</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>default-resources</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testResources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <executions> + <execution> + <id>attach-descriptor</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>build-for-jdk-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build><dependencies> + <dependency> + <groupId>com.google.inject</groupId> + <artifactId>guice</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + </profile> + <profile> + <id>animal-sniffer-java-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <configuration> + <signature> + <groupId>org.codehaus.mojo.signature</groupId> + <artifactId>java18</artifactId> + <version>1.0</version> + </signature> + </configuration> + <executions> + <execution> + <id>check_java_8</id> + <phase>test</phase> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractBasicStageable.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractBasicStageable.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractBasicStageable.java new file mode 100644 index 0000000..a065a55 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractBasicStageable.java @@ -0,0 +1,50 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +/** + * Base implementation for stageables. + * + * @author Mikhail Mazursky + */ +public abstract class AbstractBasicStageable<S> + implements Stageable +{ + + /** + * Object to stage. + */ + protected final S object; + + protected AbstractBasicStageable( S object ) + { + this.object = object; + } + + /** + * {@inheritDoc} + */ + @Override + public final String toString() + { + return object.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractMethodTypeListener.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractMethodTypeListener.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractMethodTypeListener.java new file mode 100644 index 0000000..6ae63c5 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractMethodTypeListener.java @@ -0,0 +1,112 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.TypeLiteral; +import com.google.inject.spi.TypeEncounter; +import com.google.inject.spi.TypeListener; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.List; + +/** + * A Guice {@code TypeListener} to hear annotated methods with lifecycle annotations. + */ +abstract class AbstractMethodTypeListener + implements TypeListener +{ + + /** + * The {@code java} package constants. + */ + private static final String JAVA_PACKAGE = "java"; + + /** + * The lifecycle annotations to search on methods in the order to be searched. + */ + protected final List<? extends Class<? extends Annotation>> annotationTypes; + + /** + * Creates a new methods listener instance. + * + * @param annotationTypes the lifecycle annotations to search on methods in the order to be searched. + */ + public AbstractMethodTypeListener( List<? extends Class<? extends Annotation>> annotationTypes ) + { + this.annotationTypes = annotationTypes; + } + + /** + * {@inheritDoc} + */ + @Override + public final <I> void hear( TypeLiteral<I> type, TypeEncounter<I> encounter ) + { + hear( type, type.getRawType(), encounter ); + } + + /** + * Allows traverse the input klass hierarchy. + * + * @param parentType the owning type being heard + * @param klass encountered by Guice. + * @param encounter the injection context. + */ + private <I> void hear( final TypeLiteral<I> parentType, Class<? super I> klass, TypeEncounter<I> encounter ) + { + Package pkg; + if ( klass == null || ( ( pkg = klass.getPackage() ) != null && pkg.getName().startsWith( JAVA_PACKAGE ) ) ) + { + return; + } + + for ( Class<? extends Annotation> annotationType : annotationTypes ) + { + for ( Method method : klass.getDeclaredMethods() ) + { + if ( method.isAnnotationPresent( annotationType ) ) + { + if ( method.getParameterTypes().length != 0 ) + { + encounter.addError( "Annotated methods with @%s must not accept any argument, found %s", + annotationType.getName(), method ); + } + + hear( method, parentType, encounter, annotationType ); + } + } + } + + hear( parentType, klass.getSuperclass(), encounter ); + } + + /** + * Allows implementations to define the behavior when lifecycle annotation is found on the method. + * + * @param method encountered by this type handler. + * @param parentType the owning type being heard + * @param encounter the injection context. + * @param annotationType the annotation type that was specified. + */ + protected abstract <I> void hear( Method method, TypeLiteral<I> parentType, TypeEncounter<I> encounter, + Class<? extends Annotation> annotationType ); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractStageable.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractStageable.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractStageable.java new file mode 100644 index 0000000..07cbc84 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/AbstractStageable.java @@ -0,0 +1,60 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +/** + * Base implementation for stageables. + * + * @author Mikhail Mazursky + */ +public abstract class AbstractStageable<S> + extends AbstractBasicStageable<S> +{ + + protected AbstractStageable( S object ) + { + super( object ); + } + + /** + * {@inheritDoc} + */ + @Override + public final void stage( StageHandler stageHandler ) + { + try + { + doStage(); + } + catch ( Throwable e ) + { + stageHandler.onError( object, e ); + return; + } + stageHandler.onSuccess( object ); + } + + /** + * Does actual object staging. + * + * @throws Exception + */ + protected abstract void doStage() throws Exception; +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DefaultStager.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DefaultStager.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DefaultStager.java new file mode 100644 index 0000000..96cf2a2 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DefaultStager.java @@ -0,0 +1,218 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.io.Closeable; +import java.lang.annotation.Annotation; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Queue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Default {@link Stager} implementation. + */ +public class DefaultStager<A extends Annotation> + implements DisposingStager<A> +{ + private final Class<A> stage; + + /** + * Stack of elements have to be disposed. + */ + private final Queue<Stageable> stageables; + + /** + * @param stage the annotation that specifies this stage + */ + public DefaultStager( Class<A> stage ) + { + this( stage, Order.FIRST_IN_FIRST_OUT ); + } + + /** + * @param stage the annotation that specifies this stage + * @param mode execution order + */ + public DefaultStager( Class<A> stage, Order mode ) + { + this.stage = stage; + + Queue<Stageable> localStageables; + switch ( mode ) + { + case FIRST_IN_FIRST_OUT: + { + localStageables = new ArrayDeque<Stageable>(); + break; + } + + case FIRST_IN_LAST_OUT: + { + localStageables = Collections.asLifoQueue( new ArrayDeque<Stageable>() ); + break; + } + + default: + { + throw new IllegalArgumentException( "Unknown mode: " + mode ); + } + } + stageables = localStageables; + } + + /** + * {@inheritDoc} + */ + @Override + public void register( Stageable stageable ) + { + synchronized ( stageables ) + { + stageables.add( stageable ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public <T extends ExecutorService> T register( T executorService ) + { + register( new ExecutorServiceStageable( executorService ) ); + return executorService; + } + + /** + * {@inheritDoc} + */ + @Override + public <T extends Closeable> T register( T closeable ) + { + register( new CloseableStageable( closeable ) ); + return closeable; + } + + /** + * {@inheritDoc} + */ + @Override + public void stage() + { + stage( null ); + } + + /** + * {@inheritDoc} + */ + @Override + public void stage( StageHandler stageHandler ) + { + if ( stageHandler == null ) + { + stageHandler = new NoOpStageHandler(); + } + + while ( true ) + { + Stageable stageable; + synchronized ( stageables ) + { + stageable = stageables.poll(); + } + if ( stageable == null ) + { + break; + } + stageable.stage( stageHandler ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Class<A> getStage() + { + return stage; + } + + /** + * specifies ordering for a {@link DefaultStager} + */ + public static enum Order + { + /** + * FIFO + */ + FIRST_IN_FIRST_OUT, + + /** + * FILO/LIFO + */ + FIRST_IN_LAST_OUT + } + + private static class CloseableStageable extends AbstractStageable<Closeable> + { + + public CloseableStageable( Closeable closeable ) + { + super( closeable ); + } + + @Override + protected void doStage() throws Exception + { + object.close(); + } + + } + + private static class ExecutorServiceStageable extends AbstractStageable<ExecutorService> + { + + public ExecutorServiceStageable( ExecutorService executor ) + { + super( executor ); + } + + @Override + protected void doStage() throws Exception + { + object.shutdown(); + try + { + if ( !object.awaitTermination( 1, TimeUnit.MINUTES ) ) + { + object.shutdownNow(); + } + } + catch ( InterruptedException e ) + { + object.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DisposingStager.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DisposingStager.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DisposingStager.java new file mode 100644 index 0000000..87bec80 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/DisposingStager.java @@ -0,0 +1,50 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.io.Closeable; +import java.lang.annotation.Annotation; +import java.util.concurrent.ExecutorService; + +/** + * {@link org.apache.onami.lifecycle.core.Stager} that disposes resources. + * + * @author Mikhail Mazursky + */ +public interface DisposingStager<A extends Annotation> extends Stager<A> +{ + + /** + * Register an {@link java.util.concurrent.ExecutorService} to be staged. + * + * @param executorService object to be staged to dispose resources. + * @return Staged object + */ + <T extends ExecutorService> T register( T executorService ); + + /** + * Register a {@link java.io.Closeable} to be staged. + * + * @param closeable object to be staged to dispose resources. + * @return Staged object + */ + <T extends Closeable> T register( T closeable ); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleModule.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleModule.java new file mode 100644 index 0000000..1827e38 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleModule.java @@ -0,0 +1,117 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.AbstractModule; +import com.google.inject.ProvisionException; +import com.google.inject.TypeLiteral; +import com.google.inject.matcher.Matcher; +import com.google.inject.spi.InjectionListener; +import com.google.inject.spi.TypeEncounter; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +import static com.google.inject.matcher.Matchers.any; +import static java.lang.String.format; +import static java.util.Arrays.asList; + +/** + * Guice module to register methods to be invoked after injection is complete. + */ +public abstract class LifeCycleModule + extends AbstractModule +{ + + /** + * Binds lifecycle listener. + * + * @param annotation the lifecycle annotation to be searched. + */ + protected final void bindLifeCycle( Class<? extends Annotation> annotation ) + { + bindLifeCycle( annotation, any() ); + } + + /** + * Binds lifecycle listener. + * + * @param annotation the lifecycle annotation to be searched. + * @param typeMatcher the filter for injectee types. + */ + protected final void bindLifeCycle( Class<? extends Annotation> annotation, Matcher<? super TypeLiteral<?>> typeMatcher ) + { + bindLifeCycle( asList( annotation ), typeMatcher ); + } + + /** + * Binds lifecycle listener. + * + * @param annotations the lifecycle annotations to be searched in the order to be searched. + * @param typeMatcher the filter for injectee types. + */ + protected final void bindLifeCycle( List<? extends Class<? extends Annotation>> annotations, Matcher<? super TypeLiteral<?>> typeMatcher ) + { + bindListener( typeMatcher, new AbstractMethodTypeListener( annotations ) + { + + @Override + protected <I> void hear( final Method method, TypeLiteral<I> parentType, TypeEncounter<I> encounter, + final Class<? extends Annotation> annotationType ) + { + encounter.register( new InjectionListener<I>() + { + + @Override + public void afterInjection( I injectee ) + { + try + { + method.invoke( injectee ); + } + catch ( IllegalArgumentException e ) + { + // should not happen, anyway... + throw new ProvisionException( + format( "Method @%s %s requires arguments", annotationType.getName(), method ), e ); + } + catch ( IllegalAccessException e ) + { + throw new ProvisionException( + format( "Impossible to access to @%s %s on %s", annotationType.getName(), method, + injectee ), e ); + } + catch ( InvocationTargetException e ) + { + throw new ProvisionException( + format( "An error occurred while invoking @%s %s on %s", annotationType.getName(), + method, injectee ), e.getCause() ); + } + } + + } ); + } + + } ); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleStageModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleStageModule.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleStageModule.java new file mode 100644 index 0000000..8698df1 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/LifeCycleStageModule.java @@ -0,0 +1,200 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.Key; +import com.google.inject.TypeLiteral; +import com.google.inject.matcher.Matcher; +import com.google.inject.spi.InjectionListener; +import com.google.inject.spi.TypeEncounter; +import com.google.inject.util.Types; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.List; + +import static com.google.inject.matcher.Matchers.any; +import static java.util.Arrays.asList; + +/** + * Guice module to register methods to be invoked when {@link Stager#stage()} is invoked. + * <p/> + * Module instance have has so it must not be used to construct more than one {@link com.google.inject.Injector}. + */ +public abstract class LifeCycleStageModule + extends LifeCycleModule +{ + + private List<BindingBuilder<?>> bindings; + + /** + * Convenience to generate the correct key for retrieving stagers from an injector. + * E.g. + * <p/> + * <code><pre> + * Stager<MyAnnotation> stager = injector.getInstance( LifeCycleStageModule.key( MyAnnotation.class ) ); + * </pre></code> + * + * @param stage the annotation that represents this stage and the methods with this annotation + * @param <A> the Annotation type + * @return the Guice key to use for accessing the stager for the input stage + */ + public static <A extends Annotation> Key<Stager<A>> key( Class<A> stage ) + { + return Key.get( type( stage ) ); + } + + private static <A extends Annotation> TypeLiteral<Stager<A>> type( Class<A> stage ) + { + ParameterizedType parameterizedType = Types.newParameterizedTypeWithOwner( null, Stager.class, stage ); + //noinspection unchecked + @SuppressWarnings( "unchecked" ) // TODO + TypeLiteral<Stager<A>> stagerType = (TypeLiteral<Stager<A>>) TypeLiteral.get( parameterizedType ); + return stagerType; + } + + /** + * {@inheritDoc} + */ + @Override + protected final void configure() + { + if ( bindings != null ) + { + throw new IllegalStateException( "Re-entry is not allowed" ); + } + bindings = new ArrayList<BindingBuilder<?>>(); + try + { + configureBindings(); + for ( BindingBuilder<?> binding : bindings ) + { + bind( binding ); + } + } + finally + { + bindings = null; + } + } + + private <A extends Annotation> void bind( BindingBuilder<A> binding ) + { + final Stager<A> stager = binding.stager; + final StageableTypeMapper typeMapper = binding.typeMapper; + bind( type( stager.getStage() ) ).toInstance( stager ); + + bindListener( binding.typeMatcher, new AbstractMethodTypeListener( asList( stager.getStage() ) ) + { + + @Override + protected <I> void hear( final Method stageMethod, final TypeLiteral<I> parentType, + final TypeEncounter<I> encounter, + final Class<? extends Annotation> annotationType ) + { + encounter.register( new InjectionListener<I>() + { + + @Override + public void afterInjection( I injectee ) + { + Stageable stageable = new StageableMethod( stageMethod, injectee ); + stager.register( stageable ); + typeMapper.registerType( stageable, parentType ); + } + + } ); + } + + } ); + } + + protected abstract void configureBindings(); + + protected final <A extends Annotation> MapperBinding bindStager( Stager<A> stager ) + { + BindingBuilder<A> builder = new BindingBuilder<A>( checkNotNull( stager, "Argument 'stager' must be not null" ) ); + bindings.add( builder ); + return builder; + } + + protected interface MatcherBinding + { + /** + * Sets the filter for injectee types. + * + * @param typeMatcher the filter for injectee types. + */ + void matching( Matcher<? super TypeLiteral<?>> typeMatcher ); + } + + protected interface MapperBinding extends MatcherBinding + { + /** + * Sets the container to register mappings from {@link Stageable}s to the types that created them. + * + * @param typeMapper container to map {@link Stageable}s to types. + */ + MatcherBinding mappingWith( StageableTypeMapper typeMapper ); + } + + /** + * Builder pattern helper. + */ + private static class BindingBuilder<A extends Annotation> implements MapperBinding + { + + private Matcher<? super TypeLiteral<?>> typeMatcher = any(); + + private final Stager<A> stager; + + private StageableTypeMapper typeMapper = new NoOpStageableTypeMapper(); + + public BindingBuilder( Stager<A> stager ) + { + this.stager = stager; + } + + @Override + public MatcherBinding mappingWith( StageableTypeMapper typeMapper ) + { + this.typeMapper = checkNotNull( typeMapper, "Argument 'typeMapper' must be not null." ); + return this; + } + + @Override + public void matching( Matcher<? super TypeLiteral<?>> typeMatcher ) + { + this.typeMatcher = checkNotNull( typeMatcher, "Argument 'typeMatcher' must be not null" ); + } + + } + + private static <T> T checkNotNull( T object, String message ) + { + if ( object == null ) + { + throw new IllegalArgumentException( message ); + } + return object; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageHandler.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageHandler.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageHandler.java new file mode 100644 index 0000000..b543857 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageHandler.java @@ -0,0 +1,47 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +/** + * NOP {@code StageHandler} implementation. + */ +public final class NoOpStageHandler + implements StageHandler +{ + + /** + * {@inheritDoc} + */ + @Override + public <I, E extends Throwable> void onError( I injectee, E error ) + { + // do nothing + } + + /** + * {@inheritDoc} + */ + @Override + public <I> void onSuccess( I injectee ) + { + // do nothing + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageableTypeMapper.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageableTypeMapper.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageableTypeMapper.java new file mode 100644 index 0000000..f273b6f --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/NoOpStageableTypeMapper.java @@ -0,0 +1,34 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.TypeLiteral; + +class NoOpStageableTypeMapper + implements StageableTypeMapper +{ + + @Override + public <I> void registerType( Stageable stageable, TypeLiteral<I> parentType ) + { + // NOP + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/PreDestroyModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/PreDestroyModule.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/PreDestroyModule.java new file mode 100644 index 0000000..e28060b --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/PreDestroyModule.java @@ -0,0 +1,52 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.TypeLiteral; + +import javax.annotation.PreDestroy; + +/** + * Guice module to register methods to be invoked when {@link org.apache.onami.lifecycle.core.Stager#stage()} is invoked. + * <p/> + * Module instance have state so it must not be used to construct more than one {@link com.google.inject.Injector}. + * + * @author Mikhail Mazursky + */ +public class PreDestroyModule + extends LifeCycleStageModule +{ + + private final DisposingStager<PreDestroy> stager = new DefaultStager<PreDestroy>( + PreDestroy.class, DefaultStager.Order.FIRST_IN_LAST_OUT ); + + @Override + protected void configureBindings() + { + bindStager( stager ); + bind( new TypeLiteral<DisposingStager<PreDestroy>>() {} ).toInstance( stager ); + } + + public DisposingStager<PreDestroy> getStager() + { + return stager; + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageHandler.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageHandler.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageHandler.java new file mode 100644 index 0000000..fa2a576 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageHandler.java @@ -0,0 +1,43 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +/** + * A {@link StageHandler} instance is used to track staging progresses. + */ +public interface StageHandler +{ + + /** + * Tracks the input injectee successfully staged the resources. + * + * @param injectee the injectee to be staged + */ + <I> void onSuccess( I injectee ); + + /** + * Tracks an error occurred while the input injectee staged the resources. + * + * @param injectee the injectee to be staged + * @param error the exception occurred + */ + <I, E extends Throwable> void onError( I injectee, E error ); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stageable.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stageable.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stageable.java new file mode 100644 index 0000000..8865cca --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stageable.java @@ -0,0 +1,41 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +/** + * Object that knows how to stage some resources. + */ +public interface Stageable +{ + + /** + * Stage allocated resources, tracking progresses in the + * input {@code StageHandler}. + * + * @param stageHandler the handler to track progresses. + */ + void stage( StageHandler stageHandler ); + + /** + * @return Description of a stageable resource. + */ + @Override + String toString(); +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableMethod.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableMethod.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableMethod.java new file mode 100644 index 0000000..b10315a --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableMethod.java @@ -0,0 +1,86 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * A {@link StageableMethod} is a reference to a stageable injectee + * and related method to release resources. + */ +final class StageableMethod + extends AbstractBasicStageable<Object> +{ + + /** + * The method to be invoked to stage resources. + */ + private final Method stageMethod; + + /** + * Creates a new {@link StageableMethod} reference. + * + * @param stageMethod the method to be invoked to stage resources. + * @param injectee the target injectee has to stage the resources. + */ + StageableMethod( Method stageMethod, Object injectee ) + { + super( injectee ); + this.stageMethod = stageMethod; + } + + /** + * {@inheritDoc} + */ + @Override + public final void stage( StageHandler stageHandler ) + { + try + { + AccessController.doPrivileged( new PrivilegedAction<Void>() + { + + @Override + public Void run() + { + stageMethod.setAccessible( true ); + return null; + } + + } ); + stageMethod.invoke( object ); + } + catch ( InvocationTargetException e ) + { + stageHandler.onError( object, e.getCause() ); + return; + } + catch ( Throwable e ) + { + stageHandler.onError( object, e ); + return; + } + stageHandler.onSuccess( object ); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableTypeMapper.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableTypeMapper.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableTypeMapper.java new file mode 100644 index 0000000..e799373 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/StageableTypeMapper.java @@ -0,0 +1,39 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import com.google.inject.TypeLiteral; + +/** + * Container for mapping a {@link Stageable} to the parent type + * that created it. Useful in specialty Stage containers. + */ +public interface StageableTypeMapper +{ + + /** + * Register a new {@link Stageable} with the type that created it + * + * @param stageable stageable + * @param parentType the owning type being heard + */ + <I> void registerType( Stageable stageable, TypeLiteral<I> parentType ); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stager.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stager.java b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stager.java new file mode 100644 index 0000000..a6a63c0 --- /dev/null +++ b/server/container/guice/onami/src/main/java/org/apache/james/onami/lifecycle/Stager.java @@ -0,0 +1,64 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.lang.annotation.Annotation; + +/** + * A Stager is a mini-container that stages resources + * invoking {@link Stageable#stage(StageHandler)}. + * <p/> + * Order of disposal is specified by the concrete implementation of this + * interface via {@link org.apache.onami.lifecycle.core.DefaultStager.Order}. + * <p/> + * Implementations must be thread-safe because registration can be done from + * any thread. + */ +public interface Stager<A extends Annotation> +{ + + /** + * Register a {@link Stageable} to stage resources. + * + * @param stageable object to be invoked to stage resources. + */ + void register( Stageable stageable ); + + /** + * Stages resources invoking {@link Stageable#stage(StageHandler)}. + */ + void stage(); + + /** + * Stages resources invoking {@link Stageable#stage(StageHandler)}. + * + * @param stageHandler the {@link StageHandler} instance that tracks progresses. + * @since 0.2.0 + */ + void stage( StageHandler stageHandler ); + + /** + * Returns the annotation that represents this stage. + * + * @return stage annotation. + */ + Class<A> getStage(); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/DefaultStagerTestCase.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/DefaultStagerTestCase.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/DefaultStagerTestCase.java new file mode 100644 index 0000000..03c9ee6 --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/DefaultStagerTestCase.java @@ -0,0 +1,105 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Assert; +import org.junit.Test; + +public class DefaultStagerTestCase +{ + + @Test + public void stagerShouldStageObjectsRegisteredWhileStaging() + { + final Stager<TestAnnotationA> stager = + new DefaultStager<TestAnnotationA>( TestAnnotationA.class ); + final AtomicBoolean staged = new AtomicBoolean(); + stager.register( new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + stager.register( new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + staged.set( true ); + } + } ); + } + } ); + + stager.stage(); + + Assert.assertTrue( staged.get() ); + } + + /* + * Deadlock scenario: + * 1. DefaultStager holds lock while calling Stageable.stage(); + * 2. Stageable.stage() blocks on some thread + * 3. the thread blocks on the lock in DefaultStager.register() + */ + @Test + public void stagerShouldNotDeadlockWhileStagingObjectChains() + { + final AtomicBoolean staged = new AtomicBoolean(); + final Stager<TestAnnotationA> stager = + new DefaultStager<TestAnnotationA>( TestAnnotationA.class ); + stager.register( new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + Thread thread = new Thread( new Runnable() + { + @Override + public void run() + { + stager.register( new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + staged.set( true ); + } + } ); + } + } ); + thread.start(); + try + { + thread.join(); + } + catch ( InterruptedException e ) + { + Thread.currentThread().interrupt(); + } + } + } ); + + stager.stage(); + + Assert.assertTrue( staged.get() ); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleObject.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleObject.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleObject.java new file mode 100644 index 0000000..615ad3b --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleObject.java @@ -0,0 +1,76 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import javax.inject.Singleton; + +@Singleton +public class MultiLifeCycleObject +{ + private final StringBuilder str = new StringBuilder(); + + @TestAnnotationC + public void foo() + { + str.append( "c" ); + } + + @TestAnnotationA + public void aaa() + { + str.append( "a" ); + } + + @TestAnnotationB + public void bbb() + { + str.append( "b" ); + } + + @TestAnnotationA + public void mmm() + { + str.append( "a" ); + } + + @TestAnnotationB + public void nnn() + { + str.append( "b" ); + } + + @TestAnnotationB + public void qqq() + { + str.append( "b" ); + } + + @TestAnnotationA + public void zzz() + { + str.append( "a" ); + } + + @Override + public String toString() + { + return str.toString(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleTestCase.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleTestCase.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleTestCase.java new file mode 100644 index 0000000..0160ac6 --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/MultiLifeCycleTestCase.java @@ -0,0 +1,144 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import static com.google.inject.matcher.Matchers.any; +import static java.util.Arrays.asList; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import javax.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Module; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.annotation.Annotation; +import java.util.List; + +public class MultiLifeCycleTestCase +{ + @Test + public void testOrdering() + { + Module lifeCycleModule = new TestLifeCycleModule( + asList( TestAnnotationA.class, TestAnnotationB.class, TestAnnotationC.class ) ); + MultiLifeCycleObject obj = Guice.createInjector( lifeCycleModule ).getInstance( MultiLifeCycleObject.class ); + Assert.assertEquals( "aaabbbc", obj.toString() ); + } + + public static class Foo + { + @Inject + public Foo( Stager<TestAnnotationA> stager ) + { + System.out.println( stager.getStage() ); + } + } + + @Test + public void testStaging() + { + Module moduleA = + new TestLifeCycleStageModule( new DefaultStager<TestAnnotationA>( TestAnnotationA.class ) ); + Module moduleB = + new TestLifeCycleStageModule( new DefaultStager<TestAnnotationB>( TestAnnotationB.class ) ); + Module moduleC = + new TestLifeCycleStageModule( new DefaultStager<TestAnnotationC>( TestAnnotationC.class ) ); + + Injector injector = Guice.createInjector( moduleA, moduleB, moduleC ); + MultiLifeCycleObject obj = injector.getInstance( MultiLifeCycleObject.class ); + + Assert.assertEquals( obj.toString(), "" ); + + injector.getInstance( LifeCycleStageModule.key( TestAnnotationA.class ) ).stage(); + Assert.assertEquals( "aaa", obj.toString() ); + injector.getInstance( LifeCycleStageModule.key( TestAnnotationB.class ) ).stage(); + Assert.assertEquals( "aaabbb", obj.toString() ); + injector.getInstance( LifeCycleStageModule.key( TestAnnotationC.class ) ).stage(); + Assert.assertEquals( "aaabbbc", obj.toString() ); + + injector.getInstance( Foo.class ); + } + + @Test + public void testStagingOrdering() + { + Module moduleA = + new TestLifeCycleStageModule( new DefaultStager<TestAnnotationA>( TestAnnotationA.class, DefaultStager.Order.FIRST_IN_FIRST_OUT ) ); + Module moduleB = + new TestLifeCycleStageModule( new DefaultStager<TestAnnotationB>( TestAnnotationB.class, DefaultStager.Order.FIRST_IN_LAST_OUT ) ); + + final StringBuilder str = new StringBuilder(); + Module m = new AbstractModule() + { + @Override + protected void configure() + { + binder().bind( StringBuilder.class ).toInstance( str ); + } + }; + + Injector injector = Guice.createInjector( moduleA, moduleB, m ); + injector.getInstance( StageObject1.class ); + injector.getInstance( StageObject2.class ); + + injector.getInstance( LifeCycleStageModule.key( TestAnnotationA.class ) ).stage(); + Assert.assertEquals( "1a2a", str.toString() ); + str.setLength( 0 ); + + injector.getInstance( LifeCycleStageModule.key( TestAnnotationB.class ) ).stage(); + Assert.assertEquals( "2b1b", str.toString() ); + } + + private static class TestLifeCycleModule extends LifeCycleModule + { + + private final List<? extends Class<? extends Annotation>> annotations; + + public TestLifeCycleModule( List<? extends Class<? extends Annotation>> annotations ) + { + this.annotations = annotations; + } + + @Override + protected void configure() + { + bindLifeCycle( annotations, any() ); + } + } + + private static class TestLifeCycleStageModule extends LifeCycleStageModule + { + + private final Stager<?> stager; + + public TestLifeCycleStageModule( Stager<?> stager ) + { + this.stager = stager; + } + + @Override + protected void configureBindings() + { + bindStager( stager ); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject1.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject1.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject1.java new file mode 100644 index 0000000..e5a899b --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject1.java @@ -0,0 +1,45 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import javax.inject.Inject; + +public class StageObject1 +{ + private final StringBuilder str; + + @Inject + public StageObject1( StringBuilder str ) + { + this.str = str; + } + + @TestAnnotationA + public void stageA() + { + str.append( "1a" ); + } + + @TestAnnotationB + public void stageB() + { + str.append( "1b" ); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject2.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject2.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject2.java new file mode 100644 index 0000000..1929d7c --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StageObject2.java @@ -0,0 +1,45 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import javax.inject.Inject; + +public class StageObject2 +{ + private final StringBuilder str; + + @Inject + public StageObject2( StringBuilder str ) + { + this.str = str; + } + + @TestAnnotationA + public void stageA() + { + str.append( "2a" ); + } + + @TestAnnotationB + public void stageB() + { + str.append( "2b" ); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StagingOrderTestCase.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StagingOrderTestCase.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StagingOrderTestCase.java new file mode 100644 index 0000000..287367c --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/StagingOrderTestCase.java @@ -0,0 +1,85 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StagingOrderTestCase +{ + @Test + public void testFifo() + { + List<Integer> order = new ArrayList<Integer>(); + DefaultStager<TestAnnotationA> stager = makeStager( order, DefaultStager.Order.FIRST_IN_FIRST_OUT ); + stager.stage(); + + Assert.assertEquals( Arrays.asList( 1, 2, 3 ), order ); + } + + @Test + public void testFilo() + { + List<Integer> order = new ArrayList<Integer>(); + DefaultStager<TestAnnotationA> stager = makeStager( order, DefaultStager.Order.FIRST_IN_LAST_OUT ); + stager.stage(); + + Assert.assertEquals( Arrays.asList( 3, 2, 1 ), order ); + } + + private DefaultStager<TestAnnotationA> makeStager( final List<Integer> order, DefaultStager.Order stagingOrder ) + { + Stageable stageable1 = new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + order.add( 1 ); + } + }; + Stageable stageable2 = new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + order.add( 2 ); + } + }; + Stageable stageable3 = new Stageable() + { + @Override + public void stage( StageHandler stageHandler ) + { + order.add( 3 ); + } + }; + + DefaultStager<TestAnnotationA> stager = + new DefaultStager<TestAnnotationA>( TestAnnotationA.class, stagingOrder ); + stager.register( stageable1 ); + stager.register( stageable2 ); + stager.register( stageable3 ); + return stager; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationA.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationA.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationA.java new file mode 100644 index 0000000..1e17c5e --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationA.java @@ -0,0 +1,33 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target(METHOD) +public @interface TestAnnotationA +{ + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/c1f94a46/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationB.java ---------------------------------------------------------------------- diff --git a/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationB.java b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationB.java new file mode 100644 index 0000000..ec26c62 --- /dev/null +++ b/server/container/guice/onami/src/test/java/org/apache/james/onami/lifecycle/TestAnnotationB.java @@ -0,0 +1,33 @@ +/**************************************************************** + * 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.james.onami.lifecycle; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention( RUNTIME ) +@Target( METHOD ) +public @interface TestAnnotationB +{ + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
