/** * Copyright 2016 Red Hat, Inc, and individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.swarm.maven.plugin; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Holds the testing Maven project setup and is able to prepare the {@code testing-project} directory to the desired state. * This means filling the blanks in {@code pom.xml}, ensuring that only one {@code web.xml} exists in the project, * adjusting {@code Main.java} and making sure that excluded {@code .java} files are deleted. * * @see Packaging * @see Dependencies * @see Autodetection * @see IncludedTechnology * @see AdditionalDependency * @see AdditionalFraction */ public final class TestingProject { public final Packaging packaging; public final Dependencies dependencies; public final Autodetection autodetection; public final Set<IncludedTechnology> includedTechnologies; public final AdditionalDependency additionalDependency; public final AdditionalFraction additionalFraction; public TestingProject(Packaging packaging, Dependencies dependencies, Autodetection autodetection, IncludedTechnology[] includedTechnologies, AdditionalDependency additionalDependency, AdditionalFraction additionalFraction) { this.packaging = packaging; this.dependencies = dependencies; this.autodetection = autodetection; Set<IncludedTechnology> techs = new HashSet<>(Arrays.asList(includedTechnologies)); // to ensure that the test is well-formed: the intended set of included technologies must respect fraction dependencies for (IncludedTechnology includedTechnology : includedTechnologies) { if (!techs.containsAll(includedTechnology.dependsOn())) { throw new IllegalArgumentException("If you want to include " + includedTechnology + ", you also have to include " + includedTechnology.dependsOn()); } } if (packaging == Packaging.JAR_WITH_MAIN && !techs.contains(IncludedTechnology.SERVLET)) { throw new IllegalArgumentException("Project with JAR packaging needs SERVLET for the custom Main class"); } this.includedTechnologies = Collections.unmodifiableSet(techs); this.additionalDependency = additionalDependency; this.additionalFraction = additionalFraction; } public void prepare(Path dir) throws IOException { String swarmVersion = System.getProperty("project.version"); // from Maven // pom.xml Path pomXml = dir.resolve("pom.xml"); String pomXmlContent = new String(Files.readAllBytes(pomXml), StandardCharsets.UTF_8) .replace("<!--PLACEHOLDER:swarm-version-->", swarmVersion) .replace("<!--PLACEHOLDER:packaging-->", packaging.packagingType()) .replace("<!--PLACEHOLDER:dependencies-->", dependenciesSnippet()) .replace("<!--PLACEHOLDER:configuration-->", swarmPluginConfigurationSnippet()); Files.write(pomXml, pomXmlContent.getBytes(StandardCharsets.UTF_8)); // web.xml if (packaging == Packaging.WAR || packaging == Packaging.WAR_WITH_MAIN) { Files.delete(dir.resolve(Paths.get("src", "main", "resources", "web.xml"))); Files.delete(dir.resolve(Paths.get("src", "main", "resources"))); } else if (packaging == Packaging.JAR_WITH_MAIN) { Files.delete(dir.resolve(Paths.get("src", "main", "webapp", "WEB-INF", "web.xml"))); Files.delete(dir.resolve(Paths.get("src", "main", "webapp", "WEB-INF"))); Files.delete(dir.resolve(Paths.get("src", "main", "webapp"))); } else { throw new IllegalStateException("Unknown packaging " + packaging); } Path javaFiles = dir.resolve(Paths.get("src", "main", "java", "org", "wildfly", "swarm", "test")); Path testJavaFiles = dir.resolve(Paths.get("src", "test", "java", "org", "wildfly", "swarm", "test")); // Main.java Path mainClass = javaFiles.resolve("Main.java"); if (this.packaging.hasCustomMain()) { String mainClassContent = new String(Files.readAllBytes(mainClass), StandardCharsets.UTF_8); for (Packaging packaging : Packaging.values()) { // only retain one section /*BEGIN:custom main:<packaging>*/ ... /*END:custom main:<packaging>*/; // the one that corresponds to the project's packaging if (this.packaging == packaging) { continue; } String beginning = Pattern.quote("/*BEGIN:custom main:" + packaging + "*/"); String ending = Pattern.quote("/*END:custom main:" + packaging + "*/"); mainClassContent = mainClassContent.replaceAll("(?s)" + beginning + ".*?" + ending, ""); } Files.write(mainClass, mainClassContent.getBytes(StandardCharsets.UTF_8)); } else { Files.delete(mainClass); } // remaining *.java if (!includedTechnologies.contains(IncludedTechnology.SERVLET)) { Files.delete(javaFiles.resolve("HelloServlet.java")); Files.delete(testJavaFiles.resolve("HelloServletIT.java")); } if (!includedTechnologies.contains(IncludedTechnology.JAX_RS)) { Files.delete(javaFiles.resolve("HelloJaxrsApplication.java")); Files.delete(javaFiles.resolve("HelloJaxrsResource.java")); Files.delete(testJavaFiles.resolve("HelloJaxrsIT.java")); } if (!includedTechnologies.contains(IncludedTechnology.EJB)) { Files.delete(javaFiles.resolve("HelloEjbBean.java")); Files.delete(javaFiles.resolve("HelloEjbServlet.java")); Files.delete(testJavaFiles.resolve("HelloEjbIT.java")); } } private String dependenciesSnippet() { StringBuilder result = new StringBuilder(); if (packaging.hasCustomMain()) { result.append("<dependency>\n" + " <groupId>org.wildfly.swarm</groupId>\n" + " <artifactId>container</artifactId>\n" + "</dependency>\n"); } if (packaging == Packaging.JAR_WITH_MAIN && dependencies == Dependencies.JAVA_EE_APIS) { // for the Main class result.append("<dependency>\n" + " <groupId>org.wildfly.swarm</groupId>\n" + " <artifactId>undertow</artifactId>\n" + "</dependency>\n"); } for (IncludedTechnology technology : includedTechnologies) { result.append(technology.dependencySnippet(dependencies)); } result.append(additionalDependency.dependencySnippet()); return result.toString(); } private String swarmPluginConfigurationSnippet() { String result = "<fractionDetectMode>" + autodetection.toSwarmPluginValue() + "</fractionDetectMode>\n"; if (packaging.hasCustomMain()) { result += "<mainClass>org.wildfly.swarm.test.Main</mainClass>\n"; } Optional<String> anotherFraction = additionalFraction.shouldBringFraction(); if (anotherFraction.isPresent()) { result += "<fractions><fraction>" + anotherFraction.get() + "</fraction></fractions>\n"; } return result; } public boolean hasExplicitFractionDependencies() { // with custom main, org.wildfly.swarm:container is always added, and that's an explicit fraction dependency return dependencies == Dependencies.FRACTIONS || packaging.hasCustomMain(); } public boolean doesAutodetectionHappen() { switch (autodetection) { case FORCE: return true; case NEVER: return false; case WHEN_MISSING: return !hasExplicitFractionDependencies(); default: throw new AssertionError(); } } public boolean canRunTests() { if (dependencies == Dependencies.JAVA_EE_APIS) { return doesAutodetectionHappen(); } return true; } public Set<String> fractionsThatShouldBePresent() { Set<String> expectedFractions = includedTechnologies .stream() .map(IncludedTechnology::fraction) .collect(Collectors.toCollection(HashSet::new)); if (doesAutodetectionHappen()) { additionalDependency.shouldBringFraction().ifPresent(expectedFractions::add); } if (dependencies == Dependencies.JAVA_EE_APIS && !doesAutodetectionHappen()) { expectedFractions = new HashSet<>(); if (packaging == Packaging.JAR_WITH_MAIN) { // always included for the custom main class expectedFractions.add(IncludedTechnology.SERVLET.fraction()); } } additionalFraction.shouldBringFraction().ifPresent(expectedFractions::add); return expectedFractions; } public Set<String> fractionsThatShouldBeMissing() { Set<String> allFractions = Stream.of(IncludedTechnology.values()) .map(IncludedTechnology::fraction) .collect(Collectors.toCollection(HashSet::new)); allFractions.addAll(AdditionalDependency.allPossibleFractions()); allFractions.addAll(AdditionalFraction.allPossibleFractions()); allFractions.removeAll(fractionsThatShouldBePresent()); return allFractions; } @Override public String toString() { return "packaging: " + packaging + ", dependencies: " + dependencies + ", autodetection: " + autodetection + ", technologies: " + includedTechnologies + ", additional dependency: " + additionalDependency + ", additional fraction: " + additionalFraction; } // --- public String serialize() { String technologies = includedTechnologies.stream() .map(IncludedTechnology::toString) .collect(Collectors.joining(",")); return packaging + ":" + dependencies + ":" + autodetection + ":" + technologies + ":" + additionalDependency + ":" + additionalFraction; } public static TestingProject deserialize(String string) { String[] parts = string.split(":"); Packaging packaging = Packaging.valueOf(parts[0]); Dependencies dependencies = Dependencies.valueOf(parts[1]); Autodetection autodetection = Autodetection.valueOf(parts[2]); IncludedTechnology[] includedTechnologies = Arrays.stream(parts[3].split(",")) .map(IncludedTechnology::valueOf) .toArray(IncludedTechnology[]::new); AdditionalDependency additionalDependency = AdditionalDependency.valueOf(parts[4]); AdditionalFraction additionalFraction = AdditionalFraction.valueOf(parts[5]); return new TestingProject(packaging, dependencies, autodetection, includedTechnologies, additionalDependency, additionalFraction); } }