View Javadoc
1   package org.apache.onami.lifecycle.core;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import com.google.inject.Key;
23  import com.google.inject.TypeLiteral;
24  import com.google.inject.matcher.Matcher;
25  import com.google.inject.spi.InjectionListener;
26  import com.google.inject.spi.TypeEncounter;
27  import com.google.inject.util.Types;
28  
29  import java.lang.annotation.Annotation;
30  import java.lang.reflect.Method;
31  import java.lang.reflect.ParameterizedType;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import static com.google.inject.matcher.Matchers.any;
36  import static java.util.Arrays.asList;
37  
38  /**
39   * Guice module to register methods to be invoked when {@link Stager#stage()} is invoked.
40   * <p/>
41   * Module instance have has so it must not be used to construct more than one {@link com.google.inject.Injector}.
42   */
43  public abstract class LifeCycleStageModule
44      extends LifeCycleModule
45  {
46  
47      private List<BindingBuilder<?>> bindings;
48  
49      /**
50       * Convenience to generate the correct key for retrieving stagers from an injector.
51       * E.g.
52       * <p/>
53       * <code><pre>
54       * Stager&lt;MyAnnotation&gt; stager = injector.getInstance( LifeCycleStageModule.key( MyAnnotation.class ) );
55       * </pre></code>
56       *
57       * @param stage the annotation that represents this stage and the methods with this annotation
58       * @param <A>   the Annotation type
59       * @return the Guice key to use for accessing the stager for the input stage
60       */
61      public static <A extends Annotation> Key<Stager<A>> key( Class<A> stage )
62      {
63          return Key.get( type( stage ) );
64      }
65  
66      private static <A extends Annotation> TypeLiteral<Stager<A>> type( Class<A> stage )
67      {
68          ParameterizedType parameterizedType = Types.newParameterizedTypeWithOwner( null, Stager.class, stage );
69          //noinspection unchecked
70          @SuppressWarnings( "unchecked" ) // TODO
71          TypeLiteral<Stager<A>> stagerType = (TypeLiteral<Stager<A>>) TypeLiteral.get( parameterizedType );
72          return stagerType;
73      }
74  
75      /**
76       * {@inheritDoc}
77       */
78      @Override
79      protected final void configure()
80      {
81          if ( bindings != null )
82          {
83              throw new IllegalStateException( "Re-entry is not allowed" );
84          }
85          bindings = new ArrayList<BindingBuilder<?>>();
86          try
87          {
88              configureBindings();
89              for ( BindingBuilder<?> binding : bindings )
90              {
91                  bind( binding );
92              }
93          }
94          finally
95          {
96              bindings = null;
97          }
98      }
99  
100     private <A extends Annotation> void bind( BindingBuilder<A> binding )
101     {
102         final Stager<A> stager = binding.stager;
103         final StageableTypeMapper typeMapper = binding.typeMapper;
104         bind( type( stager.getStage() ) ).toInstance( stager );
105 
106         bindListener( binding.typeMatcher, new AbstractMethodTypeListener( asList( stager.getStage() ) )
107         {
108 
109             @Override
110             protected <I> void hear( final Method stageMethod, final TypeLiteral<I> parentType,
111                                      final TypeEncounter<I> encounter,
112                                      final Class<? extends Annotation> annotationType )
113             {
114                 encounter.register( new InjectionListener<I>()
115                 {
116 
117                     @Override
118                     public void afterInjection( I injectee )
119                     {
120                         Stageable stageable = new StageableMethod( stageMethod, injectee );
121                         stager.register( stageable );
122                         typeMapper.registerType( stageable, parentType );
123                     }
124 
125                 } );
126             }
127 
128         } );
129     }
130 
131     protected abstract void configureBindings();
132 
133     protected final <A extends Annotation> MapperBinding bindStager( Stager<A> stager )
134     {
135         BindingBuilder<A> builder = new BindingBuilder<A>( checkNotNull( stager, "Argument 'stager' must be not null" ) );
136         bindings.add( builder );
137         return builder;
138     }
139 
140     protected interface MatcherBinding
141     {
142         /**
143          * Sets the filter for injectee types.
144          *
145          * @param typeMatcher the filter for injectee types.
146          */
147         void matching( Matcher<? super TypeLiteral<?>> typeMatcher );
148     }
149 
150     protected interface MapperBinding extends MatcherBinding
151     {
152         /**
153          * Sets the container to register mappings from {@link Stageable}s to the types that created them.
154          *
155          * @param typeMapper container to map {@link Stageable}s to types.
156          */
157         MatcherBinding mappingWith( StageableTypeMapper typeMapper );
158     }
159 
160     /**
161      * Builder pattern helper.
162      */
163     private static class BindingBuilder<A extends Annotation> implements MapperBinding
164     {
165 
166         private Matcher<? super TypeLiteral<?>> typeMatcher = any();
167 
168         private final Stager<A> stager;
169 
170         private StageableTypeMapper typeMapper = new NoOpStageableTypeMapper();
171 
172         public BindingBuilder( Stager<A> stager )
173         {
174             this.stager = stager;
175         }
176 
177         @Override
178         public MatcherBinding mappingWith( StageableTypeMapper typeMapper )
179         {
180             this.typeMapper = checkNotNull( typeMapper, "Argument 'typeMapper' must be not null." );
181             return this;
182         }
183 
184         @Override
185         public void matching( Matcher<? super TypeLiteral<?>> typeMatcher )
186         {
187             this.typeMatcher = checkNotNull( typeMatcher, "Argument 'typeMatcher' must be not null" );
188         }
189 
190     }
191 
192     private static <T> T checkNotNull( T object, String message )
193     {
194         if ( object == null )
195         {
196             throw new IllegalArgumentException( message );
197         }
198         return object;
199     }
200 }