/maven-amps-plugin/src/main/java/com/atlassian/maven/plugins/amps/RunMojo.java

https://bitbucket.org/mmeinhold/amps · Java · 349 lines · 260 code · 45 blank · 44 comment · 32 complexity · 495e0eda63b24079610ac3a895d94b7d MD5 · raw file

  1. package com.atlassian.maven.plugins.amps;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.OutputStream;
  6. import java.util.*;
  7. import java.util.concurrent.TimeUnit;
  8. import com.atlassian.maven.plugins.amps.product.ProductHandler;
  9. import com.atlassian.maven.plugins.amps.util.GoogleAmpsTracker;
  10. import com.google.common.base.Predicate;
  11. import com.google.common.collect.Iterables;
  12. import com.google.common.collect.Lists;
  13. import org.apache.commons.io.IOUtils;
  14. import org.apache.maven.artifact.Artifact;
  15. import org.apache.maven.plugin.MojoExecutionException;
  16. import org.apache.maven.plugin.MojoFailureException;
  17. import org.apache.maven.plugins.annotations.Execute;
  18. import org.apache.maven.plugins.annotations.LifecyclePhase;
  19. import org.apache.maven.plugins.annotations.Mojo;
  20. import org.apache.maven.plugins.annotations.Parameter;
  21. import org.apache.maven.plugins.annotations.ResolutionScope;
  22. import org.apache.maven.surefire.shade.org.apache.commons.lang.StringUtils;
  23. import static org.apache.commons.lang.StringUtils.isBlank;
  24. /**
  25. * Run the webapp
  26. */
  27. @Mojo(name = "run", requiresDependencyResolution = ResolutionScope.RUNTIME)
  28. @Execute(phase = LifecyclePhase.PACKAGE)
  29. public class RunMojo extends AbstractTestGroupsHandlerMojo
  30. {
  31. @Parameter(property = "wait", defaultValue = "true")
  32. private boolean wait;
  33. /**
  34. * Whether or not to write properties used by the plugin to amps.properties.
  35. */
  36. @Parameter(property = "amps.properties", required = true, defaultValue = "false")
  37. protected boolean writePropertiesToFile;
  38. /**
  39. * Test group to run. If provided, used to determine the products to run.
  40. */
  41. @Parameter(property = "testGroup")
  42. protected String testGroup;
  43. /**
  44. * Excluded instances from the execution. Only useful when Studio brings in all instances and you want to run only one.
  45. * List of comma separated instanceIds, or {@literal *}/instanceId to exclude all but one product.
  46. * <p>
  47. * Examples:
  48. * <ul>
  49. * <li>mvn amps:run -DexcludeInstances=studio-crowd</li>
  50. * <li>mvn amps:run -DexcludeInstances={@literal *}/studio-crowd to run only StudioCrowd</li>
  51. * </ul>
  52. */
  53. @Parameter(property = "excludeInstances")
  54. protected String excludeInstances;
  55. /**
  56. * The properties actually used by the mojo when running
  57. */
  58. protected final Map<String, String> properties = new HashMap<String, String>();
  59. protected void doExecute() throws MojoExecutionException, MojoFailureException
  60. {
  61. getUpdateChecker().check();
  62. getAmpsPluginVersionChecker().checkAmpsVersionInPom(getSdkVersion(),getMavenContext().getProject());
  63. promptForEmailSubscriptionIfNeeded();
  64. trackFirstRunIfNeeded();
  65. getGoogleTracker().track(GoogleAmpsTracker.RUN);
  66. final List<ProductExecution> productExecutions = getProductExecutions();
  67. startProducts(productExecutions);
  68. }
  69. protected void startProducts(List<ProductExecution> productExecutions) throws MojoExecutionException
  70. {
  71. long globalStartTime = System.nanoTime();
  72. setParallelMode(productExecutions);
  73. List<StartupInformation> successMessages = Lists.newArrayList();
  74. for (ProductExecution productExecution : productExecutions)
  75. {
  76. final ProductHandler productHandler = productExecution.getProductHandler();
  77. final Product product = productExecution.getProduct();
  78. if (product.isInstallPlugin() == null)
  79. {
  80. product.setInstallPlugin(shouldInstallPlugin());
  81. }
  82. //add artifacts for test console
  83. if(shouldBuildTestPlugin())
  84. {
  85. List<ProductArtifact> plugins = product.getBundledArtifacts();
  86. plugins.addAll(testFrameworkPlugins);
  87. }
  88. // Leave a blank line and say what it's doing
  89. getLog().info("");
  90. if (StringUtils.isNotBlank(product.getOutput()))
  91. {
  92. getLog().info(String.format("Starting %s... (see log at %s)", product.getInstanceId(), product.getOutput()));
  93. }
  94. else
  95. {
  96. getLog().info(String.format("Starting %s...", product.getInstanceId()));
  97. }
  98. // Actually start the product
  99. long startTime = System.nanoTime();
  100. int actualHttpPort = productHandler.start(product);
  101. long durationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
  102. // Log the success message
  103. StartupInformation message = new StartupInformation(product, "started successfully", actualHttpPort, durationSeconds);
  104. if (!parallel)
  105. {
  106. getLog().info(message.toString());
  107. }
  108. successMessages.add(message);
  109. if (writePropertiesToFile)
  110. {
  111. if (productExecutions.size() == 1)
  112. {
  113. properties.put("http.port", String.valueOf(actualHttpPort));
  114. properties.put("context.path", product.getContextPath());
  115. }
  116. properties.put("http." + product.getInstanceId() + ".port", String.valueOf(actualHttpPort));
  117. properties.put("context." + product.getInstanceId() + ".path", product.getContextPath());
  118. String baseUrl = MavenGoals.getBaseUrl(product, actualHttpPort);
  119. properties.put("baseurl." + product.getInstanceId(), baseUrl);
  120. }
  121. }
  122. if (writePropertiesToFile)
  123. {
  124. writePropertiesFile();
  125. }
  126. if (parallel)
  127. {
  128. waitForProducts(productExecutions, true);
  129. }
  130. long globalDurationSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - globalStartTime);
  131. // Give the messages once all applications are started
  132. if (successMessages.size() > 1 || parallel)
  133. {
  134. getLog().info("");
  135. getLog().info("=== Summary (total time " + globalDurationSeconds + "s):");
  136. // First show the log files
  137. for (StartupInformation message : successMessages)
  138. {
  139. if (StringUtils.isNotBlank(message.getOutput()))
  140. {
  141. getLog().info("Log available at: " + message.getOutput());
  142. }
  143. }
  144. // Then show the applications
  145. for (StartupInformation message : successMessages)
  146. {
  147. getLog().info(message.toString());
  148. }
  149. }
  150. if (wait)
  151. {
  152. getLog().info("Type Ctrl-D to shutdown gracefully");
  153. getLog().info("Type Ctrl-C to exit");
  154. try
  155. {
  156. while (System.in.read() != -1)
  157. {
  158. }
  159. }
  160. catch (final IOException e)
  161. {
  162. // ignore
  163. }
  164. // We don't stop products when -Dwait=false, because some projects rely on the
  165. // application running after the end of the RunMojo goal. The SHITTY tests
  166. // check this behaviour.
  167. stopProducts(productExecutions);
  168. }
  169. }
  170. protected List<ProductExecution> getProductExecutions() throws MojoExecutionException
  171. {
  172. final List<ProductExecution> productExecutions;
  173. final MavenGoals goals = getMavenGoals();
  174. if (!isBlank(testGroup))
  175. {
  176. productExecutions = getTestGroupProductExecutions(testGroup);
  177. }
  178. else if (!isBlank(instanceId))
  179. {
  180. Product ctx = getProductContexts().get(instanceId);
  181. if (ctx == null)
  182. {
  183. throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
  184. }
  185. ProductHandler product = createProductHandler(ctx.getId());
  186. productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
  187. }
  188. else
  189. {
  190. Product ctx = getProductContexts().get(getProductId());
  191. ProductHandler product = createProductHandler(ctx.getId());
  192. productExecutions = Collections.singletonList(new ProductExecution(ctx, product));
  193. }
  194. return filterExcludedInstances(includeStudioDependentProducts(productExecutions, goals));
  195. }
  196. private List<ProductExecution> filterExcludedInstances(List<ProductExecution> executions) throws MojoExecutionException
  197. {
  198. if (StringUtils.isBlank(excludeInstances))
  199. {
  200. return executions;
  201. }
  202. boolean inverted = excludeInstances.startsWith("*/");
  203. String instanceIdList = inverted ? excludeInstances.substring(2) : excludeInstances;
  204. // Parse the list given by the user and find ProductExecutions
  205. List<String> excludedInstanceIds = Lists.newArrayList(instanceIdList.split(","));
  206. List<ProductExecution> excludedExecutions = Lists.newArrayList();
  207. for (final String instanceId : excludedInstanceIds)
  208. {
  209. try
  210. {
  211. excludedExecutions.add(Iterables.find(executions, new Predicate<ProductExecution>()
  212. {
  213. @Override
  214. public boolean apply(ProductExecution input)
  215. {
  216. return input.getProduct().getInstanceId().equals(instanceId);
  217. }
  218. }));
  219. }
  220. catch (NoSuchElementException nsee)
  221. {
  222. throw new MojoExecutionException("You specified -Dexclude=" + excludeInstances + " but " + instanceId + " is not an existing instance id.");
  223. }
  224. }
  225. if (inverted)
  226. {
  227. return excludedExecutions;
  228. }
  229. else
  230. {
  231. executions.removeAll(excludedExecutions);
  232. return executions;
  233. }
  234. }
  235. /**
  236. * Only install a plugin if the installPlugin flag is true and the project is a jar. If the test plugin was built,
  237. * it will be installed as well.
  238. */
  239. private boolean shouldInstallPlugin()
  240. {
  241. Artifact artifact = getMavenContext().getProject().getArtifact();
  242. return installPlugin &&
  243. (artifact != null && !"pom".equalsIgnoreCase(artifact.getType()));
  244. }
  245. private void writePropertiesFile() throws MojoExecutionException
  246. {
  247. final Properties props = new Properties();
  248. for (Map.Entry<String, String> entry : properties.entrySet())
  249. {
  250. props.setProperty(entry.getKey(), entry.getValue());
  251. }
  252. final File ampsProperties = new File(getMavenContext().getProject().getBuild().getDirectory(), "amps.properties");
  253. OutputStream out = null;
  254. try
  255. {
  256. out = new FileOutputStream(ampsProperties);
  257. props.store(out, "");
  258. }
  259. catch (IOException e)
  260. {
  261. throw new MojoExecutionException("Error writing " + ampsProperties.getAbsolutePath(), e);
  262. }
  263. finally
  264. {
  265. IOUtils.closeQuietly(out);
  266. }
  267. }
  268. /**
  269. * Wraps information about the startup of a product
  270. */
  271. private static class StartupInformation
  272. {
  273. int actualHttpPort;
  274. long durationSeconds;
  275. Product product;
  276. String event;
  277. public StartupInformation(Product product, String event, int actualHttpPort, long durationSeconds)
  278. {
  279. super();
  280. this.actualHttpPort = actualHttpPort;
  281. this.product = product;
  282. this.event = event;
  283. this.durationSeconds = durationSeconds;
  284. }
  285. @Override
  286. public String toString()
  287. {
  288. String message = String.format("%s %s in %ds", product.getInstanceId(), event
  289. + (Boolean.FALSE.equals(product.getSynchronousStartup()) ? " (asynchronously)" : ""), durationSeconds);
  290. if (actualHttpPort != 0)
  291. {
  292. message += " at http://" + product.getServer() + ":" + actualHttpPort + (product.getContextPath().equals("ROOT") ? "" : product.getContextPath());
  293. }
  294. return message;
  295. }
  296. /**
  297. * @return the output of the product
  298. */
  299. public String getOutput()
  300. {
  301. return product.getOutput();
  302. }
  303. }
  304. }