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