package org.ops4j.pax.exam.maven; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Set; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Build; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException; import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts; import org.apache.maven.shared.artifact.filter.collection.ScopeFilter; import org.apache.maven.shared.artifact.filter.collection.TypeFilter; /** * For each dependency that has a manifest, this Mojo creates a file * {@literal ${project.build.directory}/pax-exam-links/BUNDLE_SYMBOLIC_NAME.link} * with content {@literal file:/path/to/local/maven/repo/.../artifactId.jar}. * This allows users to provision a bundle via * {@code bundle("link:classpath:META-INF/pax-exam-links/bundleSymbolicName.jar")}. * The {@code linkBundle(bundleSymbolicName)} option serves as a shortcut. */ @Mojo(name = "generate-link-files", defaultPhase = LifecyclePhase.GENERATE_TEST_RESOURCES, requiresDependencyResolution = ResolutionScope.TEST) public class GenerateLinkFilesMojo extends AbstractMojo { @Parameter(defaultValue = "${project.artifacts}", readonly = true, required = true) private Set<Artifact> projectArtifacts; @Parameter(defaultValue = "${project.build}", readonly = true, required = true) private Build build; /** * Where to put the link files. */ @Parameter(defaultValue = "${project.build.directory}/pax-exam-links") private File outputDirectory; @SuppressWarnings("unchecked") public void execute() throws MojoExecutionException, MojoFailureException { FilterArtifacts filter = new FilterArtifacts(); filter.addFilter(new ScopeFilter(Artifact.SCOPE_TEST, null)); filter.addFilter(new TypeFilter("jar", null)); Set<Artifact> artifacts; try { artifacts = filter.filter(projectArtifacts); } catch (ArtifactFilterException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } // create output directory with link files outputDirectory.mkdirs(); for (Artifact artifact : artifacts) { Manifest manifest = getManifest(artifact); if (manifest != null) { createLinkFile(artifact, manifest); } } // add output directory as test resource directory Resource resource = new Resource(); resource.setDirectory(outputDirectory.toString()); build.addTestResource(resource); } private void createLinkFile(Artifact artifact, Manifest manifest) throws MojoExecutionException { String symbolicName = manifest.getMainAttributes().getValue("Bundle-SymbolicName"); if (symbolicName != null) { int idx = symbolicName.indexOf(';'); if (idx != -1) { symbolicName = symbolicName.substring(0, idx); } File linkFile = new File(outputDirectory, symbolicName.trim() + ".link"); try { PrintWriter out = new PrintWriter(new FileOutputStream(linkFile), false); try { out.write(artifact.getFile().toURI().toString()); } finally { out.close(); } } catch (IOException ex) { throw new MojoExecutionException("Failed to create " + linkFile, ex); } } } private Manifest getManifest(Artifact artifact) { Manifest manifest = null; try { InputStream in = new FileInputStream(artifact.getFile()); try { ZipInputStream zip = new ZipInputStream(in); ZipEntry entry; while ((entry = zip.getNextEntry()) != null) { if (entry.getName().equals("META-INF/MANIFEST.MF")) { manifest = new Manifest(zip); break; } } } finally { in.close(); } } catch (IOException ex) { getLog().error("Unable to read " + artifact.getFile(), ex); } return manifest; } }