View Javadoc
1   /**
2    * Copyright 2011-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.maven.plugins.guice;
17  
18  import static com.google.common.base.Optional.absent;
19  import static com.google.common.base.Preconditions.checkState;
20  import static com.google.common.collect.Lists.newArrayList;
21  import static com.google.inject.Guice.createInjector;
22  import static java.lang.Character.toUpperCase;
23  import static org.apache.commons.lang3.StringUtils.isNotBlank;
24  import static org.kuali.common.jute.base.Exceptions.illegalArgument;
25  import static org.kuali.common.jute.base.Optionals.fromTrimToNull;
26  
27  import java.lang.annotation.Annotation;
28  import java.util.List;
29  
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugins.annotations.Component;
32  import org.apache.maven.plugins.annotations.Execute;
33  import org.apache.maven.plugins.annotations.Mojo;
34  import org.apache.maven.plugins.annotations.Parameter;
35  import org.apache.maven.project.MavenProject;
36  import org.apache.maven.settings.Settings;
37  
38  import com.google.common.base.Optional;
39  import com.google.inject.AbstractModule;
40  import com.google.inject.Injector;
41  import com.google.inject.Key;
42  
43  /**
44   * <p>
45   * Create a Guice injector from the configured modules, get a {@code java.lang.Runnable} from the injector, and run it.
46   * </p>
47   *
48   * <p>
49   * If no explicit modules are configured, look for a default module using {@code groupId + artifactId} by converting {@code artifactId} to camel case and appending the word
50   * {@code Module}. For example, the default module that would be loaded using this plugin's GAV information is:
51   *
52   * <pre>
53   * org.kuali.maven.plugins.GuiceMavenPluginModule
54   * </pre>
55   *
56   * </p>
57   */
58  @Mojo(name = GuiceMojo.RUN, threadSafe = true)
59  @Execute(goal = GuiceMojo.RUN)
60  public class GuiceMojo extends AbstractMojo {
61  
62      static final String RUN = "run";
63      private static final String MODULE = "Module";
64  
65      @Component
66      MavenProject project;
67  
68      @Component
69      Settings settings;
70  
71      /**
72       * List of Guice modules to use when creating the injector. If supplied, these come before {@code guice.module}
73       */
74      @Parameter(property = "guice.modules")
75      List<String> modules = newArrayList();
76  
77      /**
78       * The Guice module to use when creating the injector. This comes after {@code guice.modules} (if any were supplied).
79       */
80      @Parameter(property = "guice.module")
81      String module;
82  
83      /**
84       * If supplied, the {@code java.lang.Runnable} must be injected using this annotation
85       */
86      @Parameter(property = "guice.annotation")
87      String annotation;
88  
89      @Override
90      public void execute() {
91          // TODO Gain a better understanding of Guice scopes and if a custom scope might be appropriate here
92          // TODO Strongly suspect what is being done here would be considered naive, and better accomplished via some kind
93          // TODO of "project scope", or "reactor scope" or something similar, possibly not even being done as a plugin
94          // TODO https://github.com/google/guice/wiki/CustomScopes
95          Optional<AbstractModule> module = getModule(this);
96          checkState(module.isPresent() || !getModules().isEmpty(), "no modules");
97          List<AbstractModule> modules = getModules(module, getModules(), new MavenModule(this));
98          Injector injector = createInjector(modules);
99          Runnable runnable = getRunnable(injector, fromTrimToNull(annotation));
100         runnable.run();
101     }
102 
103     protected Optional<AbstractModule> getModule(GuiceMojo mojo) {
104         if (isNotBlank(mojo.getModule())) {
105             // they've supplied a specific module, use it
106             AbstractModule module = getModule(mojo.getModule());
107             return Optional.of(module);
108         } else {
109             // otherwise see if we can find a default module
110             return getDefaultModule(mojo.getProject());
111         }
112     }
113 
114     private List<AbstractModule> getModules(Optional<AbstractModule> module, List<String> modules, MavenModule maven) {
115         List<AbstractModule> list = newArrayList();
116         list.add(maven);
117         for (String moduleName : modules) {
118             AbstractModule element = getModule(moduleName);
119             list.add(element);
120         }
121         if (module.isPresent()) {
122             list.add(module.get());
123         }
124         return list;
125     }
126 
127     private Runnable getRunnable(Injector injector, Optional<String> annotation) {
128         if (annotation.isPresent()) {
129             Class<? extends Annotation> type = newClass(annotation.get());
130             Key<Runnable> key = Key.get(Runnable.class, type);
131             return injector.getInstance(key);
132         } else {
133             return injector.getInstance(Runnable.class);
134         }
135     }
136 
137     private Optional<AbstractModule> getDefaultModule(MavenProject project) {
138         String className = getDefaultModuleClassName(project);
139         try {
140             AbstractModule module = getModule(className);
141             return Optional.of(module);
142         } catch (IllegalArgumentException e) {
143             // if we can't lookup a default module, just return absent
144             return absent();
145         }
146     }
147 
148     private String getDefaultModuleClassName(MavenProject project) {
149         String groupId = project.getGroupId();
150         String artifactId = project.getArtifactId();
151         return groupId + "." + capitalize(artifactId) + MODULE;
152     }
153 
154     protected String capitalize(String artifactId) {
155         char[] chars = artifactId.replace('_', '-').toCharArray();
156         StringBuilder sb = new StringBuilder();
157         char prevChar = 0;
158         for (char c : chars) {
159             if (c == '-') {
160                 // skip it, don't add it to the string
161             } else if (prevChar == 0 || prevChar == '-') {
162                 // if it's the first character OR the previous character was a dash
163                 // convert the current character to upper case and append it
164                 sb.append(toUpperCase(c));
165             } else {
166                 // otherwise just append the current character
167                 sb.append(c);
168             }
169             prevChar = c;
170         }
171         return sb.toString();
172     }
173 
174     protected AbstractModule getModule(String module) {
175         Class<? extends AbstractModule> type = newClass(module);
176         return newInstance(type);
177     }
178 
179     @SuppressWarnings("unchecked")
180     protected <T> T newClass(String type) {
181         try {
182             return (T) Class.forName(type);
183         } catch (Exception e) {
184             throw illegalArgument(e);
185         }
186     }
187 
188     protected <T> T newInstance(Class<T> type) {
189         try {
190             return type.newInstance();
191         } catch (Exception e) {
192             throw illegalArgument(e);
193         }
194     }
195 
196     public String getModule() {
197         return module;
198     }
199 
200     public void setModule(String module) {
201         this.module = module;
202     }
203 
204     public MavenProject getProject() {
205         return project;
206     }
207 
208     public Settings getSettings() {
209         return settings;
210     }
211 
212     public void setModules(List<String> modules) {
213         this.modules = modules;
214     }
215 
216     public List<String> getModules() {
217         return modules;
218     }
219 
220     public String getAnnotation() {
221         return annotation;
222     }
223 
224     public void setAnnotation(String annotation) {
225         this.annotation = annotation;
226     }
227 
228 }