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 }
131 }
132
133 if (writePropertiesToFile)
134 {
135 writePropertiesFile();
136 }
137
138 if (parallel)
139 {
140 waitForProducts(productExecutions, true);
141 }
142 long globalDurationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - globalStartTime);
143
144
145 if (successMessages.size() > 1 || parallel)
146 {
147 getLog().info("");
148 getLog().info("=== Summary (total time " + globalDurationSeconds + "s):");
149
150 for (StartupInformation message : successMessages)
151 {
152 if (StringUtils.isNotBlank(message.getOutput()))
153 {
154 getLog().info("Log available at: " + message.getOutput());
155 }
156 }
157
158 for (StartupInformation message : successMessages)
159 {
160 getLog().info(message.toString());
161 }
162 }
163
164 if (wait)
165 {
166 getLog().info("Type Ctrl-D to shutdown gracefully");
167 getLog().info("Type Ctrl-C to exit");
168 try
169 {
170 while (System.in.read() != -1)
171 {
172 }
173 }
174 catch (final IOException e)
175 {
176
177 }
178
179
180
181
182 stopProducts(productExecutions);
183 }
184 }
185
186 protected List<ProductExecution> getProductExecutions() throws MojoExecutionException
187 {
188 final List<ProductExecution> productExecutions;
189 final MavenGoals goals = getMavenGoals();
190 if (!isBlank(testGroup))
191 {
192 productExecutions = getTestGroupProductExecutions(testGroup);
193 }
194 else if (!isBlank(instanceId))
195 {
196 Product ctx = getProductContexts().get(instanceId);
197 if (ctx == null)
198 {
199 throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
200 }
201 ProductHandler product = createProductHandler(ctx.getId());
202 productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
203 }
204 else
205 {
206 Product ctx = getProductContexts().get(getProductId());
207 ProductHandler product = createProductHandler(ctx.getId());
208 productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
209 }
210 return filterExcludedInstances(includeStudioDependentProducts(productExecutions, goals));
211 }
212
213 private List<ProductExecution> filterExcludedInstances(List<ProductExecution> executions) throws MojoExecutionException
214 {
215 if (StringUtils.isBlank(excludeInstances))
216 {
217 return executions;
218 }
219 boolean inverted = excludeInstances.startsWith("*/");
220 String instanceIdList = inverted ? excludeInstances.substring(2) : excludeInstances;
221
222
223 List<String> excludedInstanceIds = Lists.newArrayList(instanceIdList.split(","));
224 List<ProductExecution> excludedExecutions = Lists.newArrayList();
225 for (final String instanceId : excludedInstanceIds)
226 {
227 try
228 {
229 excludedExecutions.add(Iterables.find(executions, new Predicate<ProductExecution>()
230 {
231 @Override
232 public boolean apply(ProductExecution input)
233 {
234 return input.getProduct().getInstanceId().equals(instanceId);
235 }
236 }));
237 }
238 catch (NoSuchElementException nsee)
239 {
240 throw new MojoExecutionException("You specified -Dexclude=" + excludeInstances + " but " + instanceId + " is not an existing instance id.");
241 }
242 }
243
244 if (inverted)
245 {
246 return excludedExecutions;
247 }
248 else
249 {
250 executions.removeAll(excludedExecutions);
251 return executions;
252 }
253 }
254
255
256
257
258
259 private boolean shouldInstallPlugin()
260 {
261 Artifact artifact = getMavenContext().getProject().getArtifact();
262 return installPlugin &&
263 (artifact != null && !"pom".equalsIgnoreCase(artifact.getType()));
264 }
265
266 private void writePropertiesFile() throws MojoExecutionException
267 {
268 final Properties props = new Properties();
269
270 for (Map.Entry<String, String> entry : properties.entrySet())
271 {
272 props.setProperty(entry.getKey(), entry.getValue());
273 }
274
275 final File ampsProperties = new File(getMavenContext().getProject().getBuild().getDirectory(), "amps.properties");
276 OutputStream out = null;
277 try
278 {
279 out = new FileOutputStream(ampsProperties);
280 props.store(out, "");
281 }
282 catch (IOException e)
283 {
284 throw new MojoExecutionException("Error writing " + ampsProperties.getAbsolutePath(), e);
285 }
286 finally
287 {
288 IOUtils.closeQuietly(out);
289 }
290 }
291
292
293
294
295 private static class StartupInformation
296 {
297 int actualHttpPort;
298 long durationSeconds;
299 Product product;
300 String event;
301
302 public StartupInformation(Product product, String event, int actualHttpPort, long durationSeconds)
303 {
304 super();
305 this.actualHttpPort = actualHttpPort;
306 this.product = product;
307 this.event = event;
308 this.durationSeconds = durationSeconds;
309 }
310
311 @Override
312 public String toString()
313 {
314 String message = String.format("%s %s in %ds", product.getInstanceId(), event
315 + (Boolean.FALSE.equals(product.getSynchronousStartup()) ? " (asynchronously)" : ""), durationSeconds);
316 if (actualHttpPort != 0)
317 {
318 message += " at http://" + product.getServer() + ":" + actualHttpPort + product.getContextPath();
319 }
320 return message;
321 }
322
323
324
325
326 public String getOutput()
327 {
328 return product.getOutput();
329 }
330
331 }
332 }