package org.wildfly.swarm.arquillian.adapter; import java.io.IOException; import java.net.URI; import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.CodeSource; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription; import org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator; import org.jboss.arquillian.test.spi.TestClass; import org.jboss.shrinkwrap.api.Archive; import org.jboss.shrinkwrap.api.ArchivePath; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.FileAsset; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.impl.base.URLPackageScanner; import org.jboss.shrinkwrap.impl.base.asset.AssetUtil; import org.jboss.shrinkwrap.impl.base.path.BasicPath; import org.wildfly.swarm.arquillian.DefaultDeployment; import org.wildfly.swarm.spi.api.DependenciesContainer; import org.wildfly.swarm.spi.api.JARArchive; import org.wildfly.swarm.spi.api.MarkerContainer; import org.wildfly.swarm.spi.api.annotations.DeploymentModule; import org.wildfly.swarm.spi.api.annotations.DeploymentModules; /** * @author Bob McWhirter */ public class DefaultDeploymentScenarioGenerator extends AnnotationDeploymentScenarioGenerator { @Override public List<DeploymentDescription> generate(TestClass testClass) { DefaultDeployment anno = testClass.getAnnotation(DefaultDeployment.class); if (anno == null) { return super.generate(testClass); } String classPrefix = ( anno.type() == DefaultDeployment.Type.JAR ? "" : "WEB-INF/classes" ); Archive<?> archive; if (DefaultDeployment.Type.WAR.equals(anno.type())) { WebArchive webArchive = ShrinkWrap.create(WebArchive.class, testClass.getJavaClass().getSimpleName() + ".war"); // Add the marker to also include the project dependencies MarkerContainer.addMarker(webArchive, DependenciesContainer.ALL_DEPENDENCIES_MARKER); archive = webArchive; } else { archive = ShrinkWrap.create(JavaArchive.class, testClass.getJavaClass().getSimpleName() + ".jar"); } ClassLoader cl = testClass.getJavaClass().getClassLoader(); Set<CodeSource> codeSources = new HashSet<>(); URLPackageScanner.Callback callback = (className, asset) -> { ArchivePath classNamePath = AssetUtil.getFullPathForClassResource(className); ArchivePath location = new BasicPath(classPrefix, classNamePath); archive.add(asset, location); try { Class<?> cls = cl.loadClass(className); codeSources.add(cls.getProtectionDomain().getCodeSource()); } catch (ClassNotFoundException | NoClassDefFoundError e) { //e.printStackTrace(); } }; URLPackageScanner scanner = URLPackageScanner.newInstance( true, cl, callback, testClass.getJavaClass().getPackage().getName()); scanner.scanPackage(); Set<String> prefixes = codeSources.stream().map(e -> e.getLocation().toExternalForm()).collect(Collectors.toSet()); try { List<URL> resources = Collections.list(cl.getResources("")); resources.stream() .filter(e -> { for (String prefix : prefixes) { if (e.toExternalForm().startsWith(prefix)) { return true; } } return false; }) .filter(e -> e.getProtocol().equals("file")) .map(e -> getPlatformPath(e.getPath())) .map(e -> Paths.get(e)) .filter(e -> Files.isDirectory(e)) .forEach(e -> { try { Files.walkFileTree(e, new SimpleFileVisitor<Path>() { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!file.toString().endsWith(".class")) { String location = javaSlashize(handleExceptionalCases(archive, e.relativize(file))); archive.add(new FileAsset(file.toFile()), location); } return super.visitFile(file, attrs); } }); } catch (IOException e1) { } }); } catch (IOException e) { throw new RuntimeException(e); } /* Map<ArchivePath, Node> content = archive.getContent(); for (ArchivePath each : content.keySet()) { System.err.println(" --> " + each); } */ DeploymentModules deploymentModules = testClass.getAnnotation(DeploymentModules.class); if (deploymentModules != null) { for (DeploymentModule each : deploymentModules.value()) { archive.as(JARArchive.class).addModule(each.name(), each.slot()); } } DeploymentModule deploymentModule = testClass.getAnnotation(DeploymentModule.class); if (deploymentModule != null) { archive.as(JARArchive.class).addModule(deploymentModule.name(), deploymentModule.slot()); } DeploymentDescription description = new DeploymentDescription(testClass.getName(), archive); Class<?> mainClass = anno.main(); if (mainClass != Void.class) { archive.add(new StringAsset(mainClass.getName()), "META-INF/arquillian-main-class"); } description.shouldBeTestable(anno.testable()); return Collections.singletonList(description); } protected Path handleExceptionalCases(Archive archive, Path input) { if (archive.getName().endsWith(".war")) { String fileName = input.getFileName().toString(); if (META_INF_SPECIAL_CASES.contains(fileName)) { return input; } if (fileName.startsWith("project-") && fileName.endsWith(".yml")) { return input; } if (input.getName(0).toString().equals("WEB-INF")) { return input; } return Paths.get("WEB-INF", "classes").resolve(input); } return input; } protected String getPlatformPath(String path) { if (!isWindows()) { return path; } URI uri = URI.create("file://" + path); return Paths.get(uri).toString(); } protected boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("win"); } public String javaSlashize(Path path) { List<String> parts = new ArrayList<>(); int numParts = path.getNameCount(); for (int i = 0; i < numParts; ++i) { parts.add(path.getName(i).toString()); } return String.join("/", parts); } private static Set<String> META_INF_SPECIAL_CASES = new HashSet<String>() {{ add("jboss-deployment-structure.xml"); add("swarm.swagger.conf"); }}; }