PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/atlassian/amps
Java | 219 lines | 152 code | 26 blank | 41 comment | 24 complexity | 1f73b1648be94dfa0e9775a21b4d0b5f 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.ProductHandlerFactory;
  3. import com.google.common.annotations.VisibleForTesting;
  4. import org.apache.commons.lang3.StringUtils;
  5. import org.apache.commons.lang3.mutable.MutableInt;
  6. import org.apache.maven.plugin.MojoExecutionException;
  7. import org.apache.maven.plugins.annotations.Parameter;
  8. import javax.annotation.ParametersAreNonnullByDefault;
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.HashMap;
  12. import java.util.HashSet;
  13. import java.util.List;
  14. import java.util.Map;
  15. import java.util.Objects;
  16. import java.util.Set;
  17. import java.util.stream.Stream;
  18. import static java.util.Arrays.asList;
  19. import static java.util.Collections.singletonList;
  20. import static java.util.Objects.requireNonNull;
  21. import static java.util.stream.Collectors.toSet;
  22. import static org.apache.commons.lang3.StringUtils.isNotBlank;
  23. /**
  24. * Superclass for any Mojo that needs to run in the context of test groups.
  25. */
  26. public abstract class AbstractTestGroupsHandlerMojo extends AbstractProductHandlerMojo {
  27. private static final String PORT_CONFLICT_MESSAGE_FORMAT =
  28. "%s (node %d): The configured %s port, %d, is in use by the %s port for %s (node %d)";
  29. /**
  30. * The test group to run. If provided, determines the products to run.
  31. */
  32. @Parameter(property = "testGroup")
  33. private String testGroup;
  34. /**
  35. * The configured test groups.
  36. */
  37. @VisibleForTesting
  38. @Parameter
  39. List<TestGroup> testGroups = new ArrayList<>();
  40. protected final List<TestGroup> getTestGroups() {
  41. return testGroups;
  42. }
  43. protected List<Product> getProductsForTestGroup(final String testGroupId) throws MojoExecutionException {
  44. final List<Product> products = new ArrayList<>();
  45. int dupCounter = 0;
  46. final Set<String> uniqueProductIds = new HashSet<>();
  47. final Map<String, Product> productContexts = getProductContexts();
  48. for (final String instanceId : getTestGroupInstanceIds(testGroupId)) {
  49. final Product product = productContexts.get(instanceId);
  50. if (product == null) {
  51. throw new MojoExecutionException("The test group '" + testGroupId + "' refers to a product '" +
  52. instanceId + "' that doesn't have an associated <product> configuration.");
  53. }
  54. // Give unique ids to duplicate product instances
  55. if (uniqueProductIds.contains(instanceId)) {
  56. product.setInstanceId(instanceId + "-" + dupCounter++);
  57. } else {
  58. uniqueProductIds.add(instanceId);
  59. }
  60. products.add(product);
  61. }
  62. if (products.size() > 1) {
  63. validatePortConfiguration(products);
  64. }
  65. return products;
  66. }
  67. /**
  68. * Returns the products in the test group:
  69. * <ul>
  70. * <li>If a {@literal <testGroup>} is defined, all the products of this test group</li>
  71. * <li>If testGroupId is __no_test_group__, adds it</li>
  72. * <li>If testGroupId is a product instanceId, adds it</li>
  73. * </ul>
  74. */
  75. private List<String> getTestGroupInstanceIds(String testGroupId) throws MojoExecutionException {
  76. final List<String> instanceIds = new ArrayList<>();
  77. if (NO_TEST_GROUP.equals(testGroupId)) {
  78. instanceIds.add(getProductId());
  79. }
  80. for (TestGroup group : testGroups) {
  81. if (StringUtils.equals(group.getId(), testGroupId)) {
  82. instanceIds.addAll(group.getInstanceIds());
  83. }
  84. }
  85. if (ProductHandlerFactory.getIds().contains(testGroupId) && !instanceIds.contains(testGroupId)) {
  86. instanceIds.add(testGroupId);
  87. }
  88. if (instanceIds.isEmpty()) {
  89. getLog().warn("Unknown test group ID: " + testGroupId + " Detected IDs: " + getTestGroupIds());
  90. }
  91. return instanceIds;
  92. }
  93. protected final Set<String> getTestGroupIds() {
  94. return testGroups.stream()
  95. .map(TestGroup::getId)
  96. .filter(Objects::nonNull)
  97. .collect(toSet());
  98. }
  99. protected final List<Product> getProductsToExecute() throws MojoExecutionException {
  100. if (isNotBlank(testGroup)) {
  101. return getProductsForTestGroup(testGroup);
  102. }
  103. if (isNotBlank(instanceId)) {
  104. final Product product = getProductContexts().get(instanceId);
  105. if (product == null) {
  106. throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
  107. }
  108. return singletonList(product);
  109. }
  110. return singletonList(getProductContexts().get(getProductId()));
  111. }
  112. /**
  113. * Ensures that there are no port conflicts between products and raises an exception if there
  114. * are conflicts
  115. *
  116. * @param products two or more products, for which the configured ports should be validated
  117. * @throws MojoExecutionException if any of the configured ports collide between products
  118. * @since 8.0
  119. */
  120. void validatePortConfiguration(final List<Product> products) throws MojoExecutionException {
  121. final Map<Integer, ConfiguredPort> portsById = new HashMap<>();
  122. final MutableInt collisions = new MutableInt();
  123. products.stream()
  124. .flatMap(AbstractTestGroupsHandlerMojo::getConfiguredPorts)
  125. .filter(ConfiguredPort::isStatic) // Only verify statically-configured ports
  126. .forEach(configured -> {
  127. ConfiguredPort conflict = portsById.get(configured.port);
  128. if (conflict == null) {
  129. portsById.put(configured.port, configured);
  130. } else {
  131. getLog().error(String.format(PORT_CONFLICT_MESSAGE_FORMAT,
  132. configured.instanceId, configured.nodeIndex, configured.type, configured.port,
  133. conflict.type, conflict.instanceId, conflict.nodeIndex));
  134. collisions.increment();
  135. }
  136. });
  137. final int collisionCount = collisions.intValue();
  138. if (collisionCount != 0) {
  139. throw new MojoExecutionException(collisionCount + " port conflict" +
  140. ((collisionCount == 1) ? " was" : "s were") + " detected between the " +
  141. products.size() + " products in the '" + testGroup + "' test group");
  142. }
  143. }
  144. /**
  145. * Returns the {@link ConfiguredPort configured ports} for the given {@link Product product}.
  146. *
  147. * @param product the product for which to get the ports
  148. * @return the configured ports
  149. * @since 8.0
  150. */
  151. private static Stream<ConfiguredPort> getConfiguredPorts(final Product product) {
  152. final String instanceId = product.getInstanceId();
  153. final Collection<ConfiguredPort> configuredPorts = new ArrayList<>();
  154. final List<Node> nodes = product.getNodes();
  155. for (int i = 0; i < nodes.size(); i++) {
  156. final Node node = nodes.get(i);
  157. configuredPorts.addAll(getConfiguredPorts(instanceId, node, i, product.getProtocol()));
  158. }
  159. return configuredPorts.stream();
  160. }
  161. private static Collection<ConfiguredPort> getConfiguredPorts(
  162. final String instanceId, final Node node, final int nodeIndex, final String protocol) {
  163. return asList(
  164. new ConfiguredPort(instanceId, node.getWebPort(), protocol.toUpperCase(), nodeIndex),
  165. new ConfiguredPort(instanceId, node.getAjpPort(), "AJP", nodeIndex),
  166. new ConfiguredPort(instanceId, node.getRmiPort(), "RMI", nodeIndex)
  167. );
  168. }
  169. /**
  170. * Describes a configured port for a {@link Product}, detailing the instance ID as well as the port and its type.
  171. * <p>
  172. * This data class simplifies maintaining an instance+port+type association, which facilitates using descriptive
  173. * error messages when the configured ports for two instances collide.
  174. *
  175. * @since 8.0
  176. */
  177. @ParametersAreNonnullByDefault
  178. private static class ConfiguredPort {
  179. private final String instanceId;
  180. private final int port;
  181. private final String type;
  182. private final int nodeIndex;
  183. ConfiguredPort(final String instanceId, final int port, final String type, final int nodeIndex) {
  184. this.instanceId = requireNonNull(instanceId, "instanceId");
  185. this.port = port;
  186. this.type = requireNonNull(type, "type");
  187. this.nodeIndex = nodeIndex;
  188. }
  189. boolean isStatic() {
  190. return port != 0;
  191. }
  192. }
  193. }