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

https://bitbucket.org/mmeinhold/amps · Java · 975 lines · 605 code · 131 blank · 239 comment · 91 complexity · 83ab670c1693e91b63672427974b1a13 MD5 · raw file

  1. package com.atlassian.maven.plugins.amps;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Properties;
  11. import java.util.concurrent.ExecutionException;
  12. import java.util.concurrent.ExecutorService;
  13. import java.util.concurrent.Executors;
  14. import java.util.concurrent.Future;
  15. import java.util.concurrent.TimeUnit;
  16. import java.util.concurrent.TimeoutException;
  17. import com.atlassian.maven.plugins.amps.product.ProductHandler;
  18. import com.atlassian.maven.plugins.amps.product.ProductHandlerFactory;
  19. import com.atlassian.maven.plugins.amps.product.studio.StudioProductHandler;
  20. import com.atlassian.maven.plugins.amps.util.ArtifactRetriever;
  21. import com.atlassian.maven.plugins.amps.util.ProjectUtils;
  22. import com.google.common.base.Predicate;
  23. import com.google.common.collect.Iterables;
  24. import com.google.common.collect.Lists;
  25. import com.google.common.collect.Maps;
  26. import org.apache.commons.lang.StringUtils;
  27. import org.apache.maven.artifact.factory.ArtifactFactory;
  28. import org.apache.maven.artifact.repository.ArtifactRepository;
  29. import org.apache.maven.artifact.resolver.ArtifactResolver;
  30. import org.apache.maven.model.Resource;
  31. import org.apache.maven.plugin.MojoExecutionException;
  32. import org.apache.maven.plugin.MojoFailureException;
  33. import org.apache.maven.plugins.annotations.Component;
  34. import org.apache.maven.plugins.annotations.Parameter;
  35. import org.apache.maven.project.MavenProject;
  36. import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO;
  37. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.*;
  38. /**
  39. * Base class for webapp mojos
  40. */
  41. public abstract class AbstractProductHandlerMojo extends AbstractProductHandlerAwareMojo {
  42. // ------ start inline product context
  43. protected static final String JUNIT_VERSION = "4.10_1";
  44. protected static final String ATLASSIAN_TEST_RUNNER_VERSION = "1.1";
  45. protected static final String NO_TEST_GROUP = "__no_test_group__";
  46. /**
  47. * The artifacts to deploy for the test console if needed
  48. */
  49. protected final List<ProductArtifact> testFrameworkPlugins = new ArrayList<ProductArtifact>()
  50. {{
  51. add(new ProductArtifact("org.apache.servicemix.bundles","org.apache.servicemix.bundles.junit",JUNIT_VERSION));
  52. add(new ProductArtifact("com.atlassian.plugins","atlassian-plugins-osgi-testrunner-bundle",ATLASSIAN_TEST_RUNNER_VERSION));
  53. }};
  54. /**
  55. * Container to run in
  56. */
  57. @Parameter(property = "container")
  58. protected String containerId;
  59. /**
  60. * HTTP port for the servlet containers
  61. */
  62. @Parameter(property = "http.port", defaultValue = "0")
  63. private int httpPort;
  64. /**
  65. * If product should be started with https on port 443
  66. */
  67. @Parameter(property = "use.https", defaultValue = "false")
  68. protected boolean useHttps;
  69. /**
  70. * Application context path
  71. */
  72. @Parameter(property = "context.path")
  73. protected String contextPath;
  74. /**
  75. * Application server
  76. */
  77. @Parameter(property = "server")
  78. protected String server;
  79. /**
  80. * Webapp version
  81. */
  82. @Parameter(property = "product.version")
  83. private String productVersion;
  84. /**
  85. * JVM arguments to pass to cargo
  86. */
  87. @Parameter(property = "jvmargs")
  88. protected String jvmArgs;
  89. /**
  90. * Product startup timeout in milliseconds
  91. */
  92. @Parameter(property = "product.start.timeout")
  93. private int startupTimeout;
  94. /**
  95. * Product shutdown timeout in milliseconds
  96. */
  97. @Parameter(property = "product.stop.timeout")
  98. private int shutdownTimeout;
  99. /**
  100. * System systemProperties to pass to cargo
  101. *
  102. * @deprecated Since 3.2, use systemPropertyVariables instead
  103. */
  104. @Parameter
  105. @Deprecated
  106. protected Properties systemProperties = new Properties();
  107. /**
  108. * System Properties to pass to cargo using a more familiar syntax.
  109. *
  110. * @since 3.2
  111. */
  112. @Parameter
  113. protected Map<String, Object> systemPropertyVariables = new HashMap<String, Object>();
  114. /**
  115. * A log4j systemProperties file
  116. */
  117. @Parameter
  118. protected File log4jProperties;
  119. /**
  120. * The test resources version
  121. * @deprecated Since 3.0-beta2
  122. */
  123. @Deprecated
  124. @Parameter(property = "test.resources.version")
  125. private String testResourcesVersion;
  126. /**
  127. * The test resources version
  128. */
  129. @Parameter(property = "product.data.version")
  130. private String productDataVersion;
  131. /**
  132. * The path to a custom test resources zip
  133. */
  134. @Parameter(property = "product.data.path")
  135. private String productDataPath;
  136. /**
  137. * If FastDev should be enabled
  138. */
  139. @Parameter(property = "fastdev.enable", defaultValue = "true")
  140. protected boolean enableFastdev;
  141. /**
  142. * The version of FastDev to bundle
  143. */
  144. @Parameter(property = "fastdev.version", defaultValue = DEFAULT_FASTDEV_VERSION)
  145. protected String fastdevVersion;
  146. /**
  147. * If DevToolbox should be enabled
  148. */
  149. @Parameter(property = "devtoolbox.enable", defaultValue = "true")
  150. protected boolean enableDevToolbox;
  151. /**
  152. * The version of DevToolbox to bundle
  153. */
  154. @Parameter(property = "devtoolbox.version", defaultValue = DEFAULT_DEV_TOOLBOX_VERSION)
  155. protected String devToolboxVersion;
  156. /**
  157. * If PDE should be enabled
  158. */
  159. @Parameter(property = "pde.enable", defaultValue = "true")
  160. protected boolean enablePde;
  161. /**
  162. * The version of the PDE to bundle
  163. */
  164. @Parameter(property = "pde.version", defaultValue = DEFAULT_PDE_VERSION)
  165. protected String pdeVersion;
  166. @Parameter
  167. private List<ProductArtifact> pluginArtifacts = new ArrayList<ProductArtifact>();
  168. /**
  169. */
  170. @Parameter
  171. private List<ProductArtifact> libArtifacts = new ArrayList<ProductArtifact>();
  172. /**
  173. */
  174. @Parameter
  175. private List<ProductArtifact> bundledArtifacts = new ArrayList<ProductArtifact>();
  176. /**
  177. * SAL version
  178. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  179. */
  180. @Deprecated
  181. @Parameter
  182. private String salVersion;
  183. /**
  184. * Atlassian Plugin Development Kit (PDK) version
  185. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  186. */
  187. @Deprecated
  188. @Parameter(defaultValue = DEFAULT_PDK_VERSION)
  189. private String pdkVersion;
  190. /**
  191. * Atlassian REST module version
  192. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  193. */
  194. @Deprecated
  195. @Parameter
  196. private String restVersion;
  197. /**
  198. * Felix OSGi web console version
  199. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  200. */
  201. @Deprecated
  202. @Parameter(defaultValue = DEFAULT_WEB_CONSOLE_VERSION)
  203. private String webConsoleVersion;
  204. // ---------------- end product context
  205. /**
  206. * Comma-delimited list of plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  207. * ommitted, defaulting to LATEST
  208. */
  209. @Parameter(property = "plugins")
  210. private String pluginArtifactsString;
  211. /**
  212. * Comma-delimited list of lib artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  213. * ommitted, defaulting to LATEST
  214. */
  215. @Parameter(property = "lib.plugins")
  216. private String libArtifactsString;
  217. /**
  218. * Comma-delimited list of bundled plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  219. * ommitted, defaulting to LATEST
  220. */
  221. @Parameter(property = "bundled.plugins")
  222. private String bundledArtifactsString;
  223. /**
  224. * The build directory
  225. */
  226. @Parameter(property = "project.build.directory", required = true)
  227. protected File targetDirectory;
  228. /**
  229. * The jar name
  230. */
  231. @Parameter(property = "project.build.finalName", required = true)
  232. protected String finalName;
  233. /**
  234. * If the plugin and optionally its test plugin should be installed
  235. */
  236. @Parameter(property = "install.plugin", defaultValue = "true")
  237. protected boolean installPlugin;
  238. /**
  239. * The artifact resolver is used to dynamically resolve JARs that have to be in the embedded
  240. * container's classpaths. Another solution would have been to statitically define them a
  241. * dependencies in the plugin's POM. Resolving them in a dynamic manner is much better as only
  242. * the required JARs for the defined embedded container are downloaded.
  243. */
  244. @Component
  245. protected ArtifactResolver artifactResolver;
  246. /**
  247. * The local Maven repository. This is used by the artifact resolver to download resolved
  248. * JARs and put them in the local repository so that they won't have to be fetched again next
  249. * time the plugin is executed.
  250. */
  251. @Parameter(property = "localRepository")
  252. protected ArtifactRepository localRepository;
  253. /**
  254. * The remote Maven repositories used by the artifact resolver to look for JARs.
  255. */
  256. @Parameter(property = "project.remoteArtifactRepositories")
  257. protected List repositories;
  258. /**
  259. * The artifact factory is used to create valid Maven
  260. * {@link org.apache.maven.artifact.Artifact} objects. This is used to pass Maven artifacts to
  261. * the artifact resolver so that it can download the required JARs to put in the embedded
  262. * container's classpaths.
  263. */
  264. @Component
  265. protected ArtifactFactory artifactFactory;
  266. /**
  267. * A list of product-specific configurations (as literally provided in the pom.xml)
  268. */
  269. @Parameter
  270. protected List<Product> products = new ArrayList<Product>();
  271. /**
  272. * A map of {instanceId -> Product}, initialized by {@link #createProductContexts()}.
  273. * Cannot be set by the user.
  274. */
  275. private Map<String, Product> productMap;
  276. /**
  277. * File the container logging output will be sent to.
  278. */
  279. @Parameter
  280. private String output;
  281. /**
  282. * Comma-delimited list of bundled plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  283. * ommitted, defaulting to LATEST
  284. */
  285. @Parameter(property = "additional.resource.folders")
  286. private String additionalResourceFolders;
  287. /**
  288. * Start the products in parallel (TestGroups and Studio).
  289. */
  290. @Parameter(property = "parallel", defaultValue = "false")
  291. protected boolean parallel;
  292. protected Product createDefaultProductContext() throws MojoExecutionException
  293. {
  294. Product ctx = new Product();
  295. ctx.setId(getProductId());
  296. ctx.setContainerId(containerId);
  297. ctx.setServer(server);
  298. ctx.setContextPath(contextPath);
  299. ctx.setJvmArgs(jvmArgs);
  300. ctx.setStartupTimeout(startupTimeout);
  301. ctx.setShutdownTimeout(shutdownTimeout);
  302. // If they aren't defined, define those system properties. They will override the product
  303. // handler's properties.
  304. Map<String, Object> properties = new HashMap<String, Object>(systemPropertyVariables);
  305. properties.put("atlassian.sdk.version", getSdkVersion());
  306. setDefaultSystemProperty(properties, "atlassian.dev.mode", "true");
  307. setDefaultSystemProperty(properties, "java.awt.headless", "true");
  308. setDefaultSystemProperty(properties, "plugin.resource.directories", buildResourcesList());
  309. setDefaultSystemProperty(properties, "plugin.root.directories", buildRootProperty());
  310. ctx.setSystemPropertyVariables(properties);
  311. ctx.setBundledArtifacts(bundledArtifacts);
  312. ctx.setLibArtifacts(libArtifacts);
  313. ctx.setPluginArtifacts(pluginArtifacts);
  314. ctx.setLog4jProperties(log4jProperties);
  315. ctx.setHttpPort(httpPort);
  316. ctx.setUseHttps(useHttps);
  317. ctx.setVersion(productVersion);
  318. ctx.setDataVersion(productDataVersion);
  319. ctx.setDataPath(productDataPath);
  320. // continue to have these work for now
  321. ctx.setRestVersion(restVersion);
  322. ctx.setSalVersion(salVersion);
  323. ctx.setPdkVersion(pdkVersion);
  324. ctx.setWebConsoleVersion(webConsoleVersion);
  325. ctx.setEnableFastdev(enableFastdev);
  326. ctx.setFastdevVersion(fastdevVersion);
  327. ctx.setEnableDevToolbox(enableDevToolbox);
  328. ctx.setDevToolboxVersion(devToolboxVersion);
  329. ctx.setEnablePde(enablePde);
  330. ctx.setPdeVersion(pdeVersion);
  331. ctx.setHttpPort(httpPort);
  332. return ctx;
  333. }
  334. /**
  335. * @return a comma-separated list of resource directories. If a test plugin is detected, the
  336. * test resources directories are included as well.
  337. */
  338. private String buildResourcesList()
  339. {
  340. // collect all resource directories and make them available for
  341. // on-the-fly reloading
  342. StringBuilder resourceProp = new StringBuilder();
  343. if(StringUtils.isNotBlank(additionalResourceFolders))
  344. {
  345. String[] dirs = StringUtils.split(additionalResourceFolders,",");
  346. for(String rDir : dirs)
  347. {
  348. resourceProp.append(StringUtils.trim(rDir)).append(",");
  349. }
  350. }
  351. MavenProject mavenProject = getMavenContext().getProject();
  352. @SuppressWarnings("unchecked") List<Resource> resList = mavenProject.getResources();
  353. for (int i = 0; i < resList.size(); i++) {
  354. resourceProp.append(resList.get(i).getDirectory());
  355. if (i + 1 != resList.size()) {
  356. resourceProp.append(",");
  357. }
  358. }
  359. if (ProjectUtils.shouldDeployTestJar(getMavenContext()))
  360. {
  361. @SuppressWarnings("unchecked") List<Resource> testResList = mavenProject.getTestResources();
  362. for (int i = 0; i < testResList.size(); i++) {
  363. if (i == 0 && resourceProp.length() > 0)
  364. {
  365. resourceProp.append(",");
  366. }
  367. resourceProp.append(testResList.get(i).getDirectory());
  368. if (i + 1 != testResList.size()) {
  369. resourceProp.append(",");
  370. }
  371. }
  372. }
  373. return resourceProp.toString();
  374. }
  375. /**
  376. * @return the path of the project root, for the <tt>plugin.root.directories</tt> system property.
  377. *
  378. * @since 3.6
  379. */
  380. private String buildRootProperty()
  381. {
  382. MavenProject mavenProject = getMavenContext().getProject();
  383. return mavenProject.getBasedir().getPath();
  384. }
  385. private static void setDefaultSystemProperty(final Map<String,Object> props, final String key, final String value)
  386. {
  387. if (!props.containsKey(key))
  388. {
  389. props.put(key, System.getProperty(key, value));
  390. }
  391. }
  392. /**
  393. * Set the default values for the product
  394. * @param product the product
  395. * @param handler the product handler associated to the product
  396. */
  397. protected void setDefaultValues(Product product, ProductHandler handler)
  398. {
  399. product.setInstanceId(getProductInstanceId(product));
  400. //Apply the common default values
  401. String dversion = System.getProperty("product.data.version", product.getDataVersion());
  402. String pversion = System.getProperty("product.version", product.getVersion());
  403. String dpath = System.getProperty("product.data.path", product.getDataPath());
  404. // If it's a Studio product, some defaults are different (ex: context path for Confluence is /wiki)
  405. if (!StudioProductHandler.setDefaultValues(getMavenContext(), product))
  406. {
  407. // hacky workaround for AMPS-738: avoid applying the regular product defaults to a Studio sub-product;
  408. // however, do apply them to the main Studio product if and only if we're explicitly running Studio (so
  409. // we'll get the right result for command-line options like "-Dproduct=studio -Dproduct.version=108.3").
  410. if (!STUDIO.equals(product.getId()) || STUDIO.equals(System.getProperty("product")))
  411. {
  412. product.setVersion(pversion);
  413. product.setDataVersion(dversion);
  414. product.setDataPath(dpath);
  415. }
  416. }
  417. product.setArtifactRetriever(new ArtifactRetriever(artifactResolver, artifactFactory, localRepository, repositories));
  418. if (product.getContainerId() == null)
  419. {
  420. product.setContainerId(handler.getDefaultContainerId());
  421. }
  422. if (product.getServer() == null)
  423. {
  424. product.setServer(DEFAULT_SERVER);
  425. }
  426. if (product.getPdkVersion() == null)
  427. {
  428. product.setPdkVersion(DEFAULT_PDK_VERSION);
  429. }
  430. if (product.getWebConsoleVersion() == null)
  431. {
  432. product.setWebConsoleVersion(DEFAULT_WEB_CONSOLE_VERSION);
  433. }
  434. if (product.isEnableFastdev() == null)
  435. {
  436. product.setEnableFastdev(true);
  437. }
  438. if (product.getFastdevVersion() == null)
  439. {
  440. product.setFastdevVersion(DEFAULT_FASTDEV_VERSION);
  441. }
  442. if (product.isEnableDevToolbox() == null)
  443. {
  444. product.setEnableDevToolbox(true);
  445. }
  446. if (product.getDevToolboxVersion() == null)
  447. {
  448. product.setDevToolboxVersion(DEFAULT_DEV_TOOLBOX_VERSION);
  449. }
  450. if (product.getPdeVersion() == null)
  451. {
  452. product.setPdeVersion(DEFAULT_PDE_VERSION);
  453. }
  454. if (product.getOutput() == null)
  455. {
  456. product.setOutput(output);
  457. }
  458. if (product.getStartupTimeout() <= 0)
  459. {
  460. product.setStartupTimeout(DEFAULT_PRODUCT_STARTUP_TIMEOUT);
  461. }
  462. if (product.getShutdownTimeout() <= 0)
  463. {
  464. product.setShutdownTimeout(DEFAULT_PRODUCT_SHUTDOWN_TIMEOUT);
  465. }
  466. if (product.getHttpPort() == 0)
  467. {
  468. product.setHttpPort(handler.getDefaultHttpPort());
  469. }
  470. if (product.getUseHttps() == null)
  471. {
  472. product.setUseHttps(false);
  473. }
  474. if (product.getVersion() == null)
  475. {
  476. product.setVersion("RELEASE");
  477. }
  478. if (product.getDataVersion() == null)
  479. {
  480. // Default the productDataVersion to match the productVersion. Defaulting to LATEST
  481. // is bad because there is no guarantee that a snapshots let alone a more recent
  482. // version of a product's data is compatible with an earlier version of the product or
  483. // that a product is required to provide a 'downgrade' task. Developers can still
  484. // specify LATEST explicitly
  485. product.setDataVersion(product.getVersion());
  486. }
  487. if (product.getContextPath() == null)
  488. {
  489. product.setContextPath(handler.getDefaultContextPath());
  490. }
  491. if (product.getDataSources() == null)
  492. {
  493. product.setDataSources(Lists.<DataSource>newArrayList());
  494. }
  495. }
  496. private List<ProductArtifact> stringToArtifactList(String val, List<ProductArtifact> artifacts)
  497. {
  498. if (val == null || val.trim().length() == 0)
  499. {
  500. return artifacts;
  501. }
  502. for (String ptn : val.split(","))
  503. {
  504. String[] items = ptn.split(":");
  505. if (items.length < 2 || items.length > 3)
  506. {
  507. throw new IllegalArgumentException("Invalid artifact pattern: " + ptn);
  508. }
  509. String groupId = items[0];
  510. String artifactId = items[1];
  511. String version = (items.length == 3 ? items[2] : "LATEST");
  512. artifacts.add(new ProductArtifact(groupId, artifactId, version));
  513. }
  514. return artifacts;
  515. }
  516. @Override
  517. public final void execute() throws MojoExecutionException, MojoFailureException
  518. {
  519. stringToArtifactList(pluginArtifactsString, pluginArtifacts);
  520. stringToArtifactList(libArtifactsString, libArtifacts);
  521. stringToArtifactList(bundledArtifactsString, bundledArtifacts);
  522. systemPropertyVariables.putAll((Map) systemProperties);
  523. detectDeprecatedVersionOverrides();
  524. doExecute();
  525. }
  526. private void detectDeprecatedVersionOverrides()
  527. {
  528. Properties props = getMavenContext().getProject().getProperties();
  529. for (String deprecatedProperty : new String[] {"sal.version", "rest.version", "web.console.version", "pdk.version"})
  530. {
  531. if (props.containsKey(deprecatedProperty))
  532. {
  533. getLog().warn("The property '" + deprecatedProperty + "' is no longer usable to override the related bundled plugin." +
  534. " Use <pluginArtifacts> or <libArtifacts> to explicitly override bundled plugins and libraries, respectively.");
  535. }
  536. }
  537. }
  538. /**
  539. * Builds the map {instanceId -> Product bean}, based on: <ul>
  540. * <li>the {@literal <products>} tag</li>
  541. * <li>the configuration values inherited from the {@literal <configuration>} tag
  542. * </ul>
  543. * @throws MojoExecutionException
  544. */
  545. Map<String, Product> createProductContexts() throws MojoExecutionException
  546. {
  547. Map<String, Product> productMap = Maps.newHashMap();
  548. MavenContext mavenContext = getMavenContext();
  549. MavenGoals goals = getMavenGoals();
  550. // Products in the <products> tag inherit from the upper settings, e.g. when there's a <httpPort> tag for all products
  551. makeProductsInheritDefaultConfiguration(products, productMap);
  552. for (Product ctx : Lists.newArrayList(productMap.values()))
  553. {
  554. ProductHandler handler = ProductHandlerFactory.create(ctx.getId(), mavenContext, goals);
  555. setDefaultValues(ctx, handler);
  556. // If it's a Studio product, check dependent instance are present
  557. for (String instanceId : StudioProductHandler.getDependantInstances(ctx))
  558. {
  559. if (!productMap.containsKey(instanceId))
  560. {
  561. ProductHandler dependantHandler = createProductHandler(instanceId);
  562. productMap.put(instanceId, createProductContext(instanceId, instanceId, dependantHandler));
  563. }
  564. }
  565. }
  566. // Submit the Studio products for configuration
  567. StudioProductHandler studioProductHandler = (StudioProductHandler) ProductHandlerFactory.create(ProductHandlerFactory.STUDIO, mavenContext, goals);
  568. studioProductHandler.configureStudioProducts(productMap);
  569. return productMap;
  570. }
  571. /**
  572. * Returns the map { instanceId -> Product } with initialized values.
  573. */
  574. protected Map<String, Product> getProductContexts() throws MojoExecutionException
  575. {
  576. if (productMap == null)
  577. {
  578. productMap = createProductContexts();
  579. }
  580. return productMap;
  581. }
  582. /**
  583. * Puts the list of {@literal <products>} in productMap:
  584. * <ul>
  585. * <li>The {@literal <product>} from the maven-amps-plugin configuration (if missing, RefApp is used)</li>
  586. * <li>The {@literal <products>} from the maven-amps-plugin configuration</li>
  587. * </ul>
  588. */
  589. void makeProductsInheritDefaultConfiguration(List<Product> products, Map<String, Product> productMap) throws MojoExecutionException
  590. {
  591. Product defaultProduct = createDefaultProductContext();
  592. productMap.put(getProductId(), defaultProduct);
  593. if (!products.isEmpty())
  594. {
  595. for (Product product : products)
  596. {
  597. Product processedProduct = product.merge(defaultProduct);
  598. if (ProductHandlerFactory.STUDIO_CROWD.equals(processedProduct.getId()))
  599. {
  600. // This is a temporary fix for StudioCrowd - it requires atlassian.dev.mode=false - see AMPS-556
  601. processedProduct.getSystemPropertyVariables().put("atlassian.dev.mode", "false");
  602. }
  603. String instanceId = getProductInstanceId(processedProduct);
  604. productMap.put(instanceId, processedProduct);
  605. }
  606. }
  607. }
  608. private String getProductInstanceId(Product processedProduct)
  609. {
  610. return processedProduct.getInstanceId() == null ? processedProduct.getId() : processedProduct.getInstanceId();
  611. }
  612. private Product createProductContext(String productNickname, String instanceId, ProductHandler handler) throws MojoExecutionException
  613. {
  614. getLog().info(String.format("Studio (instanceId=%s): No product with name %s is defined in the pom. Using a default product.", instanceId, productNickname));
  615. Product product;
  616. product = createDefaultProductContext();
  617. product.setId(productNickname);
  618. product.setInstanceId(instanceId);
  619. setDefaultValues(product, handler);
  620. if (ProductHandlerFactory.STUDIO_CROWD.equals(product.getId()))
  621. {
  622. // This is a temporary fix for StudioCrowd - it requires atlassian.dev.mode=false - see AMPS-556
  623. product.getSystemPropertyVariables().put("atlassian.dev.mode", "false");
  624. }
  625. return product;
  626. }
  627. /**
  628. * Attempts to stop all products. Returns after the timeout or as soon as all products
  629. * are shut down.
  630. */
  631. protected void stopProducts(List<ProductExecution> productExecutions) throws MojoExecutionException
  632. {
  633. ExecutorService executor = Executors.newFixedThreadPool(productExecutions.size());
  634. try
  635. {
  636. long before = System.nanoTime();
  637. for (final ProductExecution execution : Iterables.reverse(productExecutions))
  638. {
  639. final Product product = execution.getProduct();
  640. final ProductHandler productHandler = execution.getProductHandler();
  641. Future<?> task = executor.submit(new Runnable()
  642. {
  643. @Override
  644. public void run()
  645. {
  646. getLog().info(product.getInstanceId() + ": Shutting down");
  647. try
  648. {
  649. productHandler.stop(product);
  650. }
  651. catch (MojoExecutionException e)
  652. {
  653. getLog().error("Exception while trying to stop " + product.getInstanceId(), e);
  654. }
  655. }
  656. });
  657. try
  658. {
  659. task.get(product.getShutdownTimeout(), TimeUnit.MILLISECONDS);
  660. }
  661. catch (TimeoutException e)
  662. {
  663. getLog().info(product.getInstanceId() + " shutdown: Didn't return in time");
  664. task.cancel(true);
  665. }
  666. }
  667. long after = System.nanoTime();
  668. getLog().info("amps:stop in " + TimeUnit.NANOSECONDS.toSeconds(after - before) + "s");
  669. }
  670. catch (InterruptedException e1)
  671. {
  672. Thread.currentThread().interrupt();
  673. }
  674. catch (ExecutionException e)
  675. {
  676. throw new MojoExecutionException("Exception while stopping the products", e);
  677. }
  678. // If products were launched in parallel, check they are stopped: CodeHaus Cargo returns before
  679. // products are down.
  680. if (parallel)
  681. {
  682. waitForProducts(productExecutions, false);
  683. }
  684. }
  685. /**
  686. * Waits until all products are running or stopped
  687. * @param startingUp true if starting up the products, false if shutting down.
  688. */
  689. protected void waitForProducts(List<ProductExecution> productExecutions, boolean startingUp) throws MojoExecutionException
  690. {
  691. for (ProductExecution productExecution : productExecutions)
  692. {
  693. pingRepeatedly(productExecution.getProduct(), startingUp);
  694. }
  695. }
  696. /**
  697. * Ping the product until it's up or stopped
  698. * @param startingUp true if applications are expected to be up; false if applications are expected to be brought down
  699. * @throws MojoExecutionException if the product didn't have the expected behaviour beofre the timeout
  700. */
  701. private void pingRepeatedly(Product product, boolean startingUp) throws MojoExecutionException
  702. {
  703. if (product.getHttpPort() != 0)
  704. {
  705. String url = "http://" + product.getServer() + ":" + product.getHttpPort();
  706. if (StringUtils.isNotBlank(product.getContextPath()))
  707. {
  708. url = url + product.getContextPath();
  709. }
  710. int timeout = startingUp ? product.getStartupTimeout() : product.getShutdownTimeout();
  711. final long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout);
  712. boolean interrupted = false;
  713. boolean success = false;
  714. String lastMessage = "";
  715. // keep retrieving from the url until a good response is returned, under a time limit.
  716. while (!success && !interrupted && System.nanoTime() < end)
  717. {
  718. HttpURLConnection connection = null;
  719. try
  720. {
  721. URL urlToPing = new URL(url);
  722. connection = (HttpURLConnection) urlToPing.openConnection();
  723. int response = connection.getResponseCode();
  724. // Tomcat returns 404 until the webapp is up
  725. lastMessage = "Last response code is " + response;
  726. if (startingUp)
  727. {
  728. success = response < 400;
  729. }
  730. else
  731. {
  732. success = response >= 400;
  733. }
  734. }
  735. catch (IOException e)
  736. {
  737. lastMessage = e.getMessage();
  738. success = !startingUp;
  739. }
  740. finally
  741. {
  742. if (connection != null)
  743. {
  744. try
  745. {
  746. connection.getInputStream().close();
  747. }
  748. catch (IOException e)
  749. {
  750. // Don't do anything
  751. }
  752. }
  753. }
  754. if (!success)
  755. {
  756. getLog().info("Waiting for " + url + (startingUp ? "" : " to stop"));
  757. try
  758. {
  759. Thread.sleep(1000);
  760. }
  761. catch (InterruptedException e)
  762. {
  763. Thread.currentThread().interrupt();
  764. interrupted = true;
  765. break;
  766. }
  767. }
  768. }
  769. if (!success)
  770. {
  771. throw new MojoExecutionException(String.format("The product %s didn't %s after %ds at %s. %s",
  772. product.getInstanceId(), startingUp ? "start" : "stop", TimeUnit.MILLISECONDS.toSeconds(timeout), url, lastMessage));
  773. }
  774. }
  775. }
  776. /**
  777. * @return the list of instances for the product 'studio'
  778. */
  779. private Iterable<ProductExecution> getStudioExecutions(final List<ProductExecution> productExecutions)
  780. {
  781. return Iterables.filter(productExecutions, new Predicate<ProductExecution>(){
  782. @Override
  783. public boolean apply(ProductExecution input)
  784. {
  785. return input.getProductHandler() instanceof StudioProductHandler;
  786. }});
  787. }
  788. /**
  789. * If there is any Studio instance, returns a list with all products requested by this instance.
  790. *
  791. * Configures both the Studio instance and its dependent products.
  792. *
  793. * @param productExecutions the current list of products to run
  794. * @param goals
  795. * @return the complete list of products to run
  796. * @throws MojoExecutionException
  797. */
  798. protected List<ProductExecution> includeStudioDependentProducts(final List<ProductExecution> productExecutions, final MavenGoals goals)
  799. throws MojoExecutionException
  800. {
  801. // If one of the products is Studio, ask him/her which other products he/she wants to run
  802. Iterable<ProductExecution> studioExecutions = getStudioExecutions(productExecutions);
  803. if (Iterables.isEmpty(studioExecutions))
  804. {
  805. return productExecutions;
  806. }
  807. // We have studio execution(s), so we need to add all products requested by Studio
  808. List<ProductExecution> productExecutionsIncludingStudio = Lists.newArrayList(productExecutions);
  809. Map<String, Product> allContexts = getProductContexts();
  810. for(ProductExecution execution : studioExecutions)
  811. {
  812. for (String dependantProduct : StudioProductHandler.getDependantInstances(execution.getProduct()))
  813. {
  814. Product product = allContexts.get(dependantProduct);
  815. productExecutionsIncludingStudio.add(toProductExecution(product));
  816. }
  817. }
  818. return productExecutionsIncludingStudio;
  819. }
  820. protected ProductExecution toProductExecution(Product product)
  821. {
  822. return new ProductExecution(product, createProductHandler(product.getId()));
  823. }
  824. protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;
  825. protected void setParallelMode(List<ProductExecution> executions)
  826. {
  827. // Apply the configuration of the mojo to the products
  828. for (ProductExecution execution : executions)
  829. {
  830. Product product = execution.getProduct();
  831. if (parallel)
  832. {
  833. if (product.getSynchronousStartup() == null)
  834. {
  835. product.setSynchronousStartup(Boolean.FALSE);
  836. }
  837. }
  838. else
  839. {
  840. product.setSynchronousStartup(Boolean.TRUE);
  841. }
  842. }
  843. }
  844. }