/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/AbstractTestGroupsHandlerMojo.java
Java | 219 lines | 152 code | 26 blank | 41 comment | 24 complexity | 1f73b1648be94dfa0e9775a21b4d0b5f MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- package com.atlassian.maven.plugins.amps;
- import com.atlassian.maven.plugins.amps.product.ProductHandlerFactory;
- import com.google.common.annotations.VisibleForTesting;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.lang3.mutable.MutableInt;
- import org.apache.maven.plugin.MojoExecutionException;
- import org.apache.maven.plugins.annotations.Parameter;
- import javax.annotation.ParametersAreNonnullByDefault;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import java.util.Set;
- import java.util.stream.Stream;
- import static java.util.Arrays.asList;
- import static java.util.Collections.singletonList;
- import static java.util.Objects.requireNonNull;
- import static java.util.stream.Collectors.toSet;
- import static org.apache.commons.lang3.StringUtils.isNotBlank;
- /**
- * Superclass for any Mojo that needs to run in the context of test groups.
- */
- public abstract class AbstractTestGroupsHandlerMojo extends AbstractProductHandlerMojo {
- private static final String PORT_CONFLICT_MESSAGE_FORMAT =
- "%s (node %d): The configured %s port, %d, is in use by the %s port for %s (node %d)";
- /**
- * The test group to run. If provided, determines the products to run.
- */
- @Parameter(property = "testGroup")
- private String testGroup;
- /**
- * The configured test groups.
- */
- @VisibleForTesting
- @Parameter
- List<TestGroup> testGroups = new ArrayList<>();
- protected final List<TestGroup> getTestGroups() {
- return testGroups;
- }
- protected List<Product> getProductsForTestGroup(final String testGroupId) throws MojoExecutionException {
- final List<Product> products = new ArrayList<>();
- int dupCounter = 0;
- final Set<String> uniqueProductIds = new HashSet<>();
- final Map<String, Product> productContexts = getProductContexts();
- for (final String instanceId : getTestGroupInstanceIds(testGroupId)) {
- final Product product = productContexts.get(instanceId);
- if (product == null) {
- throw new MojoExecutionException("The test group '" + testGroupId + "' refers to a product '" +
- instanceId + "' that doesn't have an associated <product> configuration.");
- }
- // Give unique ids to duplicate product instances
- if (uniqueProductIds.contains(instanceId)) {
- product.setInstanceId(instanceId + "-" + dupCounter++);
- } else {
- uniqueProductIds.add(instanceId);
- }
- products.add(product);
- }
- if (products.size() > 1) {
- validatePortConfiguration(products);
- }
- return products;
- }
- /**
- * Returns the products in the test group:
- * <ul>
- * <li>If a {@literal <testGroup>} is defined, all the products of this test group</li>
- * <li>If testGroupId is __no_test_group__, adds it</li>
- * <li>If testGroupId is a product instanceId, adds it</li>
- * </ul>
- */
- private List<String> getTestGroupInstanceIds(String testGroupId) throws MojoExecutionException {
- final List<String> instanceIds = new ArrayList<>();
- if (NO_TEST_GROUP.equals(testGroupId)) {
- instanceIds.add(getProductId());
- }
- for (TestGroup group : testGroups) {
- if (StringUtils.equals(group.getId(), testGroupId)) {
- instanceIds.addAll(group.getInstanceIds());
- }
- }
- if (ProductHandlerFactory.getIds().contains(testGroupId) && !instanceIds.contains(testGroupId)) {
- instanceIds.add(testGroupId);
- }
- if (instanceIds.isEmpty()) {
- getLog().warn("Unknown test group ID: " + testGroupId + " Detected IDs: " + getTestGroupIds());
- }
- return instanceIds;
- }
- protected final Set<String> getTestGroupIds() {
- return testGroups.stream()
- .map(TestGroup::getId)
- .filter(Objects::nonNull)
- .collect(toSet());
- }
- protected final List<Product> getProductsToExecute() throws MojoExecutionException {
- if (isNotBlank(testGroup)) {
- return getProductsForTestGroup(testGroup);
- }
- if (isNotBlank(instanceId)) {
- final Product product = getProductContexts().get(instanceId);
- if (product == null) {
- throw new MojoExecutionException("No product with instance ID '" + instanceId + "'");
- }
- return singletonList(product);
- }
- return singletonList(getProductContexts().get(getProductId()));
- }
- /**
- * Ensures that there are no port conflicts between products and raises an exception if there
- * are conflicts
- *
- * @param products two or more products, for which the configured ports should be validated
- * @throws MojoExecutionException if any of the configured ports collide between products
- * @since 8.0
- */
- void validatePortConfiguration(final List<Product> products) throws MojoExecutionException {
- final Map<Integer, ConfiguredPort> portsById = new HashMap<>();
- final MutableInt collisions = new MutableInt();
- products.stream()
- .flatMap(AbstractTestGroupsHandlerMojo::getConfiguredPorts)
- .filter(ConfiguredPort::isStatic) // Only verify statically-configured ports
- .forEach(configured -> {
- ConfiguredPort conflict = portsById.get(configured.port);
- if (conflict == null) {
- portsById.put(configured.port, configured);
- } else {
- getLog().error(String.format(PORT_CONFLICT_MESSAGE_FORMAT,
- configured.instanceId, configured.nodeIndex, configured.type, configured.port,
- conflict.type, conflict.instanceId, conflict.nodeIndex));
- collisions.increment();
- }
- });
- final int collisionCount = collisions.intValue();
- if (collisionCount != 0) {
- throw new MojoExecutionException(collisionCount + " port conflict" +
- ((collisionCount == 1) ? " was" : "s were") + " detected between the " +
- products.size() + " products in the '" + testGroup + "' test group");
- }
- }
- /**
- * Returns the {@link ConfiguredPort configured ports} for the given {@link Product product}.
- *
- * @param product the product for which to get the ports
- * @return the configured ports
- * @since 8.0
- */
- private static Stream<ConfiguredPort> getConfiguredPorts(final Product product) {
- final String instanceId = product.getInstanceId();
- final Collection<ConfiguredPort> configuredPorts = new ArrayList<>();
- final List<Node> nodes = product.getNodes();
- for (int i = 0; i < nodes.size(); i++) {
- final Node node = nodes.get(i);
- configuredPorts.addAll(getConfiguredPorts(instanceId, node, i, product.getProtocol()));
- }
- return configuredPorts.stream();
- }
- private static Collection<ConfiguredPort> getConfiguredPorts(
- final String instanceId, final Node node, final int nodeIndex, final String protocol) {
- return asList(
- new ConfiguredPort(instanceId, node.getWebPort(), protocol.toUpperCase(), nodeIndex),
- new ConfiguredPort(instanceId, node.getAjpPort(), "AJP", nodeIndex),
- new ConfiguredPort(instanceId, node.getRmiPort(), "RMI", nodeIndex)
- );
- }
- /**
- * Describes a configured port for a {@link Product}, detailing the instance ID as well as the port and its type.
- * <p>
- * This data class simplifies maintaining an instance+port+type association, which facilitates using descriptive
- * error messages when the configured ports for two instances collide.
- *
- * @since 8.0
- */
- @ParametersAreNonnullByDefault
- private static class ConfiguredPort {
- private final String instanceId;
- private final int port;
- private final String type;
- private final int nodeIndex;
- ConfiguredPort(final String instanceId, final int port, final String type, final int nodeIndex) {
- this.instanceId = requireNonNull(instanceId, "instanceId");
- this.port = port;
- this.type = requireNonNull(type, "type");
- this.nodeIndex = nodeIndex;
- }
- boolean isStatic() {
- return port != 0;
- }
- }
- }