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