1   package com.atlassian.maven.plugins.amps;
2   
3   import com.atlassian.maven.plugins.amps.product.ProductHandler;
4   import com.atlassian.maven.plugins.amps.product.ProductHandlerFactory;
5   import com.atlassian.maven.plugins.amps.util.ArtifactRetriever;
6   import com.atlassian.maven.plugins.amps.util.ProjectUtils;
7   import org.apache.maven.artifact.factory.ArtifactFactory;
8   import org.apache.maven.artifact.repository.ArtifactRepository;
9   import org.apache.maven.artifact.resolver.ArtifactResolver;
10  import org.apache.maven.model.Resource;
11  import org.apache.maven.plugin.MojoExecutionException;
12  import org.apache.maven.plugin.MojoFailureException;
13  import org.apache.maven.project.MavenProject;
14  import org.jfrog.maven.annomojo.annotations.MojoComponent;
15  import org.jfrog.maven.annomojo.annotations.MojoParameter;
16  
17  import java.io.File;
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Properties;
23  
24  /**
25   * Base class for webapp mojos
26   */
27  public abstract class AbstractProductHandlerMojo extends AbstractProductHandlerAwareMojo {
28      // ------ start inline product context
29  
30      private static final String DEFAULT_CONTAINER = "tomcat6x";
31      private static final String DEFAULT_SERVER = "localhost";
32      private static final String DEFAULT_PRODUCT_DATA_VERSION = "LATEST";
33      private static final String DEFAULT_PDK_VERSION = "0.4";
34      private static final String DEFAULT_WEB_CONSOLE_VERSION = "1.2.8";
35  
36      /**
37       * Container to run in
38       */
39      @MojoParameter(expression = "${container}", defaultValue = DEFAULT_CONTAINER)
40      protected String containerId;
41  
42      /**
43       * HTTP port for the servlet containers
44       */
45      @MojoParameter(expression = "${http.port}", defaultValue = "0")
46      private int httpPort;
47  
48      /**
49       * Application context path
50       */
51      @MojoParameter(expression = "${context.path}")
52      protected String contextPath;
53  
54      /**
55       * Application server
56       */
57      @MojoParameter(expression = "${server}", defaultValue = DEFAULT_SERVER)
58      protected String server;
59  
60      /**
61       * Webapp version
62       */
63      @MojoParameter(expression = "${product.version}")
64      private String productVersion;
65  
66      /**
67       * JVM arguments to pass to cargo
68       */
69      @MojoParameter(expression = "${jvmargs}")
70      protected String jvmArgs;
71  
72  
73      /**
74       * System systemProperties to pass to cargo
75       *
76       * @deprecated Since 3.2, use systemPropertyVariables instead
77       */
78      @MojoParameter
79      @Deprecated
80      protected Properties systemProperties = new Properties();
81  
82      /**
83       * System Properties to pass to cargo using a more familiar syntax.
84       *
85       * @since 3.2
86       */
87      @MojoParameter
88      protected Map<String, Object> systemPropertyVariables = new HashMap<String, Object>();
89  
90  
91      /**
92       * A log4j systemProperties file
93       */
94      @MojoParameter
95      protected File log4jProperties;
96  
97      /**
98       * The test resources version
99       * @deprecated Since 3.0-beta2
100      */
101     @MojoParameter(expression = "${test.resources.version}")
102     private String testResourcesVersion;
103 
104     /**
105      * The test resources version
106      */
107     @MojoParameter(expression = "${product.data.version}", defaultValue = DEFAULT_PRODUCT_DATA_VERSION)
108     private String productDataVersion;
109 
110     /**
111      * The path to a custom test resources zip
112      */
113     @MojoParameter(expression = "${product.data.path}")
114     private String productDataPath;
115 
116     /**
117      */
118     @MojoParameter
119     private List<ProductArtifact> pluginArtifacts = new ArrayList<ProductArtifact>();
120 
121     /**
122      */
123     @MojoParameter
124     private List<ProductArtifact> libArtifacts = new ArrayList<ProductArtifact>();
125 
126     /**
127      */
128     @MojoParameter
129     private List<ProductArtifact> bundledArtifacts = new ArrayList<ProductArtifact>();
130 
131     /**
132      * SAL version
133      * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
134      */
135     @MojoParameter
136     private String salVersion;
137 
138     /**
139      * Atlassian Plugin Development Kit (PDK) version
140      * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
141      */
142     @MojoParameter(defaultValue = DEFAULT_PDK_VERSION)
143     private String pdkVersion;
144 
145     /**
146      * Atlassian REST module version
147      * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
148      */
149     @MojoParameter
150     private String restVersion;
151 
152 
153     /**
154      * Felix OSGi web console version
155      * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
156      */
157     @MojoParameter(defaultValue =  DEFAULT_WEB_CONSOLE_VERSION)
158     private String webConsoleVersion;
159 
160     // ---------------- end product context
161 
162     /**
163      * Comma-delimited list of plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
164      * ommitted, defaulting to LATEST
165      */
166     @MojoParameter(expression = "${plugins}")
167     private String pluginArtifactsString;
168 
169     /**
170      * Comma-delimited list of lib artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
171      * ommitted, defaulting to LATEST
172      */
173     @MojoParameter(expression = "${lib.plugins}")
174     private String libArtifactsString;
175 
176     /**
177      * Comma-delimited list of bundled plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
178      * ommitted, defaulting to LATEST
179      */
180     @MojoParameter(expression = "${bundled.plugins}")
181     private String bundledArtifactsString;
182 
183     /**
184      * The build directory
185      */
186     @MojoParameter(expression = "${project.build.directory}", required = true)
187     protected File targetDirectory;
188 
189     /**
190      * The jar name
191      */
192     @MojoParameter(expression = "${project.build.finalName}", required = true)
193     protected String finalName;
194 
195     /**
196      * If the plugin and optionally its test plugin should be installed
197      */
198     @MojoParameter (expression = "${install.plugin}", defaultValue = "true")
199     protected boolean installPlugin;
200 
201     /**
202      * The artifact resolver is used to dynamically resolve JARs that have to be in the embedded
203      * container's classpaths. Another solution would have been to statitically define them a
204      * dependencies in the plugin's POM. Resolving them in a dynamic manner is much better as only
205      * the required JARs for the defined embedded container are downloaded.
206      */
207     @MojoComponent
208     private ArtifactResolver artifactResolver;
209 
210     /**
211      * The local Maven repository. This is used by the artifact resolver to download resolved
212      * JARs and put them in the local repository so that they won't have to be fetched again next
213      * time the plugin is executed.
214      */
215     @MojoParameter(expression = "${localRepository}")
216     private ArtifactRepository localRepository;
217 
218 
219     /**
220      * The remote Maven repositories used by the artifact resolver to look for JARs.
221      */
222     @MojoParameter(expression = "${project.remoteArtifactRepositories}")
223     private List repositories;
224 
225     /**
226      * The artifact factory is used to create valid Maven
227      * {@link org.apache.maven.artifact.Artifact} objects. This is used to pass Maven artifacts to
228      * the artifact resolver so that it can download the required JARs to put in the embedded
229      * container's classpaths.
230      */
231     @MojoComponent
232     private ArtifactFactory artifactFactory;
233 
234     /**
235      * A list of product-specific configurations
236      */
237     @MojoParameter
238     protected List<Product> products = new ArrayList<Product>();
239 
240     /**
241      * File the container logging output will be sent to.
242      */
243     @MojoParameter
244     private String output;
245 
246 
247     private Product createDefaultProductContext() throws MojoExecutionException
248     {
249         Product ctx = new Product();
250         ctx.setId(getProductId());
251         ctx.setContainerId(containerId);
252         ctx.setServer(server);
253         ctx.setContextPath(contextPath);
254         ctx.setJvmArgs(jvmArgs);
255 
256         setDefaultSystemProperty(systemPropertyVariables, "atlassian.dev.mode", "true");
257         setDefaultSystemProperty(systemPropertyVariables, "java.awt.headless", "true");
258         setDefaultSystemProperty(systemPropertyVariables, "plugin.resource.directories", buildResourcesList());
259 
260         ctx.setSystemPropertyVariables(systemPropertyVariables);
261         ctx.setBundledArtifacts(bundledArtifacts);
262         ctx.setLibArtifacts(libArtifacts);
263         ctx.setPluginArtifacts(pluginArtifacts);
264         ctx.setLog4jProperties(log4jProperties);
265         ctx.setHttpPort(httpPort);
266 
267         ctx.setVersion(productVersion);
268         ctx.setDataVersion(productDataVersion);
269         ctx.setDataPath(productDataPath);
270 
271         // continue to have these work for now
272         ctx.setRestVersion(restVersion);
273         ctx.setSalVersion(salVersion);
274         ctx.setPdkVersion(pdkVersion);
275         ctx.setWebConsoleVersion(webConsoleVersion);
276 
277         ctx.setHttpPort(httpPort);
278         return ctx;
279     }
280 
281     /**
282      * @return a comma-separated list of resource directories.  If a test plugin is detected, the 
283      * test resources directories are included as well.
284      */
285     private String buildResourcesList()
286     {
287         // collect all resource directories and make them available for
288         // on-the-fly reloading
289         StringBuilder resourceProp = new StringBuilder();
290         MavenProject mavenProject = getMavenContext().getProject();
291         @SuppressWarnings("unchecked") List<Resource> resList = mavenProject.getResources();
292         for (int i = 0; i < resList.size(); i++) {
293             resourceProp.append(resList.get(i).getDirectory());
294             if (i + 1 != resList.size()) {
295                 resourceProp.append(",");
296             }
297         }
298 
299         if (ProjectUtils.shouldDeployTestJar(getMavenContext()))
300         {
301             @SuppressWarnings("unchecked") List<Resource> testResList = mavenProject.getTestResources();
302             for (int i = 0; i < testResList.size(); i++) {
303                 if (i == 0 && resourceProp.length() > 0)
304                 {
305                     resourceProp.append(",");
306                 }
307                 resourceProp.append(testResList.get(i).getDirectory());
308                 if (i + 1 != testResList.size()) {
309                     resourceProp.append(",");
310                 }
311             }
312         }
313         return resourceProp.toString();
314     }
315 
316     private static void setDefaultSystemProperty(final Map<String,Object> props, final String key, final String value)
317     {
318         if (!props.containsKey(key))
319         {
320             props.put(key, System.getProperty(key, value));
321         }
322     }
323 
324     private void postProcessProduct(Product product)
325     {
326 
327         String dversion = System.getProperty("product.data.version", product.getDataVersion());
328         String pversion = System.getProperty("product.version", product.getVersion());
329         String dpath = System.getProperty("product.data.path", product.getDataPath());
330 
331         product.setDataPath(dpath);
332         product.setDataVersion(dversion);
333         product.setVersion(pversion);
334         product.setArtifactRetriever(new ArtifactRetriever(artifactResolver, artifactFactory, localRepository, repositories));
335 
336         if (product.getContainerId() == null)
337         {
338             product.setContainerId(DEFAULT_CONTAINER);
339         }
340 
341         if (product.getServer() == null)
342         {
343             product.setServer(DEFAULT_SERVER);
344         }
345 
346         if (product.getDataVersion() == null)
347         {
348             product.setDataVersion(DEFAULT_PRODUCT_DATA_VERSION);
349         }
350 
351         if (product.getPdkVersion() == null)
352         {
353             product.setPdkVersion(DEFAULT_PDK_VERSION);
354         }
355 
356         if (product.getWebConsoleVersion() == null)
357         {
358             product.setWebConsoleVersion(DEFAULT_WEB_CONSOLE_VERSION);
359         }
360         
361         if (product.getOutput() == null)
362         {
363         	product.setOutput(output);
364         }
365 
366         product.setInstanceId(getProductInstanceId(product));
367     }
368 
369     private List<ProductArtifact> stringToArtifactList(String val, List<ProductArtifact> artifacts)
370     {
371         if (val == null || val.trim().length() == 0)
372         {
373             return artifacts;
374         }
375 
376         for (String ptn : val.split(","))
377         {
378             String[] items = ptn.split(":");
379             if (items.length < 2 || items.length > 3)
380             {
381                 throw new IllegalArgumentException("Invalid artifact pattern: " + ptn);
382             }
383             String groupId = items[0];
384             String artifactId = items[1];
385             String version = (items.length == 3 ? items[2] : "LATEST");
386             artifacts.add(new ProductArtifact(groupId, artifactId, version));
387         }
388         return artifacts;
389     }
390 
391     public final void execute() throws MojoExecutionException, MojoFailureException
392     {
393         stringToArtifactList(pluginArtifactsString, pluginArtifacts);
394         stringToArtifactList(libArtifactsString, libArtifacts);
395         stringToArtifactList(bundledArtifactsString, bundledArtifacts);
396         systemPropertyVariables.putAll((Map) systemProperties);
397 
398         detectDeprecatedVersionOverrides();
399 
400         doExecute();
401     }
402 
403     private void detectDeprecatedVersionOverrides()
404     {
405         Properties props = getMavenContext().getProject().getProperties();
406         for (String deprecatedProperty : new String[] {"sal.version", "rest.version", "web.console.version", "pdk.version"})
407         {
408             if (props.containsKey(deprecatedProperty))
409             {
410                 getLog().warn("The property '" + deprecatedProperty + "' is no longer usable to override the related bundled plugin." +
411                         "  Use <pluginArtifacts> or <libArtifacts> to explicitly override bundled plugins and libraries, respectively.");
412             }
413         }
414     }
415 
416     protected Map<String, Product> getProductContexts(MavenGoals goals) throws MojoExecutionException
417     {
418         Map<String, Product> productMap = new HashMap<String, Product>();
419 
420 
421         makeProductsInheritDefaultConfiguration(products, productMap);
422 
423         for (Product ctx : productMap.values())
424         {
425             postProcessProduct(ctx);
426             ProductHandler handler = ProductHandlerFactory.create(ctx.getId(), getMavenContext().getProject(), goals, getLog());
427             ctx.setHttpPort(ctx.getHttpPort() == 0 ? handler.getDefaultHttpPort() : ctx.getHttpPort());
428             ctx.setVersion(ctx.getVersion() == null ? "RELEASE" : ctx.getVersion());
429             ctx.setContextPath(ctx.getContextPath() == null ? "/" + handler.getId() : ctx.getContextPath());
430         }
431         return productMap;
432     }
433 
434     void makeProductsInheritDefaultConfiguration(List<Product> products, Map<String, Product> productMap) throws MojoExecutionException
435     {
436         productMap.put(getProductId(), createDefaultProductContext());
437         if (!products.isEmpty())
438         {
439             Product defaultProduct = createDefaultProductContext();
440             for (Product product : products)
441             {
442                 Product processedProduct = product.merge(defaultProduct);
443                 String id = getProductInstanceId(processedProduct);
444                 productMap.put(id, processedProduct);
445             }
446         }
447     }
448 
449     private String getProductInstanceId(Product processedProduct)
450     {
451         return processedProduct.getInstanceId() == null ? processedProduct.getId() : processedProduct.getInstanceId();
452     }
453 
454 
455     protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;
456 }