PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/atlassian/amps
Java | 1464 lines | 665 code | 216 blank | 583 comment | 81 complexity | fd092d1e653b8db1c59743fa0d930e2e 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.util.ArtifactRetriever;
  3. import com.atlassian.maven.plugins.amps.util.MapUtils;
  4. import com.google.common.annotations.VisibleForTesting;
  5. import org.apache.commons.lang3.StringUtils;
  6. import org.apache.maven.plugin.logging.Log;
  7. import javax.annotation.Nonnull;
  8. import javax.annotation.Nullable;
  9. import java.io.File;
  10. import java.net.URI;
  11. import java.util.ArrayList;
  12. import java.util.Collection;
  13. import java.util.HashMap;
  14. import java.util.List;
  15. import java.util.Map;
  16. import java.util.Objects;
  17. import java.util.Optional;
  18. import java.util.Properties;
  19. import java.util.function.Function;
  20. import java.util.function.Supplier;
  21. import static com.atlassian.maven.plugins.amps.product.manager.BaseUrlUtils.getBaseUrl;
  22. import static java.lang.String.format;
  23. import static java.util.Collections.singletonList;
  24. import static java.util.Collections.unmodifiableList;
  25. import static java.util.Optional.empty;
  26. import static org.apache.commons.lang3.StringUtils.defaultString;
  27. import static org.apache.commons.lang3.StringUtils.isNotBlank;
  28. import static org.apache.commons.lang3.StringUtils.remove;
  29. import static org.apache.commons.lang3.StringUtils.stripToNull;
  30. import static org.apache.commons.lang3.StringUtils.trimToNull;
  31. /**
  32. * A software product that AMPS can start.
  33. */
  34. public class Product {
  35. /*
  36. To add a property to this class:
  37. - add the private field; must be of a type that Maven can unmarshal from the XML in the user's AMPS config
  38. - add a getter and setter for it
  39. - update the merge(Product) method to use the given Product's value for this field, if necessary
  40. - add a similar field to the AbstractProductHandlerMojo, since users can also configure the product there
  41. - annotate that field with @Parameter and configure it with a system property if applicable
  42. - add a line to AbstractProductHandlerMojo#createDefaultProductContext, to copy that new mojo field to the Product
  43. */
  44. /**
  45. * Container artifact to run in if containerId is not specified or containerId is equals to "customContainerArtifact"
  46. * It has format groupId:artifactId:version[:packaging][:classifier].
  47. */
  48. protected String customContainerArtifact;
  49. /**
  50. * Id of container to run in
  51. */
  52. protected String containerId;
  53. /**
  54. * Helper field set by AMPS when containerId in not provided in plugin pom or when containerId equals "productSpecified"
  55. * It used to decide if AMPS should use container artifact defined in customContainerArtifact field or not
  56. * Introduced to keep backward compatibility
  57. */
  58. protected boolean containerNotSpecified;
  59. /**
  60. * HTTP port for the servlet containers
  61. */
  62. private int httpPort = 0;
  63. /**
  64. * RMI port, for Tomcat this is port used to send shutdown message
  65. */
  66. protected int rmiPort = 0;
  67. /**
  68. * if we should start with https on port 443
  69. */
  70. private Boolean useHttps;
  71. /**
  72. * the HTTPS port to use.
  73. *
  74. * @since 5.0.4
  75. */
  76. private int httpsPort;
  77. /**
  78. * The SSL certificate chain option.
  79. *
  80. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration">Tomcat SSL HOWTO</a>
  81. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support">Tomcat SSL Support</a>
  82. * @since 5.0.4
  83. */
  84. private String httpsClientAuth;
  85. /**
  86. * The SSL protocols to use.
  87. *
  88. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration">Tomcat SSL HOWTO</a>
  89. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support">Tomcat SSL Support</a>
  90. * @since 5.0.4
  91. */
  92. private String httpsSslProtocol;
  93. /**
  94. * The pathname of the keystore file.
  95. *
  96. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration">Tomcat SSL HOWTO</a>
  97. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support">Tomcat SSL Support</a>
  98. * @since 5.0.4
  99. */
  100. private String httpsKeystoreFile;
  101. /**
  102. * The password of the keystore file.
  103. *
  104. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration">Tomcat SSL HOWTO</a>
  105. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support">Tomcat SSL Support</a>
  106. * @since 5.0.4
  107. */
  108. private String httpsKeystorePass;
  109. /**
  110. * The alias of the certificate to use.
  111. *
  112. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Configuration">Tomcat SSL HOWTO</a>
  113. * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support">Tomcat SSL Support</a>
  114. * @since 5.0.4
  115. */
  116. private String httpsKeyAlias;
  117. /**
  118. * Cargo httpSecure flag
  119. *
  120. * @see <a href="http://svn.codehaus.org/cargo/core/trunk/containers/tomcat/src/main/java/org/codehaus/cargo/container/tomcat/TomcatPropertySet.java">Cargo Tomcat Properties</a>
  121. * @since 5.0.4
  122. */
  123. private Boolean httpsHttpSecure;
  124. /**
  125. * Application context path, in the format: /context-path
  126. */
  127. protected String contextPath;
  128. /**
  129. * Application server
  130. */
  131. protected String server;
  132. /**
  133. * Webapp version
  134. */
  135. protected String version;
  136. /**
  137. * JVM arguments to pass to Cargo
  138. */
  139. protected String jvmArgs = "";
  140. /**
  141. * Debug arguments to pass to Cargo as JVM arguments
  142. */
  143. protected String debugArgs = "";
  144. /**
  145. * A log4j properties file
  146. */
  147. protected File log4jProperties;
  148. /**
  149. * The test resources version
  150. */
  151. protected String productDataVersion;
  152. /**
  153. * The path to a custom test resources zip or a directory. Takes precedence over dataVersion.
  154. * The data from this path will be copied into the home directory.
  155. */
  156. protected String productDataPath = "";
  157. /**
  158. * The path to the product's home directory. Takes precedence over dataPath.
  159. * The data from this path will be used directly (read/write) by the product.
  160. */
  161. protected String dataHome = "";
  162. /**
  163. * The path to a zip or a directory to use for home directory overrides. Takes precedence
  164. * over src/test/resources/{@link #instanceId}-home.
  165. */
  166. protected String dataOverridesPath = "";
  167. /**
  168. *
  169. */
  170. private List<Application> applications = new ArrayList<>();
  171. /**
  172. *
  173. */
  174. private List<ProductArtifact> pluginArtifacts = new ArrayList<>();
  175. /**
  176. *
  177. */
  178. private List<ProductArtifact> libArtifacts = new ArrayList<>();
  179. /**
  180. *
  181. */
  182. private List<ProductArtifact> bundledArtifacts = new ArrayList<>();
  183. /**
  184. * SAL version
  185. */
  186. private String salVersion;
  187. /**
  188. * Atlassian Plugin Development Kit (PDK) version
  189. */
  190. private String pdkVersion;
  191. /**
  192. * Atlassian REST module version
  193. */
  194. private String restVersion;
  195. /**
  196. * Version of the Felix OSGi web console
  197. */
  198. private String webConsoleVersion;
  199. /**
  200. * Flag to indicate whether or not to enable automatic bundling of DevToolbox.
  201. */
  202. private Boolean enableDevToolbox;
  203. /**
  204. * Version of the Developer Toolbox plugin
  205. */
  206. private String devToolboxVersion;
  207. /**
  208. * Should QuickReload be enabled.
  209. */
  210. private Boolean enableQuickReload;
  211. /**
  212. * Version of QuickReload.
  213. */
  214. private String quickReloadVersion;
  215. /**
  216. * If PluginViewer should be used.
  217. */
  218. private Boolean enablePluginViewer;
  219. /**
  220. * PluginViewer version should be used.
  221. */
  222. private String pluginViewerVersion;
  223. /**
  224. * Flag to indicate whether or not to enable automatic bundling of PDE.
  225. */
  226. private Boolean enablePde;
  227. /**
  228. * Version of the PDE plugin
  229. */
  230. private String pdeVersion;
  231. /**
  232. * Product id - nickname of the product to run
  233. */
  234. protected String id;
  235. /**
  236. * The name of the instance of the product
  237. */
  238. protected String instanceId;
  239. private ArtifactRetriever artifactRetriever;
  240. /**
  241. * Flag to indicate whether or not to install the plugin
  242. */
  243. private Boolean installPlugin;
  244. /**
  245. * The system properties to set for the product
  246. */
  247. private Map<String, Object> systemProperties = new HashMap<>();
  248. /**
  249. * The Cargo XML overrides for the product
  250. */
  251. private Collection<XmlOverride> cargoXmlOverrides;
  252. /**
  253. * File the container should log to.
  254. */
  255. private String output;
  256. /**
  257. * Port for debugging
  258. */
  259. private int jvmDebugPort;
  260. /**
  261. * How long to wait for product startup, in milliseconds; if not specified, default is determined by AbstractProductHandlerMojo
  262. */
  263. private int startupTimeout;
  264. /**
  265. * How long to wait for product shutdown, in milliseconds; if not specified, default is determined by AbstractProductHandlerMojo
  266. */
  267. private int shutdownTimeout;
  268. /**
  269. * Waits until the application is up before proceeding to the next one (blocking call).<ul>
  270. * <li>If -Dparallel is not specified, default is TRUE for all products.</li>
  271. * <li>If -Dparallel is specified, default is FALSE except for FeCru.</li>
  272. * <li>The pom.xml overrides the default values.</li>
  273. * <li>Use -Dparallel to start products in parallel. {@code AbstractProductHandlerMojo#setParallelMode} sets the
  274. * default values according to this parameter.</li>
  275. * </ul>
  276. */
  277. private Boolean synchronousStartup;
  278. /**
  279. * Waits until product is fully started - this is added for Jira - Jira new asynchronous start -
  280. * Jira is performing minimal initialization and then whole plugin system startup is performed in background.<br>
  281. * This can interfere with integration tests - AMPS starts them after minimal initialization and some may crash as
  282. * plugin system is not fully started (if they do not perform check if jira is fully started).<br>
  283. * This flag prevents such situation and forces product to perform full initialization before continuing to next
  284. * step.
  285. */
  286. private Boolean awaitFullInitialization;
  287. /**
  288. * An optional override of the webapp's groupId
  289. */
  290. private String groupId;
  291. /**
  292. * An optional override of the webapp's artifactId
  293. */
  294. private String artifactId;
  295. /**
  296. * Registers a JNDI datasource using cargo.datasource.datasource.
  297. * <ul>
  298. * <li>Default values depend on the product.</li>
  299. * <li>Default values will be applied to the first datasource if its definition is incomplete.</li>
  300. * <li>Only Jira has a datasource by default, and they use HSQL or H2.</li>
  301. * <li>Other products can use datasources if you configure them this way during the setup process (Requires to
  302. * start with an empty data home).</li>
  303. * </ul>
  304. * Example:
  305. * <pre>{@code
  306. * <products>
  307. * <product>
  308. * <id>jira</id>
  309. * <instanceId>jira50</instanceId>
  310. * <version>5.0</version>
  311. * <dataVersion>5.0</dataVersion>
  312. * <dataSources>
  313. * <dataSource>
  314. * <jndi>jdbc/JiraDS</jndi>
  315. * <url>jdbc:postgresql://localhost:5432/jira</url>
  316. * <driver>org.postgresql.jdbcDriver</driver>
  317. * <username>jira</username>
  318. * <password>jira</password>
  319. * <libArtifacts>
  320. * <libArtifact>
  321. * <groupId>postgresql</groupId>
  322. * <artifactId>postgresql</artifactId>
  323. * <version>9.1-901-1.jdbc4</version>
  324. * </libArtifact>
  325. * </libArtifacts>
  326. * </dataSource>
  327. * </dataSources>
  328. * </product>
  329. * </products>
  330. * }
  331. * </pre>
  332. */
  333. private List<DataSource> dataSources;
  334. // The home directory shared between multiple instances in a cluster (added for Jira)
  335. private String sharedHome;
  336. // The port for the Apache JServ Protocol; defaults to the web container's default value
  337. private int ajpPort;
  338. // The replacement license string (if any) configured by the user
  339. private String license;
  340. // The nodes of this product instance (for backward compatibility, empty implies single-node operation)
  341. // To get a non-null, non-empty list, call getNodes() instead of accessing this field directly.
  342. private List<Node> nodes;
  343. /**
  344. * Merges {@code this} product with the given one, with {@code this} product's values taking precedence.
  345. *
  346. * @param product the product to merge with; it is not modified
  347. * @return a new product; {@code this} product is not modified
  348. */
  349. public Product merge(final Product product) {
  350. final Product prod = new Product();
  351. prod.setOutput(output == null ? product.getOutput() : output);
  352. prod.setSystemPropertyVariables(MapUtils.merge(product.getSystemPropertyVariables(), systemProperties));
  353. prod.setInstallPlugin(installPlugin == null ? product.isInstallPlugin() : installPlugin);
  354. prod.setArtifactRetriever(artifactRetriever == null ? product.getArtifactRetriever() : artifactRetriever);
  355. prod.setId(id == null ? product.getId() : id);
  356. prod.setInstanceId(instanceId == null ? product.getInstanceId() : instanceId);
  357. prod.setWebConsoleVersion(webConsoleVersion == null ? product.getWebConsoleVersion() : webConsoleVersion);
  358. prod.setEnableDevToolbox(enableDevToolbox == null ? product.isEnableDevToolbox() : enableDevToolbox);
  359. prod.setDevToolboxVersion(devToolboxVersion == null ? product.getDevToolboxVersion() : devToolboxVersion);
  360. prod.setEnableQuickReload(enableQuickReload == null ? product.isEnableQuickReload() : enableQuickReload);
  361. prod.setQuickReloadVersion(quickReloadVersion == null ? product.getQuickReloadVersion() : quickReloadVersion);
  362. prod.setEnablePluginViewer(enablePluginViewer == null ? product.isEnablePluginViewer() : enablePluginViewer);
  363. prod.setPluginViewerVersion(pluginViewerVersion == null ? product.getPluginViewerVersion() : pluginViewerVersion);
  364. prod.setEnablePde(enablePde == null ? product.isEnablePde() : enablePde);
  365. prod.setPdeVersion(pdeVersion == null ? product.getPdeVersion() : pdeVersion);
  366. prod.setRestVersion(restVersion == null ? product.getRestVersion() : restVersion);
  367. prod.setPdkVersion(pdkVersion == null ? product.getPdkVersion() : pdkVersion);
  368. prod.setSalVersion(salVersion == null ? product.getSalVersion() : salVersion);
  369. prod.setBundledArtifacts(bundledArtifacts.isEmpty() ? product.getBundledArtifacts() : bundledArtifacts);
  370. prod.setPluginArtifacts(pluginArtifacts.isEmpty() ? product.getPluginArtifacts() : pluginArtifacts);
  371. prod.setLibArtifacts(libArtifacts.isEmpty() ? product.getLibArtifacts() : libArtifacts);
  372. prod.setApplications(applications.isEmpty() ? product.getApplications() : applications);
  373. prod.setDataPath(StringUtils.isBlank(productDataPath) ? product.getDataPath() : productDataPath);
  374. prod.setDataVersion(productDataVersion == null ? product.getDataVersion() : productDataVersion);
  375. prod.setDataHome(dataHome == null ? product.getDataHome() : dataHome);
  376. prod.setDataOverridesPath(dataOverridesPath == null ? product.getDataOverridesPath() : dataOverridesPath);
  377. prod.setLog4jProperties(log4jProperties == null ? product.getLog4jProperties() : log4jProperties);
  378. prod.setJvmArgs(stripToNull(jvmArgs) == null ? product.getJvmArgs() : jvmArgs);
  379. prod.setDebugArgs(stripToNull(debugArgs) == null ? product.getDebugArgs() : debugArgs);
  380. prod.setDataSources(dataSources == null ? product.getDataSources() : dataSources);
  381. prod.setGroupId(groupId == null ? product.getGroupId() : groupId);
  382. prod.setArtifactId(artifactId == null ? product.getArtifactId() : artifactId);
  383. prod.setVersion(version == null ? product.getVersion() : version);
  384. prod.setServer(server == null ? product.getServer() : server);
  385. prod.setContextPath(contextPath == null ? product.getContextPath() : contextPath);
  386. prod.setCustomContainerArtifact(customContainerArtifact == null ? product.getCustomContainerArtifact() : customContainerArtifact);
  387. prod.setContainerId(containerId == null ? product.getContainerId() : containerId);
  388. prod.setContainerNotSpecified(containerNotSpecified && product.isContainerNotSpecified());
  389. prod.setRmiPort(rmiPort == 0 ? product.getRmiPort() : rmiPort);
  390. prod.setHttpPort(httpPort == 0 ? product.getHttpPort() : httpPort);
  391. prod.setAjpPort(ajpPort == 0 ? product.getAjpPort() : ajpPort);
  392. prod.setJvmDebugPort(jvmDebugPort == 0 ? product.getJvmDebugPort() : jvmDebugPort);
  393. prod.setUseHttps(useHttps == null ? product.getUseHttps() : useHttps);
  394. prod.setStartupTimeout(startupTimeout == 0 ? product.getStartupTimeout() : startupTimeout);
  395. prod.setShutdownTimeout(shutdownTimeout == 0 ? product.getShutdownTimeout() : shutdownTimeout);
  396. prod.setSynchronousStartup(synchronousStartup == null ? product.getSynchronousStartup() : synchronousStartup);
  397. prod.setSharedHome(sharedHome == null ? product.getSharedHome() : sharedHome);
  398. prod.setCargoXmlOverrides(cargoXmlOverrides == null ? product.getCargoXmlOverrides() : cargoXmlOverrides);
  399. // https related properties
  400. prod.setHttpsPort(httpsPort == 0 ? product.getHttpsPort() : httpsPort);
  401. prod.setHttpsClientAuth(httpsClientAuth == null ? product.getHttpsClientAuth() : httpsClientAuth);
  402. prod.setHttpsSSLProtocol(httpsSslProtocol == null ? product.getHttpsSSLProtocol() : httpsSslProtocol);
  403. prod.setHttpsKeystoreFile(httpsKeystoreFile == null ? product.getHttpsKeystoreFile() : httpsKeystoreFile);
  404. prod.setHttpsKeystorePass(httpsKeystorePass == null ? product.getHttpsKeystorePass() : httpsKeystorePass);
  405. prod.setHttpsKeyAlias(httpsKeyAlias == null ? product.getHttpsKeyAlias() : httpsKeyAlias);
  406. prod.setHttpsHttpSecure(httpsHttpSecure == null ? product.getHttpsHttpSecure() : httpsHttpSecure);
  407. prod.setAwaitFullInitialization(
  408. awaitFullInitialization == null ? product.isAwaitFullInitialization() : awaitFullInitialization);
  409. prod.setLicense(license == null ? product.getLicense() : license);
  410. prod.setNodes(nodes == null ? product.nodes : nodes);
  411. return prod;
  412. }
  413. void setLicense(final String license) {
  414. this.license = license;
  415. }
  416. public String getCustomContainerArtifact() {
  417. return customContainerArtifact;
  418. }
  419. public void setCustomContainerArtifact(String customContainerArtifact) {
  420. this.customContainerArtifact = customContainerArtifact;
  421. }
  422. public String getContainerId() {
  423. return containerId;
  424. }
  425. public void setContainerId(String containerId) {
  426. this.containerId = containerId;
  427. }
  428. public String getServer() {
  429. return server;
  430. }
  431. public void setServer(String server) {
  432. this.server = server;
  433. }
  434. /**
  435. * Returns the user-configured HTTP port. Has package-private access because in reality, a DC product's nodes will
  436. * each use a separate HTTP port, and our code should call {@link Node#getWebPort()} instead, which also respects
  437. * the users wishes with regard to using HTTPS. This getter is only for finding out what port the user configured at
  438. * the product level.
  439. *
  440. * @return see above
  441. */
  442. int getHttpPort() {
  443. return httpPort;
  444. }
  445. void setHttpPort(int httpPort) {
  446. this.httpPort = httpPort;
  447. }
  448. /**
  449. * Returns the user-configured RMI port. Has package-private access because in reality, a product's nodes will each
  450. * use a separate RMI port, so our code should call {@link Node#getRmiPort()} instead. This getter is only for
  451. * finding out what port the user configured at the product level.
  452. *
  453. * @return see above
  454. */
  455. @VisibleForTesting
  456. int getRmiPort() {
  457. return rmiPort;
  458. }
  459. void setRmiPort(int rmiPort) {
  460. this.rmiPort = rmiPort;
  461. }
  462. /**
  463. * Returns the timeout for this product to change state between up and down.
  464. *
  465. * @param startingUp whether the product is starting up (as opposed to shutting down)
  466. * @return see above
  467. * @since 8.3
  468. */
  469. public int getTimeout(final boolean startingUp) {
  470. return startingUp ? startupTimeout : shutdownTimeout;
  471. }
  472. public Boolean getUseHttps() {
  473. return useHttps;
  474. }
  475. public void setUseHttps(Boolean useHttps) {
  476. this.useHttps = useHttps;
  477. }
  478. /**
  479. * @since 5.0.4
  480. */
  481. void setHttpsPort(final int httpsPort) {
  482. this.httpsPort = httpsPort;
  483. }
  484. /**
  485. * Returns the user-configured HTTPS port. Has package-private access because in reality, a product's nodes will
  486. * each use a separate HTTPS port, so our code should call {@link Node#getWebPort()} instead. This getter is only
  487. * for finding out what port the user configured at the product level.
  488. *
  489. * @since 5.0.4
  490. */
  491. int getHttpsPort() {
  492. return this.httpsPort;
  493. }
  494. /**
  495. * @since 5.0.4
  496. */
  497. public void setHttpsClientAuth(final String httpsClientAuth) {
  498. this.httpsClientAuth = httpsClientAuth;
  499. }
  500. /**
  501. * @since 5.0.4
  502. */
  503. public String getHttpsClientAuth() {
  504. return this.httpsClientAuth;
  505. }
  506. /**
  507. * @since 5.0.4
  508. */
  509. public void setHttpsSSLProtocol(final String httpsSslProtocol) {
  510. this.httpsSslProtocol = httpsSslProtocol;
  511. }
  512. /**
  513. * @since 5.0.4
  514. */
  515. public String getHttpsSSLProtocol() {
  516. return this.httpsSslProtocol;
  517. }
  518. /**
  519. * @since 5.0.4
  520. */
  521. public void setHttpsKeystoreFile(final String httpsKeystoreFile) {
  522. this.httpsKeystoreFile = httpsKeystoreFile;
  523. }
  524. /**
  525. * @since 5.0.4
  526. */
  527. public String getHttpsKeystoreFile() {
  528. return this.httpsKeystoreFile;
  529. }
  530. /**
  531. * @since 5.0.4
  532. */
  533. public void setHttpsKeystorePass(final String httpsKeystorePass) {
  534. this.httpsKeystorePass = httpsKeystorePass;
  535. }
  536. /**
  537. * @since 5.0.4
  538. */
  539. public String getHttpsKeystorePass() {
  540. return this.httpsKeystorePass;
  541. }
  542. /**
  543. * @since 5.0.4
  544. */
  545. public void setHttpsKeyAlias(final String httpsKeyAlias) {
  546. this.httpsKeyAlias = httpsKeyAlias;
  547. }
  548. /**
  549. * @since 5.0.4
  550. */
  551. public String getHttpsKeyAlias() {
  552. return this.httpsKeyAlias;
  553. }
  554. /**
  555. * @since 5.0.4
  556. */
  557. public void setHttpsHttpSecure(final Boolean httpsHttpSecure) {
  558. this.httpsHttpSecure = httpsHttpSecure;
  559. }
  560. /**
  561. * @since 5.0.4
  562. */
  563. public Boolean getHttpsHttpSecure() {
  564. return this.httpsHttpSecure;
  565. }
  566. public String getContextPath() {
  567. return contextPath;
  568. }
  569. public void setContextPath(String contextPath) {
  570. this.contextPath = contextPath;
  571. }
  572. public String getJvmArgs() {
  573. return jvmArgs;
  574. }
  575. public void setJvmArgs(final String jvmArgs) {
  576. this.jvmArgs = defaultString(jvmArgs);
  577. }
  578. /**
  579. * If this product's JVM arguments are blank, this method sets them to the given value.
  580. *
  581. * @param defaultJvmArgs the default JVM arguments, trimmed to empty
  582. * @since 8.3
  583. */
  584. public void defaultJvmArgs(final String defaultJvmArgs) {
  585. if (stripToNull(jvmArgs) == null) {
  586. setJvmArgs(defaultJvmArgs);
  587. }
  588. }
  589. /**
  590. * Returns the user-configured debug-related JVM arguments. Has package-private access because in reality, a
  591. * product's nodes will each use their own debug arguments (because they include the node's debug port), so our code
  592. * should call {@link Node#getDebugArgs()} instead. This getter is only for finding out what debug arguments the
  593. * user configured at the product level.
  594. *
  595. * @return see description
  596. */
  597. @VisibleForTesting
  598. String getDebugArgs() {
  599. return debugArgs;
  600. }
  601. void setDebugArgs(final String debugArgs) {
  602. this.debugArgs = defaultString(debugArgs, "");
  603. }
  604. public ArtifactRetriever getArtifactRetriever() {
  605. return artifactRetriever;
  606. }
  607. public void setArtifactRetriever(final ArtifactRetriever artifactRetriever) {
  608. this.artifactRetriever = artifactRetriever;
  609. }
  610. public String getVersion() {
  611. return version;
  612. }
  613. public void setVersion(String version) {
  614. this.version = version;
  615. }
  616. public String getDataVersion() {
  617. return productDataVersion;
  618. }
  619. public void setDataVersion(String productDataVersion) {
  620. this.productDataVersion = productDataVersion;
  621. }
  622. /**
  623. * @deprecated since 3.2
  624. */
  625. @Deprecated
  626. public String getProductDataVersion() {
  627. return productDataVersion;
  628. }
  629. /**
  630. * @deprecated since 3.2
  631. */
  632. @Deprecated
  633. public void setProductDataVersion(String productDataVersion) {
  634. this.productDataVersion = productDataVersion;
  635. }
  636. /**
  637. * The path to a custom test resources zip or a directory. Takes precedence over dataVersion.
  638. * The data from this path will be copied into the home directory.
  639. */
  640. public String getDataPath() {
  641. return productDataPath;
  642. }
  643. /**
  644. * The path to a custom test resources zip or a directory. Takes precedence over dataVersion.
  645. * The data from this path will be copied into the home directory.
  646. */
  647. public void setDataPath(String productDataPath) {
  648. this.productDataPath = productDataPath;
  649. }
  650. public String getDataOverridesPath() {
  651. return dataOverridesPath;
  652. }
  653. public void setDataOverridesPath(String productHomeOverridesPath) {
  654. this.dataOverridesPath = productHomeOverridesPath;
  655. }
  656. /**
  657. * @deprecated since 3.2
  658. */
  659. @Deprecated
  660. public String getProductDataPath() {
  661. return productDataPath;
  662. }
  663. /**
  664. * @deprecated since 3.2
  665. */
  666. @Deprecated
  667. public void setProductDataPath(String productDataPath) {
  668. this.productDataPath = productDataPath;
  669. }
  670. public List<Application> getApplications() {
  671. return applications;
  672. }
  673. public void setApplications(final List<Application> applications) {
  674. this.applications = applications;
  675. }
  676. public List<ProductArtifact> getPluginArtifacts() {
  677. return pluginArtifacts;
  678. }
  679. public void setPluginArtifacts(List<ProductArtifact> pluginArtifacts) {
  680. this.pluginArtifacts = pluginArtifacts;
  681. }
  682. public List<ProductArtifact> getLibArtifacts() {
  683. return libArtifacts;
  684. }
  685. public void setLibArtifacts(List<ProductArtifact> libArtifacts) {
  686. this.libArtifacts = libArtifacts;
  687. }
  688. public List<ProductArtifact> getBundledArtifacts() {
  689. return unmodifiableList(bundledArtifacts);
  690. }
  691. public void setBundledArtifacts(@Nonnull final List<ProductArtifact> bundledArtifacts) {
  692. this.bundledArtifacts.clear();
  693. this.bundledArtifacts.addAll(bundledArtifacts);
  694. }
  695. /**
  696. * Adds the given bundled artifacts.
  697. *
  698. * @param newBundledArtifacts the artifacts to add
  699. * @since 8.3
  700. */
  701. public void addBundledArtifacts(final Collection<ProductArtifact> newBundledArtifacts) {
  702. bundledArtifacts.addAll(newBundledArtifacts);
  703. }
  704. public File getLog4jProperties() {
  705. return log4jProperties;
  706. }
  707. public void setLog4jProperties(File log4jProperties) {
  708. this.log4jProperties = log4jProperties;
  709. }
  710. public String getRestVersion() {
  711. return restVersion;
  712. }
  713. public void setRestVersion(String restVersion) {
  714. this.restVersion = restVersion;
  715. }
  716. public String getSalVersion() {
  717. return salVersion;
  718. }
  719. public void setSalVersion(String salVersion) {
  720. this.salVersion = salVersion;
  721. }
  722. public String getPdkVersion() {
  723. return pdkVersion;
  724. }
  725. public void setPdkVersion(String pdkVersion) {
  726. this.pdkVersion = pdkVersion;
  727. }
  728. public String getId() {
  729. return id;
  730. }
  731. public void setId(String id) {
  732. this.id = id;
  733. }
  734. public String getInstanceId() {
  735. return instanceId;
  736. }
  737. public void setInstanceId(String instanceId) {
  738. this.instanceId = instanceId;
  739. }
  740. public Boolean isInstallPlugin() {
  741. return installPlugin;
  742. }
  743. public void setInstallPlugin(final Boolean installPlugin) {
  744. this.installPlugin = installPlugin;
  745. }
  746. public String getWebConsoleVersion() {
  747. return webConsoleVersion;
  748. }
  749. public Boolean isEnableDevToolbox() {
  750. return enableDevToolbox;
  751. }
  752. public void setEnableDevToolbox(final Boolean enableDevToolbox) {
  753. this.enableDevToolbox = enableDevToolbox;
  754. }
  755. public String getQuickReloadVersion() {
  756. return quickReloadVersion;
  757. }
  758. public void setQuickReloadVersion(final String quickReloadVersion) {
  759. this.quickReloadVersion = quickReloadVersion;
  760. }
  761. public Boolean isEnableQuickReload() {
  762. return enableQuickReload;
  763. }
  764. public void setEnableQuickReload(final Boolean enableQuickReload) {
  765. this.enableQuickReload = enableQuickReload;
  766. }
  767. public String getPluginViewerVersion() {
  768. return pluginViewerVersion;
  769. }
  770. public void setPluginViewerVersion(String pluginViewerVersion) {
  771. this.pluginViewerVersion = pluginViewerVersion;
  772. }
  773. public Boolean isEnablePluginViewer() {
  774. return enablePluginViewer;
  775. }
  776. public void setEnablePluginViewer(final Boolean enablePluginViewer) {
  777. this.enablePluginViewer = enablePluginViewer;
  778. }
  779. public String getDevToolboxVersion() {
  780. return devToolboxVersion;
  781. }
  782. public void setDevToolboxVersion(String devToolboxVersion) {
  783. this.devToolboxVersion = devToolboxVersion;
  784. }
  785. public Boolean isEnablePde() {
  786. return enablePde;
  787. }
  788. public void setEnablePde(Boolean enablePde) {
  789. this.enablePde = enablePde;
  790. }
  791. public String getPdeVersion() {
  792. return pdeVersion;
  793. }
  794. public void setPdeVersion(String pdeVersion) {
  795. this.pdeVersion = pdeVersion;
  796. }
  797. public void setWebConsoleVersion(final String webConsoleVersion) {
  798. this.webConsoleVersion = webConsoleVersion;
  799. }
  800. /**
  801. * @deprecated Since 3.2, use systemPropertyVariables
  802. */
  803. @Deprecated
  804. public void setSystemProperties(final Properties systemProperties) {
  805. systemProperties.forEach(
  806. (key, value) -> this.systemProperties.put(key.toString(), value));
  807. }
  808. /**
  809. * @deprecated Since 3.2, use systemPropertyVariables
  810. */
  811. @Deprecated
  812. public Properties getSystemProperties() {
  813. Properties props = new Properties();
  814. props.putAll(systemProperties);
  815. return props;
  816. }
  817. public void setSystemPropertyVariables(final Map<String, Object> systemProperties) {
  818. this.systemProperties = systemProperties;
  819. }
  820. public Map<String, Object> getSystemPropertyVariables() {
  821. return systemProperties;
  822. }
  823. /**
  824. * If the given system property is not set for this product, this method sets it to the given value.
  825. *
  826. * @param key the name of the property
  827. * @param valueSupplier provides the value to set (if this key is not already set)
  828. * @see Map#computeIfAbsent
  829. * @since 8.3.0
  830. */
  831. public void defaultSystemProperty(final String key, final Supplier<String> valueSupplier) {
  832. systemProperties.computeIfAbsent(key, k -> valueSupplier.get());
  833. }
  834. /**
  835. * Returns the name of the log file for this product.
  836. *
  837. * @return see description
  838. */
  839. public String getOutput() {
  840. return output;
  841. }
  842. public void setOutput(final String output) {
  843. this.output = output;
  844. }
  845. /**
  846. * Returns the user-configured debug port. Has package-private access because in reality, a product's nodes will
  847. * each use a separate debug port, so our code should call {@link Node#getJvmDebugPort()} instead. This getter is only
  848. * for finding out what port the user configured at the product level.
  849. *
  850. * @return see above
  851. */
  852. @VisibleForTesting
  853. int getJvmDebugPort() {
  854. return jvmDebugPort;
  855. }
  856. void setJvmDebugPort(final int jvmDebugPort) {
  857. this.jvmDebugPort = jvmDebugPort;
  858. }
  859. public int getStartupTimeout() {
  860. return startupTimeout;
  861. }
  862. public void setStartupTimeout(final int startupTimeout) {
  863. this.startupTimeout = startupTimeout;
  864. }
  865. public int getShutdownTimeout() {
  866. return shutdownTimeout;
  867. }
  868. public void setShutdownTimeout(final int shutdownTimeout) {
  869. this.shutdownTimeout = shutdownTimeout;
  870. }
  871. public String getGroupId() {
  872. return groupId;
  873. }
  874. public void setGroupId(final String groupId) {
  875. this.groupId = groupId;
  876. }
  877. public String getArtifactId() {
  878. return artifactId;
  879. }
  880. public void setArtifactId(final String artifactId) {
  881. this.artifactId = artifactId;
  882. }
  883. public void setSystemProperties(final Map<String, Object> systemProperties) {
  884. this.systemProperties = systemProperties;
  885. }
  886. public Boolean getSynchronousStartup() {
  887. return synchronousStartup;
  888. }
  889. public void setSynchronousStartup(final Boolean synchronousStartup) {
  890. this.synchronousStartup = synchronousStartup;
  891. }
  892. public String getDataHome() {
  893. return dataHome;
  894. }
  895. /**
  896. * The path to the product's home directory. Takes precedence over dataPath.
  897. * The data from this path will be used directly (read/write) by the product.
  898. */
  899. public void setDataHome(final String dataHome) {
  900. this.dataHome = dataHome;
  901. }
  902. /**
  903. * @return the dataSources. Not null, because initialized in {@code AbstractProductHandlerMojo#setDefaultValues}
  904. * May be empty.
  905. */
  906. public List<DataSource> getDataSources() {
  907. return dataSources;
  908. }
  909. /**
  910. * @param dataSources the dataSources to set
  911. */
  912. public void setDataSources(final List<DataSource> dataSources) {
  913. this.dataSources = dataSources;
  914. }
  915. /**
  916. * Returns the shared home directory for a Data Center cluster.
  917. *
  918. * @return null if no shared home is set, otherwise the path to that directory
  919. */
  920. public String getSharedHome() {
  921. return sharedHome;
  922. }
  923. /**
  924. * Sets the shared home directory for a Jira cluster.
  925. *
  926. * @param sharedHome the directory path to set (can be null)
  927. */
  928. public void setSharedHome(final String sharedHome) {
  929. this.sharedHome = sharedHome;
  930. }
  931. /**
  932. * Returns the user-configured AJP port. Has package-private access because in reality, a product's nodes will each
  933. * use a separate AJP port, so our code should call {@link Node#getAjpPort()} instead. This getter is only for
  934. * finding out what port the user configured at the product level.
  935. *
  936. * @return see above
  937. */
  938. @VisibleForTesting
  939. int getAjpPort() {
  940. return ajpPort;
  941. }
  942. /**
  943. * Sets the AJP port for use by the web container.
  944. *
  945. * @param ajpPort the AJP port to set
  946. */
  947. void setAjpPort(final int ajpPort) {
  948. this.ajpPort = ajpPort;
  949. }
  950. @Override
  951. public String toString() {
  952. return "Product " + id + " [instanceId=" + instanceId + "]";
  953. }
  954. /**
  955. * Returns the base URL for the given node of this product.
  956. *
  957. * @param nodeIndex the zero-based index of the node
  958. * @return the base URL
  959. * @since 8.3
  960. */
  961. public String getBaseUrlForNode(final int nodeIndex) {
  962. return getBaseUrlForPort(getNodes().get(nodeIndex).getWebPort());
  963. }
  964. /**
  965. * Returns the base URL for this product, when it's running on the given HTTP(S) port.
  966. *
  967. * @param actualHttpPort the actual web port
  968. * @return the base URL
  969. * @since 8.3
  970. */
  971. public String getBaseUrlForPort(final int actualHttpPort) {
  972. if (actualHttpPort == 0) {
  973. throw new IllegalArgumentException("Invalid or unresolved web port");
  974. }
  975. return getBaseUrl(server, actualHttpPort, contextPath);
  976. }
  977. /**
  978. * Returns the protocol transmission scheme.
  979. *
  980. * @return "http" or "https".
  981. */
  982. public String getProtocol() {
  983. return isHttps() ? "https" : "http";
  984. }
  985. /**
  986. * A version of {@link #getUseHttps()} which never returns {@code null}. If {@code useHttps} was not set, it
  987. * is assumed to be {@code false}.
  988. *
  989. * @return {@code true} if {@link #getUseHttps()} is {@code Boolean.TRUE}; otherwise, {@code false}
  990. * @see #getProtocol() to get the web protocol
  991. * @see Node#getWebPort() to get the web port
  992. * @since 6.3
  993. */
  994. public boolean isHttps() {
  995. return Boolean.TRUE.equals(useHttps);
  996. }
  997. /**
  998. * Whether to await full initialization of the product.
  999. *
  1000. * @return see description
  1001. */
  1002. public boolean isAwaitFullInitialization() {
  1003. if (awaitFullInitialization != null) {
  1004. return awaitFullInitialization;
  1005. }
  1006. return true;
  1007. }
  1008. public void setAwaitFullInitialization(Boolean awaitFullInitialization) {
  1009. this.awaitFullInitialization = awaitFullInitialization;
  1010. }
  1011. /**
  1012. * Returns any configuration overrides that should be passed on cargo start.
  1013. *
  1014. * @since 6.3
  1015. */
  1016. @Nullable
  1017. public Collection<XmlOverride> getCargoXmlOverrides() {
  1018. return cargoXmlOverrides;
  1019. }
  1020. public void setCargoXmlOverrides(final Collection<XmlOverride> cargoXmlOverrides) {
  1021. this.cargoXmlOverrides = cargoXmlOverrides;
  1022. }
  1023. public boolean isContainerNotSpecified() {
  1024. return containerNotSpecified;
  1025. }
  1026. public void setContainerNotSpecified(boolean containerNotSpecified) {
  1027. this.containerNotSpecified = containerNotSpecified;
  1028. }
  1029. /**
  1030. * Returns the license that the user has specified for this product in their AMPS configuration.
  1031. *
  1032. * @return see description
  1033. * @since 8.2
  1034. */
  1035. @Nullable
  1036. String getLicense() {
  1037. return license;
  1038. }
  1039. /**
  1040. * Indicates whether this is the given product.
  1041. *
  1042. * @param productId the product ID to check against
  1043. * @return see description
  1044. * @since 8.3
  1045. */
  1046. public boolean is(final String productId) {
  1047. return Objects.equals(productId, id);
  1048. }
  1049. /**
  1050. * Returns the nodes that make up this product instance.
  1051. *
  1052. * @return a null or empty list if the nodes have not been initialised or configured in the user's POM
  1053. * @see #initialiseNodes()
  1054. * @since 8.3
  1055. */
  1056. public List<Node> getNodes() {
  1057. return nodes;
  1058. }
  1059. /**
  1060. * Initialises this product's nodes. Until this is called, the list returned by {@link #getNodes()} can be
  1061. * {@code null} or empty. The Mojo being invoked is responsible for calling this method at the appropriate time,
  1062. * namely after the user-configured values have been read, but before {@link #getNodes()} is called from outside
  1063. * this class.
  1064. *
  1065. * @since 8.3
  1066. */
  1067. public void initialiseNodes() {
  1068. if (nodes == null || nodes.isEmpty()) {
  1069. // The user configured no nodes => synthesise one for single-node operation
  1070. nodes = singletonList(new Node(isHttps(), ajpPort, jvmDebugPort, httpPort, httpsPort, rmiPort, debugArgs));
  1071. }
  1072. for (final Node node : nodes) {
  1073. node.ensureNonDebugPortsAreSet(instanceId);
  1074. }
  1075. }
  1076. /**
  1077. * Sets this product's nodes.
  1078. *
  1079. * @param nodes the nodes to set
  1080. * @since 8.3
  1081. */
  1082. void setNodes(final List<Node> nodes) {
  1083. this.nodes = nodes;
  1084. }
  1085. /**
  1086. * Sets the startup mode of this Product based on whether the products are being started in parallel.
  1087. *
  1088. * @param parallel the desired parallelism
  1089. * @since 8.3
  1090. */
  1091. public void setSynchronicity(final boolean parallel) {
  1092. if (parallel) {
  1093. if (synchronousStartup == null) {
  1094. synchronousStartup = false;
  1095. }
  1096. } else {
  1097. synchronousStartup = true;
  1098. }
  1099. }
  1100. /**
  1101. * Sets the debug args for each node of this product. The caller must have already set each node's debug port.
  1102. *
  1103. * @param suspend whether to suspend and wait for the user to attach their debugger
  1104. * @param log the Maven log
  1105. * @since 8.3
  1106. * @throws IllegalStateException if the nodes have not been set or their debug ports are zero
  1107. */
  1108. public void setNodeDebugArgs(final boolean suspend, final Log log) {
  1109. if (nodes == null || nodes.isEmpty()) {
  1110. throw new IllegalStateException(format("No nodes set for instance '%s'", instanceId));
  1111. }
  1112. for (int i = 0; i < nodes.size(); i++) {
  1113. final Node node = nodes.get(i);
  1114. final int debugPort = node.getJvmDebugPort();
  1115. if (debugPort == 0) {
  1116. throw new IllegalStateException(
  1117. format("No debug port set for node %d of instance '%s'", i, instanceId));
  1118. }
  1119. node.defaultDebugArgs(suspend);
  1120. log.info(format("Debug port for node %d of %s is %d", i, instanceId, debugPort));
  1121. }
  1122. }
  1123. /**
  1124. * Returns the debug args for this product, assuming single-node operation. Multi-node products should instead call
  1125. * {@link Node#getDebugArgs()} on the relevant node.
  1126. *
  1127. * @return see description
  1128. * @since 8.3
  1129. */
  1130. public String getSingleNodeDebugArgs() {
  1131. return getSingleNodeProperty(Node::getDebugArgs);
  1132. }
  1133. /**
  1134. * Returns the web port for this product, assuming single-node operation. Multi-node products should instead call
  1135. * {@link Node#getWebPort()} on the relevant node.
  1136. *
  1137. * @return see description
  1138. * @since 8.3
  1139. */
  1140. public int getSingleNodeWebPort() {
  1141. return getSingleNodeProperty(Node::getWebPort);
  1142. }
  1143. private <R> R getSingleNodeProperty(final Function<Node, R> accessor) {
  1144. final List<Node> populatedNodes = getNodes();
  1145. if (populatedNodes.size() == 1) {
  1146. return accessor.apply(populatedNodes.get(0)); // happy path
  1147. }
  1148. throw new IllegalStateException("Expected one node but found " + populatedNodes);
  1149. }
  1150. /**
  1151. * Returns the system properties to be applied to the JVM when starting the given node of this product.
  1152. * This is a combination of the product-level and node-level system properties, with the latter taking precedence.
  1153. *
  1154. * @param nodeIndex the zero-based index of the node being started
  1155. * @return see description
  1156. * @since 8.3
  1157. */
  1158. @Nonnull
  1159. public Map<String, String> getSystemPropertiesForNode(final int nodeIndex) {
  1160. final Map<String, String> combinedProperties = new HashMap<>();
  1161. systemProperties.forEach((key, value) -> combinedProperties.put(key, String.valueOf(value)));
  1162. combinedProperties.putAll(getNodes().get(nodeIndex).getSystemProperties());
  1163. return combinedProperties;
  1164. }
  1165. /**
  1166. * Returns the web ports used by the nodes of this product.
  1167. *
  1168. * @return a list of non-null elements (safe to unbox)
  1169. * @throws IllegalStateException if any of the node's web ports are zero (i.e. unresolved)
  1170. * @since 8.3
  1171. */
  1172. @Nonnull
  1173. public List<Integer> getWebPorts() {
  1174. final List<Integer> webPorts = new ArrayList<>();
  1175. for (final Node node : getNodes()) {
  1176. final int webPort = node.getWebPort();
  1177. if (webPort == 0) {
  1178. throw new IllegalStateException("Web port not set for node " + node);
  1179. }
  1180. webPorts.add(webPort);
  1181. }
  1182. return webPorts;
  1183. }
  1184. /**
  1185. * Returns the web port of the given node.
  1186. *
  1187. * @param nodeIndex the zero-based node index
  1188. * @return a non-zero port number
  1189. * @throws IllegalStateException if the node's web port has not been set (is zero)
  1190. * @since 8.3
  1191. */
  1192. public int getWebPortForNode(final int nodeIndex) {
  1193. final int webPort = getNodes().get(nodeIndex).getWebPort();
  1194. if (webPort == 0) {
  1195. throw new IllegalStateException("Web port not set or resolved for node " + nodeIndex);
  1196. }
  1197. return webPort;
  1198. }
  1199. /**
  1200. * Indicates whether this product instance has multiple nodes.
  1201. *
  1202. * @return see description
  1203. * @since 8.3
  1204. */
  1205. public boolean isMultiNode() {
  1206. return getNodes().size() > 1;
  1207. }
  1208. /**
  1209. * Returns the URL that should be called to find out whether the given node is up and healthy.
  1210. *
  1211. * @param nodeIndex the zero-based index of the node being checked
  1212. * @return empty if this product does not have or require such a URL, otherwise a URL that returns 200 if healthy
  1213. * @since 8.3
  1214. */
  1215. public Optional<URI> getStatusUri(final int nodeIndex) {
  1216. if ("confluence".equals(id) && isMultiNode()) {
  1217. final String url = getBaseUrlForNode(nodeIndex) + "/status";
  1218. return Optional.of(URI.create(url));
  1219. }
  1220. return empty();
  1221. }
  1222. /**
  1223. * Returns any user-configured license for this product, as follows:
  1224. * <ol>
  1225. * <li>if {@link #license} is non-blank, it's returned with any spaces removed</li>
  1226. * <li>otherwise, this method returns empty</li>
  1227. * </ol>
  1228. *
  1229. * @param licenseCreator the license creator to use, if necessary
  1230. * @param licensePopulator the license populator to use, if necessary
  1231. * @return see description
  1232. * @since 8.3
  1233. */
  1234. @Nonnull
  1235. public Optional<String> getUserConfiguredLicense() {
  1236. return Optional.ofNullable(trimToNull(remove(license, ' ')));
  1237. }
  1238. /**
  1239. * Indicates whether this product has a user-configured license. This method is faster than calling
  1240. * {@link #getUserConfiguredLicense}, because no license generation is ever performed.
  1241. *
  1242. * @return see description
  1243. * @since 8.3
  1244. */
  1245. public boolean hasUserConfiguredLicense() {
  1246. return isNotBlank(license);
  1247. }
  1248. }