PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/atlassian/amps
Java | 946 lines | 491 code | 134 blank | 321 comment | 70 complexity | 54867e37347d0bf506b0927c1d89c4aa MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. package com.atlassian.maven.plugins.amps;
  2. import com.atlassian.maven.plugins.amps.product.ProductHandler;
  3. import com.atlassian.maven.plugins.amps.util.ArtifactRetriever;
  4. import com.google.common.annotations.VisibleForTesting;
  5. import org.apache.maven.artifact.Artifact;
  6. import org.apache.maven.artifact.repository.ArtifactRepository;
  7. import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager;
  8. import org.apache.maven.model.Resource;
  9. import org.apache.maven.plugin.MojoExecutionException;
  10. import org.apache.maven.plugin.MojoFailureException;
  11. import org.apache.maven.plugins.annotations.Component;
  12. import org.apache.maven.plugins.annotations.Parameter;
  13. import org.apache.maven.project.MavenProject;
  14. import javax.annotation.Nonnull;
  15. import java.io.File;
  16. import java.util.ArrayList;
  17. import java.util.Collection;
  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.Optional;
  22. import java.util.Properties;
  23. import java.util.concurrent.ExecutionException;
  24. import java.util.concurrent.ExecutorService;
  25. import java.util.concurrent.Future;
  26. import java.util.concurrent.TimeoutException;
  27. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_DEV_TOOLBOX_VERSION;
  28. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_PDE_VERSION;
  29. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_PDK_VERSION;
  30. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_PLUGIN_VIEWER_VERSION;
  31. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_PRODUCT_SHUTDOWN_TIMEOUT;
  32. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_PRODUCT_STARTUP_TIMEOUT;
  33. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_QUICK_RELOAD_VERSION;
  34. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_SERVER;
  35. import static com.atlassian.maven.plugins.amps.product.AmpsDefaults.DEFAULT_WEB_CONSOLE_VERSION;
  36. import static com.atlassian.maven.plugins.amps.util.ProductHandlerUtil.awaitStateChange;
  37. import static com.atlassian.maven.plugins.amps.util.ProductHandlerUtil.toArtifacts;
  38. import static com.atlassian.maven.plugins.amps.util.ProjectUtils.getReactorArtifact;
  39. import static com.atlassian.maven.plugins.amps.util.ProjectUtils.shouldDeployTestJar;
  40. import static java.lang.String.join;
  41. import static java.lang.Thread.currentThread;
  42. import static java.util.Collections.reverse;
  43. import static java.util.Collections.singletonList;
  44. import static java.util.Collections.unmodifiableList;
  45. import static java.util.Locale.ENGLISH;
  46. import static java.util.concurrent.Executors.newFixedThreadPool;
  47. import static java.util.concurrent.TimeUnit.MILLISECONDS;
  48. import static java.util.concurrent.TimeUnit.NANOSECONDS;
  49. import static org.apache.commons.lang3.StringUtils.split;
  50. import static org.apache.commons.lang3.StringUtils.trim;
  51. import static org.apache.commons.lang3.StringUtils.trimToEmpty;
  52. /**
  53. * Base class for webapp Mojos.
  54. */
  55. @SuppressWarnings("FieldMayBeFinal")
  56. public abstract class AbstractProductHandlerMojo extends AbstractProductAwareMojo {
  57. protected static final String ATLASSIAN_TEST_RUNNER_VERSION = "2.0.2";
  58. protected static final String NO_TEST_GROUP = "__no_test_group__";
  59. private static final String JUNIT_GROUP_ID = "org.apache.servicemix.bundles";
  60. private static final String JUNIT_ARTIFACT_ID = "org.apache.servicemix.bundles.junit";
  61. private static final String JUNIT_VERSION = "4.12_1";
  62. private static final String TESTRUNNER_GROUP_ID = "com.atlassian.plugins";
  63. private static final String TESTRUNNER_ARTIFACT_ID = "atlassian-plugins-osgi-testrunner";
  64. private static final String TESTRUNNER_BUNDLE_ARTIFACT_ID = "atlassian-plugins-osgi-testrunner-bundle";
  65. // SSL/https defaults
  66. public static final String DEFAULT_HTTPS_KEYSTOREFILE = "${user.home}/.keystore";
  67. public static final String DEFAULT_HTTPS_KEYSTOREPASS = "changeit";
  68. public static final String DEFAULT_HTTPS_KEYALIAS = "tomcat";
  69. public static final String DEFAULT_HTTP_SECURE = "true";
  70. public static final String DEFAULT_HTTPS_SSL_PROTOCOL = "TLS";
  71. public static final String DEFAULT_HTTPS_CLIENTAUTH = "false";
  72. public static final String DEFAULT_HTTPS_PORT = "0";
  73. @Component
  74. private RepositoryMetadataManager repositoryMetadataManager;
  75. // ------ start inline product context
  76. /**
  77. * The artifacts to deploy for the test console if needed.
  78. */
  79. private final List<ProductArtifact> testFrameworkPlugins = new ArrayList<>();
  80. /**
  81. * The Cargo ID of the container in which to start the product.
  82. */
  83. @Parameter(property = "container")
  84. private String containerId;
  85. /**
  86. * The Maven coordinates of the servlet container to run in, if {@code containerId} is not specified or is
  87. * literally equal to {@code "customContainerArtifact"}. Expected to be of the form
  88. * {@code groupId:artifactId:version[:packaging][:classifier]}.
  89. */
  90. @Parameter(property = "customContainerArtifact")
  91. private String customContainerArtifact;
  92. /**
  93. * The HTTP port for the servlet container.
  94. */
  95. @Parameter(property = "http.port", defaultValue = "0")
  96. private int httpPort;
  97. /**
  98. * The AJP port for Cargo to communicate with the servlet container.
  99. */
  100. @Parameter(property = "ajp.port", defaultValue = "8009") // Cargo's default is 8009
  101. private int ajpPort;
  102. /**
  103. * Whether the product should be started with HTTPS.
  104. */
  105. @Parameter(property = "use.https", defaultValue = "false")
  106. private boolean useHttps;
  107. /**
  108. * The HTTPS port for the servlet container.
  109. */
  110. @Parameter(property = "https.port", defaultValue = DEFAULT_HTTPS_PORT)
  111. private int httpsPort;
  112. /**
  113. * The SSL certificate chain option.
  114. *
  115. * @since 5.0.4
  116. */
  117. @Parameter(property = "https.clientAuth", defaultValue = DEFAULT_HTTPS_CLIENTAUTH)
  118. private String httpsClientAuth;
  119. /**
  120. * The SSL protocol to use.
  121. *
  122. * @since 5.0.4
  123. */
  124. @Parameter(property = "https.sslProtocol", defaultValue = DEFAULT_HTTPS_SSL_PROTOCOL)
  125. private String httpsSslProtocol;
  126. /**
  127. * The pathname of the keystore file.
  128. *
  129. * @since 5.0.4
  130. */
  131. @Parameter(property = "https.keystoreFile", defaultValue = DEFAULT_HTTPS_KEYSTOREFILE)
  132. private String httpsKeystoreFile;
  133. /**
  134. * The password to use to access the keypass store.
  135. *
  136. * @since 5.0.4
  137. */
  138. @Parameter(property = "https.keystorePass", defaultValue = DEFAULT_HTTPS_KEYSTOREPASS)
  139. private String httpsKeystorePass;
  140. /**
  141. * The alias of the certificate to use.
  142. *
  143. * @since 5.0.4
  144. */
  145. @Parameter(property = "https.keyAlias", defaultValue = DEFAULT_HTTPS_KEYALIAS)
  146. private String httpsKeyAlias;
  147. /**
  148. * Whether the HTTP container is secured.
  149. *
  150. * @since 5.0.4
  151. */
  152. @Parameter(property = "https.httpSecure", defaultValue = DEFAULT_HTTP_SECURE)
  153. private boolean httpsHttpSecure;
  154. /**
  155. * The product's context path.
  156. */
  157. @Parameter(property = "context.path")
  158. protected String contextPath;
  159. /**
  160. * The hostname of the server running the product.
  161. */
  162. @Parameter(property = "server")
  163. protected String server;
  164. /**
  165. * The version of the product.
  166. */
  167. @Parameter(property = "product.version")
  168. private String productVersion;
  169. /**
  170. * Any JVM arguments to pass to Cargo.
  171. */
  172. @Parameter(property = "jvmargs")
  173. protected String jvmArgs;
  174. /**
  175. * The product startup timeout in milliseconds.
  176. */
  177. @Parameter(property = "product.start.timeout")
  178. private int startupTimeout;
  179. /**
  180. * The product shutdown timeout in milliseconds.
  181. */
  182. @Parameter(property = "product.stop.timeout")
  183. private int shutdownTimeout;
  184. /**
  185. * The {@code systemProperties} that Cargo should apply when starting the servlet container.
  186. *
  187. * @deprecated Since 3.2, use systemPropertyVariables instead
  188. */
  189. @Parameter
  190. @Deprecated
  191. protected Properties systemProperties = new Properties();
  192. /**
  193. * The {@code systemProperties} that Cargo should apply when starting the servlet container, using a more familiar
  194. * syntax.
  195. *
  196. * @since 3.2
  197. */
  198. @Parameter
  199. protected Map<String, Object> systemPropertyVariables = new HashMap<>();
  200. /**
  201. * The full path of a custom log4j configuration file, in Java properties format. For example, set this to
  202. * {@code ${basedir}/src/test/resources/my-custom-log4j.properties} to remain location-independent. AMPS
  203. * will configure the product's loggers using this file, instead of the file that ships with the product.
  204. */
  205. @Parameter
  206. private File log4jProperties;
  207. /**
  208. * The test resources version.
  209. *
  210. * @deprecated Since 3.0-beta2, use product.data.version
  211. */
  212. @Deprecated
  213. @Parameter(property = "test.resources.version")
  214. private String testResourcesVersion;
  215. /**
  216. * The test resources version.
  217. */
  218. @Parameter(property = "product.data.version")
  219. private String productDataVersion;
  220. /**
  221. * The path to a custom test resources zip.
  222. */
  223. @Parameter(property = "product.data.path")
  224. private String productDataPath;
  225. /**
  226. * The path to a directory with home directory overrides.
  227. */
  228. @Parameter(property = "product.data.overrides.path")
  229. private String productDataOverridesPath;
  230. /**
  231. * Whether the DevToolbox should be enabled.
  232. */
  233. @Parameter(property = "devtoolbox.enable", defaultValue = "true")
  234. private boolean enableDevToolbox;
  235. /**
  236. * The version of DevToolbox to bundle.
  237. */
  238. @Parameter(property = "devtoolbox.version", defaultValue = DEFAULT_DEV_TOOLBOX_VERSION)
  239. private String devToolboxVersion;
  240. /**
  241. * Whether to enable the QuickReload plugin.
  242. */
  243. @Parameter(property = "quickreload.enable", defaultValue = "false")
  244. private boolean enableQuickReload;
  245. /**
  246. * The version of QuickReload to use.
  247. */
  248. @Parameter(property = "quickreload.version", defaultValue = DEFAULT_QUICK_RELOAD_VERSION)
  249. private String quickReloadVersion;
  250. /**
  251. * Whether the PluginViewer should be enabled.
  252. */
  253. @Parameter(property = "viewer.enable", defaultValue = "false")
  254. private boolean enablePluginViewer;
  255. /**
  256. * The version of the PluginViewer to use.
  257. */
  258. @Parameter(property = "viewer.version", defaultValue = DEFAULT_PLUGIN_VIEWER_VERSION)
  259. private String pluginViewerVersion;
  260. /**
  261. * Whether the Plugin Data Editor should be enabled.
  262. */
  263. @Parameter(property = "pde.enable", defaultValue = "true")
  264. private boolean enablePde;
  265. /**
  266. * The version of the Plugin Data Editor to use.
  267. */
  268. @Parameter(property = "pde.version", defaultValue = DEFAULT_PDE_VERSION)
  269. private String pdeVersion;
  270. /**
  271. * The applications to install into the product.
  272. */
  273. @Parameter
  274. private List<Application> applications = new ArrayList<>();
  275. /**
  276. * The plugins to load into the product.
  277. */
  278. @Parameter
  279. private List<ProductArtifact> pluginArtifacts = new ArrayList<>();
  280. /**
  281. * Artifacts to load into the servlet container's {@code lib} directory.
  282. */
  283. @Parameter
  284. private List<ProductArtifact> libArtifacts = new ArrayList<>();
  285. /**
  286. * Any plugins to be installed as bundled plugins.
  287. */
  288. @Parameter
  289. private List<ProductArtifact> bundledArtifacts = new ArrayList<>();
  290. /**
  291. * The version of Atlassian Shared Access Layer (SAL) artifacts to use.
  292. *
  293. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  294. */
  295. @Deprecated
  296. @Parameter
  297. private String salVersion;
  298. /**
  299. * The version of the Atlassian Plugin Development Kit (PDK) to use.
  300. *
  301. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  302. */
  303. @Deprecated
  304. @Parameter(defaultValue = DEFAULT_PDK_VERSION)
  305. private String pdkVersion;
  306. /**
  307. * The version of Atlassian REST artifacts to use.
  308. *
  309. * @deprecated Since 3.2, use {@link #pluginArtifacts} instead
  310. */
  311. @Deprecated
  312. @Parameter
  313. private String restVersion;
  314. /**
  315. * The Felix OSGi web console version.
  316. *
  317. * @deprecated Since 3.2, use {@code pluginArtifacts} instead
  318. */
  319. @Deprecated
  320. @Parameter(defaultValue = DEFAULT_WEB_CONSOLE_VERSION)
  321. private String webConsoleVersion;
  322. /**
  323. * The products' default DataSource, if any. DataSources can also be specified within each individual
  324. * {@code <product>} element.
  325. *
  326. * @since 8.3
  327. */
  328. @Parameter
  329. private DataSource dataSource;
  330. /**
  331. * The product nodes to start up. An empty list means single-node (non-clustered) operation.
  332. *
  333. * @since 8.3 before which we only ever started one node of each product
  334. */
  335. @Parameter
  336. private List<Node> nodes;
  337. // ---------------- end product context
  338. /**
  339. * Comma-delimited list of plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  340. * omitted, defaulting to LATEST.
  341. */
  342. @Parameter(property = "plugins")
  343. private String pluginArtifactsString;
  344. /**
  345. * Comma-delimited list of lib artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  346. * omitted, defaulting to LATEST.
  347. */
  348. @Parameter(property = "lib.plugins")
  349. private String libArtifactsString;
  350. /**
  351. * Comma-delimited list of bundled plugin artifacts in GROUP_ID:ARTIFACT_ID:VERSION form, where version can be
  352. * omitted, defaulting to LATEST.
  353. */
  354. @Parameter(property = "bundled.plugins")
  355. private String bundledArtifactsString;
  356. /**
  357. * The project's build directory.
  358. */
  359. @Parameter(property = "project.build.directory", required = true)
  360. protected File targetDirectory;
  361. /**
  362. * The filename of the plugin's JAR file.
  363. */
  364. @Parameter(property = "project.build.finalName", required = true)
  365. protected String finalName;
  366. /**
  367. * If the plugin and optionally its test plugin should be installed
  368. */
  369. @Parameter(property = "install.plugin", defaultValue = "true")
  370. protected boolean installPlugin;
  371. /**
  372. * The local Maven repository. This is used by the artifact resolver to download resolved
  373. * JARs and put them in the local repository so that they won't have to be fetched again next
  374. * time the plugin is executed.
  375. */
  376. @Parameter(property = "localRepository")
  377. private ArtifactRepository localRepository;
  378. /**
  379. * The remote Maven repositories used by the artifact resolver to look for JARs.
  380. */
  381. @Parameter(property = "project.remoteArtifactRepositories")
  382. private List<ArtifactRepository> repositories;
  383. /**
  384. * The product configurations.
  385. */
  386. @Parameter
  387. private List<Product> products = new ArrayList<>();
  388. /**
  389. * A map of {instanceId -> Product}, initialized by {@link #createProductContexts()}.
  390. * Cannot be set by the user.
  391. */
  392. private Map<String, Product> productMap;
  393. /**
  394. * The file to which the container's log output will be sent.
  395. */
  396. @Parameter
  397. private String output;
  398. /**
  399. * Comma-delimited list of directories containing plugin resources. See the
  400. * {@code AlternativeDirectoryResourceLoader} class in the {@code atlassian-plugins} project for details.
  401. */
  402. @Parameter(property = "additional.resource.folders")
  403. private String additionalResourceFolders;
  404. /**
  405. * Start the products in parallel (TestGroups).
  406. */
  407. @Parameter(property = "parallel", defaultValue = "false")
  408. protected boolean parallel;
  409. /**
  410. * Whether to wait for full initialization of the product, e.g. for the plugin system to be up.
  411. */
  412. @Parameter(property = "await.full.initialization", defaultValue = "true")
  413. private boolean awaitFullInitialization;
  414. /**
  415. * A license to override any provided by the product data file.
  416. *
  417. * @since 8.2
  418. */
  419. @Parameter(property = "product.license")
  420. private String productLicense;
  421. // -------------------------------------- Methods -------------------------------------
  422. @Override
  423. public final void execute() throws MojoExecutionException, MojoFailureException {
  424. pluginArtifacts.addAll(toArtifacts(pluginArtifactsString));
  425. libArtifacts.addAll(toArtifacts(libArtifactsString));
  426. bundledArtifacts.addAll(toArtifacts(bundledArtifactsString));
  427. systemPropertyVariables.putAll((Map) systemProperties);
  428. doExecute();
  429. }
  430. protected abstract void doExecute() throws MojoExecutionException, MojoFailureException;
  431. // Returns a Product containing the properties of this Mojo, i.e. the top-level AMPS <configuration>
  432. private Product createDefaultProductContext() throws MojoExecutionException {
  433. final Product product = new Product();
  434. product.setId(getProductId());
  435. product.setCustomContainerArtifact(customContainerArtifact);
  436. product.setContainerId(containerId);
  437. product.setServer(server);
  438. product.setContextPath(contextPath);
  439. product.setJvmArgs(jvmArgs);
  440. product.setStartupTimeout(startupTimeout);
  441. product.setShutdownTimeout(shutdownTimeout);
  442. product.setNodes(nodes);
  443. // If they aren't defined, define those system properties. They will override the product
  444. // handler's properties.
  445. final Map<String, Object> properties = new HashMap<>(systemPropertyVariables);
  446. properties.put("atlassian.sdk.version", getAmpsPluginVersion());
  447. putDefaultedSystemProperty(properties, "atlassian.dev.mode", "true");
  448. putDefaultedSystemProperty(properties, "atlassian.allow.insecure.url.parameter.login", "true");
  449. putDefaultedSystemProperty(properties, "java.awt.headless", "true");
  450. putDefaultedSystemProperty(properties, "plugin.resource.directories", getResourceDirs());
  451. putDefaultedSystemProperty(properties, "plugin.root.directories", buildRootProperty());
  452. product.setSystemPropertyVariables(properties);
  453. product.setBundledArtifacts(bundledArtifacts);
  454. product.setLibArtifacts(libArtifacts);
  455. product.setApplications(applications);
  456. product.setPluginArtifacts(pluginArtifacts);
  457. product.setLog4jProperties(log4jProperties);
  458. product.setHttpPort(httpPort);
  459. product.setAjpPort(ajpPort);
  460. // HTTPS settings to pass via cargo to tomcat
  461. product.setUseHttps(useHttps);
  462. product.setHttpsPort(httpsPort);
  463. product.setHttpsClientAuth(httpsClientAuth);
  464. product.setHttpsSSLProtocol(httpsSslProtocol);
  465. product.setHttpsKeystoreFile(httpsKeystoreFile);
  466. product.setHttpsKeystorePass(httpsKeystorePass);
  467. product.setHttpsKeyAlias(httpsKeyAlias);
  468. product.setHttpsHttpSecure(httpsHttpSecure);
  469. product.setVersion(productVersion);
  470. product.setDataVersion(productDataVersion);
  471. product.setDataPath(productDataPath);
  472. product.setDataOverridesPath(productDataOverridesPath);
  473. product.setLicense(productLicense);
  474. // continue to have these work for now
  475. product.setRestVersion(restVersion);
  476. product.setSalVersion(salVersion);
  477. product.setAwaitFullInitialization(awaitFullInitialization);
  478. product.setDevToolboxVersion(devToolboxVersion);
  479. product.setEnableDevToolbox(enableDevToolbox);
  480. product.setEnablePde(enablePde);
  481. product.setEnablePluginViewer(enablePluginViewer);
  482. product.setEnableQuickReload(enableQuickReload);
  483. product.setPdeVersion(pdeVersion);
  484. product.setPdkVersion(pdkVersion);
  485. product.setPluginViewerVersion(pluginViewerVersion);
  486. product.setQuickReloadVersion(quickReloadVersion);
  487. product.setWebConsoleVersion(webConsoleVersion);
  488. if (dataSource != null) {
  489. product.setDataSources(singletonList(dataSource));
  490. }
  491. return product;
  492. }
  493. /**
  494. * Returns a comma-separated list of resource directories for on-the-fly reloading.
  495. * If a test plugin is detected, the test resource directories are included as well.
  496. *
  497. * @return see description
  498. */
  499. private String getResourceDirs() {
  500. final List<String> resourceDirs = new ArrayList<>();
  501. // Additional resource folders
  502. final String[] additionalResourceDirs = split(trimToEmpty(additionalResourceFolders), ",");
  503. for (final String resourceDir : additionalResourceDirs) {
  504. final File dir = new File(resourceDir);
  505. if (dir.exists()) {
  506. resourceDirs.add(trim(resourceDir));
  507. }
  508. }
  509. final MavenProject mavenProject = getMavenContext().getProject();
  510. // Project resource directories
  511. for (final Resource resource : mavenProject.getResources()) {
  512. final File resourceDir = new File(resource.getDirectory());
  513. if (resourceDir.exists()) {
  514. resourceDirs.add(resource.getDirectory());
  515. }
  516. }
  517. // Test resource directories
  518. if (shouldDeployTestJar(getMavenContext())) {
  519. for (final Resource resource : mavenProject.getTestResources()) {
  520. resourceDirs.add(resource.getDirectory());
  521. }
  522. }
  523. return join(",", resourceDirs);
  524. }
  525. /**
  526. * @return the path of the project root, for the <tt>plugin.root.directories</tt> system property.
  527. * @since 3.6
  528. */
  529. private String buildRootProperty() {
  530. return Optional.of(getMavenContext())
  531. .map(MavenContext::getProject)
  532. .map(MavenProject::getBasedir)
  533. .map(File::getPath)
  534. .orElse("");
  535. }
  536. /**
  537. * If the given map doesn't contain the given key, this method puts an entry for that key, where the value is the
  538. * value of that key as a system property, defaulted to the given value.
  539. *
  540. * @param map the map to populate
  541. * @param key the key to add if necessary
  542. * @param defaultValue the value to add if the system property with that key has no value
  543. */
  544. private static void putDefaultedSystemProperty(
  545. final Map<String, Object> map, final String key, final String defaultValue) {
  546. map.computeIfAbsent(key, k -> System.getProperty(k, defaultValue));
  547. }
  548. /**
  549. * Sets the default values of the given product.
  550. *
  551. * @param product the product whose default values are to be set
  552. */
  553. private void setDefaultValues(final Product product) {
  554. final ProductHandler handler = getProductHandler(product.getId());
  555. //Apply the common default values
  556. product.setDataVersion(System.getProperty("product.data.version", product.getDataVersion()));
  557. product.setVersion(System.getProperty("product.version", product.getVersion()));
  558. product.setDataPath(System.getProperty("product.data.path", product.getDataPath()));
  559. product.setInstanceId(getProductInstanceId(product));
  560. product.setArtifactRetriever(new ArtifactRetriever(
  561. artifactResolver, repositorySystem, localRepository, repositories, repositoryMetadataManager));
  562. if (containerIdNotDefinedOrProductSpecific(product)) {
  563. try {
  564. product.setContainerId(handler.getDefaultContainerId(product));
  565. } catch (MojoExecutionException e) {
  566. product.setContainerId(handler.getDefaultContainerId());
  567. }
  568. product.setContainerNotSpecified(true);
  569. }
  570. if (product.getServer() == null) {
  571. product.setServer(DEFAULT_SERVER);
  572. }
  573. if (product.getPdkVersion() == null) {
  574. product.setPdkVersion(DEFAULT_PDK_VERSION);
  575. }
  576. if (product.getWebConsoleVersion() == null) {
  577. product.setWebConsoleVersion(DEFAULT_WEB_CONSOLE_VERSION);
  578. }
  579. if (product.isEnableDevToolbox() == null) {
  580. product.setEnableDevToolbox(true);
  581. }
  582. if (product.getDevToolboxVersion() == null) {
  583. product.setDevToolboxVersion(DEFAULT_DEV_TOOLBOX_VERSION);
  584. }
  585. if (product.isEnableQuickReload() == null) {
  586. product.setEnableQuickReload(false);
  587. }
  588. if (product.getQuickReloadVersion() == null) {
  589. product.setQuickReloadVersion(DEFAULT_QUICK_RELOAD_VERSION);
  590. }
  591. if (product.isEnablePluginViewer() == null) {
  592. product.setEnablePluginViewer(false);
  593. }
  594. if (product.getPluginViewerVersion() == null) {
  595. product.setPluginViewerVersion(DEFAULT_PLUGIN_VIEWER_VERSION);
  596. }
  597. if (product.getPdeVersion() == null) {
  598. product.setPdeVersion(DEFAULT_PDE_VERSION);
  599. }
  600. if (product.getOutput() == null) {
  601. product.setOutput(output);
  602. }
  603. if (product.getStartupTimeout() <= 0) {
  604. product.setStartupTimeout(DEFAULT_PRODUCT_STARTUP_TIMEOUT);
  605. }
  606. if (product.getShutdownTimeout() <= 0) {
  607. product.setShutdownTimeout(DEFAULT_PRODUCT_SHUTDOWN_TIMEOUT);
  608. }
  609. if (product.getHttpPort() == 0) {
  610. product.setHttpPort(handler.getDefaultHttpPort());
  611. }
  612. if (product.getUseHttps() == null) {
  613. product.setUseHttps(false);
  614. }
  615. if (product.getHttpsPort() == 0) {
  616. product.setHttpsPort(handler.getDefaultHttpsPort());
  617. }
  618. if (product.getHttpsClientAuth() == null) {
  619. product.setHttpsClientAuth(DEFAULT_HTTPS_CLIENTAUTH);
  620. }
  621. if (product.getHttpsSSLProtocol() == null) {
  622. product.setHttpsSSLProtocol(DEFAULT_HTTPS_SSL_PROTOCOL);
  623. }
  624. if (product.getHttpsKeystoreFile() == null) {
  625. product.setHttpsKeystoreFile(DEFAULT_HTTPS_KEYSTOREFILE);
  626. }
  627. if (product.getHttpsKeystorePass() == null) {
  628. product.setHttpsKeystorePass(DEFAULT_HTTPS_KEYSTOREPASS);
  629. }
  630. if (product.getHttpsKeyAlias() == null) {
  631. product.setHttpsKeyAlias(DEFAULT_HTTPS_KEYALIAS);
  632. }
  633. if (product.getHttpsHttpSecure() == null) {
  634. product.setHttpsHttpSecure(Boolean.parseBoolean(DEFAULT_HTTP_SECURE));
  635. }
  636. if (product.getVersion() == null) {
  637. product.setVersion("RELEASE");
  638. }
  639. if (product.getDataVersion() == null) {
  640. // Default the productDataVersion to match the productVersion. Defaulting to LATEST
  641. // is bad because there is no guarantee that a snapshots let alone a more recent
  642. // version of a product's data is compatible with an earlier version of the product or
  643. // that a product is required to provide a 'downgrade' task. Developers can still
  644. // specify LATEST explicitly
  645. product.setDataVersion(product.getVersion());
  646. }
  647. if (product.getContextPath() == null) {
  648. product.setContextPath(handler.getDefaultContextPath());
  649. }
  650. if (product.getDataSources() == null) {
  651. product.setDataSources(new ArrayList<>());
  652. }
  653. product.initialiseNodes();
  654. }
  655. private static boolean containerIdNotDefinedOrProductSpecific(final Product product) {
  656. return product.getContainerId() == null || isProductSpecificContainerId(product);
  657. }
  658. private static boolean isProductSpecificContainerId(final Product product) {
  659. return "productSpecific".toUpperCase(ENGLISH).equals(product.getContainerId().toUpperCase(ENGLISH));
  660. }
  661. /**
  662. * Returns the plugins required by the {@code atlassian-plugins-osgi-testrunner} framework.
  663. *
  664. * @return an unmodifiable list
  665. */
  666. @Nonnull
  667. public final List<ProductArtifact> getTestFrameworkPlugins() {
  668. if (testFrameworkPlugins.isEmpty()) {
  669. // JUnit OSGi bundle
  670. final String jUnitVersion =
  671. getMavenContext().getVersionOverrides().getProperty(JUNIT_ARTIFACT_ID, JUNIT_VERSION);
  672. testFrameworkPlugins.add(new ProductArtifact(JUNIT_GROUP_ID, JUNIT_ARTIFACT_ID, jUnitVersion));
  673. // Test Runner Plugin
  674. testFrameworkPlugins.add(new ProductArtifact(
  675. TESTRUNNER_GROUP_ID, TESTRUNNER_BUNDLE_ARTIFACT_ID, getTestRunnerVersion()));
  676. }
  677. return unmodifiableList(testFrameworkPlugins);
  678. }
  679. /**
  680. * Returns the version of the Atlassian Plugins Test Runner Plugin to load. The version is derived as follows:
  681. * <ol>
  682. * <li>If the {@link MavenContext#getVersionOverrides()} specify a version, use that, otherwise</li>
  683. * <li>if the project has a dependency upon {@code com.atlassian.plugins:atlassian-plugins-osgi-testrunner},
  684. * which it may do in order to use {@link com.atlassian.plugins.osgi.test.AtlassianPluginsTestRunner}, then
  685. * use the version of that, otherwise</li>
  686. * <li>use version {@value #ATLASSIAN_TEST_RUNNER_VERSION}.</li>
  687. * </ol>
  688. *
  689. * @return a non-blank version
  690. * @see MavenContext#getVersionOverrides()
  691. */
  692. private String getTestRunnerVersion() {
  693. final MavenContext mavenContext = getMavenContext();
  694. final Properties overrides = mavenContext.getVersionOverrides();
  695. final Artifact testRunnerDependency =
  696. getReactorArtifact(mavenContext, TESTRUNNER_GROUP_ID, TESTRUNNER_ARTIFACT_ID);
  697. final String testRunnerVersion = Optional.ofNullable(testRunnerDependency)
  698. .map(Artifact::getVersion)
  699. .orElse(ATLASSIAN_TEST_RUNNER_VERSION);
  700. return overrides.getProperty(TESTRUNNER_BUNDLE_ARTIFACT_ID, testRunnerVersion);
  701. }
  702. /**
  703. * Builds the map {instanceId -> Product}, based on:
  704. * <ul>
  705. * <li>the {@literal <products>} tag</li>
  706. * <li>the configuration values inherited from the {@literal <configuration>} tag
  707. * </ul>
  708. *
  709. * @throws MojoExecutionException if something goes wrong
  710. */
  711. private Map<String, Product> createProductContexts() throws MojoExecutionException {
  712. final Map<String, Product> productContexts = new HashMap<>();
  713. // Products in the <products> tag inherit from the upper settings, e.g. when there's an <httpPort> tag for all products
  714. makeProductsInheritDefaultConfiguration(products, productContexts);
  715. productContexts.values().forEach(this::setDefaultValues);
  716. return productContexts;
  717. }
  718. /**
  719. * Returns the map { instanceId -> Product } with initialized values.
  720. */
  721. protected final Map<String, Product> getProductContexts() throws MojoExecutionException {
  722. if (productMap == null) {
  723. productMap = createProductContexts();
  724. }
  725. return productMap;
  726. }
  727. /**
  728. * Puts the list of {@literal <products>} in productMap:
  729. * <ul>
  730. * <li>The {@literal <product>} from the amps-maven-plugin configuration (if missing, RefApp is used)</li>
  731. * <li>The {@literal <products>} from the amps-maven-plugin configuration</li>
  732. * </ul>
  733. */
  734. @VisibleForTesting
  735. final void makeProductsInheritDefaultConfiguration(
  736. final List<Product> products, final Map<String, Product> productMap)
  737. throws MojoExecutionException {
  738. final Product defaultProduct = createDefaultProductContext();
  739. productMap.put(getProductId(), defaultProduct);
  740. for (final Product product : products) {
  741. final Product processedProduct = product.merge(defaultProduct);
  742. final String instanceId = getProductInstanceId(processedProduct);
  743. productMap.put(instanceId, processedProduct);
  744. }
  745. }
  746. private static String getProductInstanceId(final Product processedProduct) {
  747. return processedProduct.getInstanceId() == null ? processedProduct.getId() : processedProduct.getInstanceId();
  748. }
  749. /**
  750. * Attempts to stop the given products one by one, within the product-level timeout.
  751. */
  752. protected final void stopProducts(final Collection<Product> products) throws MojoExecutionException {
  753. final ExecutorService executor = newFixedThreadPool(products.size());
  754. try {
  755. final long before = System.nanoTime();
  756. final List<Product> reversed = new ArrayList<>(products);
  757. reverse(reversed);
  758. for (final Product product : reversed) {
  759. shutDown(product, executor);
  760. }
  761. final long after = System.nanoTime();
  762. getLog().info("amps:stop in " + NANOSECONDS.toSeconds(after - before) + "s");
  763. } catch (final InterruptedException e) {
  764. currentThread().interrupt();
  765. } catch (ExecutionException e) {
  766. throw new MojoExecutionException("Exception while stopping the products", e);
  767. }
  768. // If products were launched in parallel, check they are stopped: Cargo returns before products are down
  769. if (parallel) {
  770. waitForProducts(products, false);
  771. }
  772. }
  773. private void shutDown(final Product product, final ExecutorService executor)
  774. throws InterruptedException, ExecutionException {
  775. // Shut down the product, waiting no longer than product.getShutdownTimeout() milliseconds
  776. final Future<?> task = executor.submit(() -> {
  777. getLog().info(product.getInstanceId() + ": Shutting down");
  778. try {
  779. getProductHandler(product.getId()).stop(product);
  780. } catch (MojoExecutionException e) {
  781. getLog().error("Exception while trying to stop " + product.getInstanceId(), e);
  782. }
  783. });
  784. try {
  785. task.get(product.getShutdownTimeout(), MILLISECONDS);
  786. } catch (TimeoutException e) {
  787. getLog().info(product.getInstanceId() + " shutdown: Didn't return in time");
  788. task.cancel(true);
  789. }
  790. }
  791. /**
  792. * Waits until all products are running or stopped
  793. *
  794. * @param startingUp true if starting up the products, false if shutting down.
  795. */
  796. protected final void waitForProducts(final Collection<Product> products, final boolean startingUp)
  797. throws MojoExecutionException {
  798. for (final Product product : products) {
  799. for (final Node node : product.getNodes()) {
  800. awaitStateChange(product, node, startingUp, getLog());
  801. }
  802. }
  803. }
  804. /**
  805. * Applies this mojo's {@code parallel} configuration to the given products.
  806. *
  807. * @param products the products to mutate
  808. */
  809. protected final void setParallelMode(final Collection<Product> products) {
  810. products.forEach(product -> product.setSynchronicity(parallel));
  811. }
  812. }