1 package com.atlassian.maven.plugins.amps;
2
3 import com.atlassian.maven.plugins.amps.product.ProductHandler;
4 import com.atlassian.maven.plugins.amps.util.GoogleAmpsTracker;
5 import com.google.common.base.Predicate;
6 import com.google.common.collect.Iterables;
7 import com.google.common.collect.Lists;
8 import org.apache.commons.io.IOUtils;
9 import org.apache.maven.plugin.MojoExecutionException;
10 import org.apache.maven.plugin.MojoFailureException;
11 import org.apache.maven.surefire.shade.org.apache.commons.lang.StringUtils;
12 import org.apache.maven.artifact.Artifact;
13 import org.jfrog.maven.annomojo.annotations.MojoExecute;
14 import org.jfrog.maven.annomojo.annotations.MojoGoal;
15 import org.jfrog.maven.annomojo.annotations.MojoParameter;
16 import org.jfrog.maven.annomojo.annotations.MojoRequiresDependencyResolution;
17
18 import java.io.File;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.NoSuchElementException;
27 import java.util.Properties;
28 import java.util.concurrent.TimeUnit;
29
30 import static org.apache.commons.lang.StringUtils.isBlank;
31
32
33
34
35 @MojoGoal("run")
36 @MojoExecute(phase = "package")
37 @MojoRequiresDependencyResolution
38 public class RunMojo extends AbstractTestGroupsHandlerMojo
39 {
40 @MojoParameter(expression = "${wait}", defaultValue = "true")
41 private boolean wait;
42
43
44
45
46 @MojoParameter(expression = "${amps.properties}", required = true, defaultValue = "false")
47 protected boolean writePropertiesToFile;
48
49
50
51
52 @MojoParameter(expression = "${testGroup}")
53 protected String testGroup;
54
55
56
57
58
59
60
61
62
63
64
65 @MojoParameter(expression = "${excludeInstances}")
66 protected String excludeInstances;
67
68
69
70
71 protected final Map<String, String> properties = new HashMap<String, String>();
72
73 protected void doExecute() throws MojoExecutionException, MojoFailureException
74 {
75 getGoogleTracker().track(GoogleAmpsTracker.RUN);
76
77 final List<ProductExecution> productExecutions = getProductExecutions();
78
79 startProducts(productExecutions);
80 }
81
82 protected void startProducts(List<ProductExecution> productExecutions) throws MojoExecutionException
83 {
84 long globalStartTime = System.nanoTime();
85 setParallelMode(productExecutions);
86 List<StartupInformation> successMessages = Lists.newArrayList();
87 for (ProductExecution productExecution : productExecutions)
88 {
89 final ProductHandler productHandler = productExecution.getProductHandler();
90 final Product product = productExecution.getProduct();
91 if (product.isInstallPlugin() == null)
92 {
93 product.setInstallPlugin(shouldInstallPlugin());
94 }
95
96
97 getLog().info("");
98 if (StringUtils.isNotBlank(product.getOutput()))
99 {
100 getLog().info(String.format("Starting %s... (see log at %s)", product.getInstanceId(), product.getOutput()));
101 }
102 else
103 {
104 getLog().info(String.format("Starting %s...", product.getInstanceId()));
105 }
106
107
108 long startTime = System.nanoTime();
109 int actualHttpPort = productHandler.start(product);
110 long durationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
111
112
113 StartupInformation message = new StartupInformation(product, "started successfully", actualHttpPort, durationSeconds);
114 if (!parallel)
115 {
116 getLog().info(message.toString());
117 }
118 successMessages.add(message);
119
120 if (writePropertiesToFile)
121 {
122 if (productExecutions.size() == 1)
123 {
124 properties.put("http.port", String.valueOf(actualHttpPort));
125 properties.put("context.path", product.getContextPath());
126 }
127
128 properties.put("http." + product.getInstanceId() + ".port", String.valueOf(actualHttpPort));
129 properties.put("context." + product.getInstanceId() + ".path", product.getContextPath());
130 String baseUrl = MavenGoals.getBaseUrl(product, actualHttpPort);
131 properties.put("baseurl." + product.getInstanceId(), baseUrl);
132 }
133 }
134
135 if (writePropertiesToFile)
136 {
137 writePropertiesFile();
138 }
139
140 if (parallel)
141 {
142 waitForProducts(productExecutions, true);
143 }
144 long globalDurationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - globalStartTime);
145
146
147 if (successMessages.size() > 1 || parallel)
148 {
149 getLog().info("");
150 getLog().info("=== Summary (total time " + globalDurationSeconds + "s):");
151
152 for (StartupInformation message : successMessages)
153 {
154 if (StringUtils.isNotBlank(message.getOutput()))
155 {
156 getLog().info("Log available at: " + message.getOutput());
157 }
158 }
159
160 for (StartupInformation message : successMessages)
161 {
162 getLog().info(message.toString());
163 }
164 }
165
166 if (wait)
167 {
168 getLog().info("Type Ctrl-D to shutdown gracefully");
169 getLog().info("Type Ctrl-C to exit");
170 try
171 {
172 while (System.in.read() != -1)
173 {
174 }
175 }
176 catch (final IOException e)
177 {
178
179 }
180
181
182
183
184 stopProducts(productExecutions);
185 }
186 }
187
188 protected List<ProductExecution> getProductExecutions() throws MojoExecutionException
189 {
190 final List<ProductExecution> productExecutions;
191 final MavenGoals goals = getMavenGoals();
192 if (!isBlank(testGroup))
193 {
194 productExecutions = getTestGroupProductExecutions(testGroup);
195 }
196 else if (!isBlank(instanceId))
197 {
198 Product ctx = getProductContexts().get(instanceId);
199 if (ctx == null)
200 {
201 throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
202 }
203 ProductHandler product = createProductHandler(ctx.getId());
204 productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
205 }
206 else
207 {
208 Product ctx = getProductContexts().get(getProductId());
209 ProductHandler product = createProductHandler(ctx.getId());
210 productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
211 }
212 return filterExcludedInstances(includeStudioDependentProducts(productExecutions, goals));
213 }
214
215 private List<ProductExecution> filterExcludedInstances(List<ProductExecution> executions) throws MojoExecutionException
216 {
217 if (StringUtils.isBlank(excludeInstances))
218 {
219 return executions;
220 }
221 boolean inverted = excludeInstances.startsWith("*/");
222 String instanceIdList = inverted ? excludeInstances.substring(2) : excludeInstances;
223
224
225 List<String> excludedInstanceIds = Lists.newArrayList(instanceIdList.split(","));
226 List<ProductExecution> excludedExecutions = Lists.newArrayList();
227 for (final String instanceId : excludedInstanceIds)
228 {
229 try
230 {
231 excludedExecutions.add(Iterables.find(executions, new Predicate<ProductExecution>()
232 {
233 @Override
234 public boolean apply(ProductExecution input)
235 {
236 return input.getProduct().getInstanceId().equals(instanceId);
237 }
238 }));
239 }
240 catch (NoSuchElementException nsee)
241 {
242 throw new MojoExecutionException("You specified -Dexclude=" + excludeInstances + " but " + instanceId + " is not an existing instance id.");
243 }
244 }
245
246 if (inverted)
247 {
248 return excludedExecutions;
249 }
250 else
251 {
252 executions.removeAll(excludedExecutions);
253 return executions;
254 }
255 }
256
257
258
259
260
261 private boolean shouldInstallPlugin()
262 {
263 Artifact artifact = getMavenContext().getProject().getArtifact();
264 return installPlugin &&
265 (artifact != null && !"pom".equalsIgnoreCase(artifact.getType()));
266 }
267
268 private void writePropertiesFile() throws MojoExecutionException
269 {
270 final Properties props = new Properties();
271
272 for (Map.Entry<String, String> entry : properties.entrySet())
273 {
274 props.setProperty(entry.getKey(), entry.getValue());
275 }
276
277 final File ampsProperties = new File(getMavenContext().getProject().getBuild().getDirectory(), "amps.properties");
278 OutputStream out = null;
279 try
280 {
281 out = new FileOutputStream(ampsProperties);
282 props.store(out, "");
283 }
284 catch (IOException e)
285 {
286 throw new MojoExecutionException("Error writing " + ampsProperties.getAbsolutePath(), e);
287 }
288 finally
289 {
290 IOUtils.closeQuietly(out);
291 }
292 }
293
294
295
296
297 private static class StartupInformation
298 {
299 int actualHttpPort;
300 long durationSeconds;
301 Product product;
302 String event;
303
304 public StartupInformation(Product product, String event, int actualHttpPort, long durationSeconds)
305 {
306 super();
307 this.actualHttpPort = actualHttpPort;
308 this.product = product;
309 this.event = event;
310 this.durationSeconds = durationSeconds;
311 }
312
313 @Override
314 public String toString()
315 {
316 String message = String.format("%s %s in %ds", product.getInstanceId(), event
317 + (Boolean.FALSE.equals(product.getSynchronousStartup()) ? " (asynchronously)" : ""), durationSeconds);
318 if (actualHttpPort != 0)
319 {
320 message += " at http://" + product.getServer() + ":" + actualHttpPort + product.getContextPath();
321 }
322 return message;
323 }
324
325
326
327
328 public String getOutput()
329 {
330 return product.getOutput();
331 }
332
333 }
334 }