All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.pipeline.pax.exam.Options Maven / Gradle / Ivy

package org.daisy.pipeline.pax.exam;

import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import static java.util.Collections.sort;
import java.util.HashSet;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.apache.maven.repository.internal.DefaultServiceLocator;
import org.apache.maven.repository.internal.MavenRepositorySystemSession;
import org.apache.maven.wagon.providers.http.HttpWagon;
import org.apache.maven.wagon.Wagon;

import static org.ops4j.pax.exam.CoreOptions.bundle;
import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.systemPackage;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
import org.ops4j.pax.exam.MavenUtils;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.options.AbstractProvisionOption;
import org.ops4j.pax.exam.options.CompositeOption;
import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
import org.ops4j.pax.exam.options.MavenUrlReference.VersionResolver;
import org.ops4j.pax.exam.options.SystemPackageOption;
import org.ops4j.pax.exam.options.SystemPropertyOption;
import org.ops4j.pax.exam.options.UrlProvisionOption;
import org.ops4j.pax.exam.util.PathUtils;

import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.collection.CollectRequest;
import org.sonatype.aether.connector.wagon.WagonProvider;
import org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory;
import org.sonatype.aether.graph.Dependency;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.repository.LocalRepository;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.resolution.DependencyRequest;
import org.sonatype.aether.resolution.DependencyResolutionException;
import org.sonatype.aether.spi.connector.RepositoryConnectorFactory;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import org.sonatype.aether.util.DefaultRepositorySystemSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Options {
	
	private static final Logger logger = LoggerFactory.getLogger(Options.class);
	
	private static final File LOCAL_REPOSITORY = new File(System.getProperty("user.home"), ".m2/repository");
	
	public static SystemPropertyOption logbackConfigFile() {
		return systemProperty("logback.configurationFile").value("file:" + PathUtils.getBaseDir() + "/src/test/resources/logback.xml");
	}
	
	public static Option calabashConfigFile() {
		return composite(
			systemProperty("org.daisy.pipeline.xproc.configuration").value(PathUtils.getBaseDir() + "/src/test/resources/config-calabash.xml"),
			systemProperty("com.xmlcalabash.config.user").value("")
		);
	}
	
	public static SystemPackageOption domTraversalPackage() {
		return systemPackage("org.w3c.dom.traversal;uses:=\"org.w3c.dom\";version=\"0.0.0.1\"");
	}
	
	public static MavenBundle felixDeclarativeServices() {
		return mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.scr").version("1.6.2");
	}
	
	public static Option spiflyBundles() {
		return composite(
			mavenBundle().groupId("org.ow2.asm").artifactId("asm-all").version("4.0"),
			mavenBundle().groupId("org.apache.aries").artifactId("org.apache.aries.util").version("1.0.0"),
			mavenBundle().groupId("org.apache.aries.spifly").artifactId("org.apache.aries.spifly.dynamic.bundle").version("1.0.0")
		);
	}
	
	public static MavenBundle logbackClassic() {
		return mavenBundle("ch.qos.logback:logback-classic:1.0.11");
	}
	
	public static MavenBundleOption xprocspec() {
		return mavenBundleComposite(
			mavenBundle("org.daisy.maven:xprocspec-runner:?"),
			mavenBundle("org.daisy.xprocspec:xprocspec:?")
		);
	}
	
	public static MavenBundle xspec() {
		return mavenBundle("org.daisy.maven:xspec-runner:?");
	}
	
	public static UrlProvisionOption thisBundle() {
		File classes = new File(PathUtils.getBaseDir() + "/target/classes");
		Manifest manifest;
		try {
			InputStream stream = new File(classes, "META-INF/MANIFEST.MF").toURI().toURL().openStream();
			try {
				manifest = new Manifest(stream); }
			finally {
				stream.close(); }}
		catch (IOException e) {
			throw new RuntimeException(e); }
		String components = manifest.getMainAttributes().getValue("Service-Component");
		if (components != null)
			for (String component : components.split(","))
				if (!(new File(classes, component)).exists())
					return bundle("reference:"
					              + (new File(PathUtils.getBaseDir() + "/target/")).listFiles(
					                  new FilenameFilter() {
					                      public boolean accept(File dir, String name) {
					                          return name.endsWith(".jar"); }}
						              )[0].toURI());
		return bundle("reference:" + classes.toURI());
	}
	
	public static MavenBundle pipelineModule(String artifactId) {
		return mavenBundle().groupId("org.daisy.pipeline.modules").artifactId(artifactId);
	}
	
	public static MavenBundle brailleModule(String artifactId) {
		return mavenBundle().groupId("org.daisy.pipeline.modules.braille").artifactId(artifactId);
	}
	
	public static MavenBundle mavenBundle() {
		return new MavenBundle();
	}
	
	/**
	 * @param artifactCoords must be a string of the form
	 *    :[:[:]]:. The default  is
	 *    "jar". When  is "?", the version as declared in the project is used.
	 */
	public static MavenBundle mavenBundle(String artifactCoords) {
		return new MavenBundle(artifactFromCoords(artifactCoords));
	}
	
	public static interface MavenBundleOption extends Option {
		public MavenBundle[] getBundles();
	}
	
	public static class MavenBundle extends AbstractProvisionOption implements MavenBundleOption {
		
		private boolean versionAsInProject = true;
		
		private MavenBundle() {}
		
		private MavenBundle(Artifact artifact) {
			this(artifact, false);
		}
		
		private MavenBundle(Artifact artifact, boolean forceVersionAsInProject) {
			groupId(artifact.getGroupId());
			artifactId(artifact.getArtifactId());
			type(artifact.getExtension());
			classifier(artifact.getClassifier());
			if (!forceVersionAsInProject)
				version(artifact.getVersion());
		}
		
		private String url = null;
		
		public String getURL() {
			if (url == null) {
				MavenArtifactProvisionOption bundle = new MavenArtifactProvisionOption();
				bundle.groupId(groupId);
				bundle.artifactId(artifactId);
				if (type != null)
					bundle.type(type);
				if (classifier != null && !"".equals(classifier))
					bundle.classifier(classifier);
				if (versionAsInProject)
					version = MavenUtils.asInProject().getVersion(groupId, artifactId);
				bundle.version(version);
				// special handling of xprocspec
				if (groupId.equals("org.daisy.xprocspec") && artifactId.equals("xprocspec"))
					url = wrappedBundle(bundle)
						.bundleSymbolicName("org.daisy.xprocspec")
						.bundleVersion(version.replaceAll("-","."))
						.getURL();
				else
					url = bundle.getURL(); }
			return url;
		}
		
		public MavenBundle[] getBundles() {
			return new MavenBundle[]{this};
		}
			
		public MavenBundle itself() {
			return this;
		}
		
		private String groupId = null;
		private String artifactId = null;
		private String type = "jar";
		private String classifier = "";
		private String version = null;
		
		public MavenBundle groupId(String groupId) {
			checkURLResolved();
			this.groupId = groupId;
			return this;
		}
		
		public MavenBundle artifactId(String artifactId) {
			checkURLResolved();
			this.artifactId = artifactId;
			return this;
		}
		
		public MavenBundle type(String type) {
			checkURLResolved();
			this.type = type;
			return this;
		}
		
		public MavenBundle classifier(String classifier) {
			checkURLResolved();
			this.classifier = classifier;
			return this;
		}
		
		public MavenBundle forThisPlatform() {
			return classifier(thisPlatform());
		}
		
		public MavenBundle version(String version) {
			checkURLResolved();
			if (version == null || version.equals("?"))
				versionAsInProject = true;
			else {
				this.version = version;
				versionAsInProject = false; }
			return this;
		}
		
		public MavenBundle versionAsInProject() {
			return version("?");
		}
		
		private Artifact asArtifact() {
			getURL();
			return new DefaultArtifact(groupId, artifactId, classifier, type, version);
		}
		
		private void checkURLResolved() {
			if (url != null)
				throw new RuntimeException();
		}
		
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("mavenBundle(\"").append(artifactCoords(asArtifact())).append("\")");
			return sb.toString();
		}
		
		@Override
		public int hashCode() {
			return toString().hashCode();
		}
	
		@Override
		public boolean equals(Object object) {
			if (this == object)
				return true;
			if (object == null)
				return false;
			if (getClass() != object.getClass())
				return false;
			MavenBundle that = (MavenBundle)object;
			return that.toString().equals(toString());
		}
	}
	
	private static MavenBundleOption mavenBundleComposite(final MavenBundleOption... options) {
		final MavenBundle[] bundles; {
			List list = new ArrayList();
			for (MavenBundleOption o : options)
				for (MavenBundle b : o.getBundles())
					list.add(b);
			bundles = list.toArray(new MavenBundle[list.size()]); }
		return new MavenBundleCompositeOption() {
			public MavenBundle[] getBundles() {
				return bundles;
			}
			@Override
			public String toString() {
				StringBuilder sb = new StringBuilder();
				sb.append("compositeMavenBundleOption(");
				int i = 0;
				for (MavenBundleOption b : bundles) {
					if (i > 0) sb.append(",");
					sb.append("\n	").append(b);
					i++; }
				sb.append(")");
				return sb.toString();
			}
		};
	}
	
	private static abstract class MavenBundleCompositeOption implements MavenBundleOption, CompositeOption {
		public MavenBundle[] getOptions() {
			return getBundles();
		}
	}
	
	public static MavenBundleOption mavenBundlesWithDependencies(MavenBundleOption... options) {
		return new MavenBundlesWithDependencies(options);
	}
	
	private static class MavenBundlesWithDependencies extends MavenBundleCompositeOption {
		
		private final List fromBundles;
		
		private MavenBundlesWithDependencies(MavenBundleOption... options) {
			fromBundles = new ArrayList();
			for (MavenBundleOption o : options)
				for (MavenBundle b : o.getBundles())
					fromBundles.add(b);
			logger.info(this.toString());
			StringBuilder sb = new StringBuilder();
			List bundlesAsStrings = new ArrayList();
			for (MavenBundle b : getBundles())
				bundlesAsStrings.add(b.toString());
			sort(bundlesAsStrings);
			sb.append("resolved to: MavenBundle[]{");
			int i = 0;
			for (String b : bundlesAsStrings) {
				if (i > 0) sb.append(",");
				sb.append("\n	").append(b);
				i++; }
			sb.append("}");
			logger.info(sb.toString());
		}
		
		private MavenBundle[] bundles = null;
		
		public MavenBundle[] getBundles() {
			if (bundles == null) {
				Set set = resolveBundles(fromBundles);
				bundles = set.toArray(new MavenBundle[set.size()]); }
			return bundles;
		}
		
		private static Set resolveBundles(List fromBundles) {
			CollectRequest request = new CollectRequest();
			for (MavenBundle bundle : fromBundles) {
				request.addDependency(new Dependency(bundle.asArtifact(), "runtime")); }
			RemoteRepository central = new RemoteRepository("central", "default", "http://repo1.maven.org/maven2/");
			request.addRepository(central);
			request.setRequestContext("runtime");
			DefaultServiceLocator locator = new DefaultServiceLocator();
			locator.addService(WagonProvider.class, HttpWagonProvider.class);
			locator.addService(RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class);
			RepositorySystem system = locator.getService(RepositorySystem.class);
			DefaultRepositorySystemSession session = new MavenRepositorySystemSession()
				.setLocalRepositoryManager(
					system.newLocalRepositoryManager(
						new LocalRepository(LOCAL_REPOSITORY.getAbsolutePath())))
				.setOffline(false);
			List repositories = new Vector();
			repositories.add(central);
			Set bundles = new HashSet();
			try {
				if (dependenciesAsBundles(
					    bundles,
					    system.resolveDependencies(session, new DependencyRequest().setCollectRequest(request)).getRoot(),
					    false,
					    fromBundles,
					    null))
					return bundles;
				else
					return resolveBundles(fromBundles); }
			catch (DependencyResolutionException e) {
				throw new RuntimeException(e); }
		}
		
		private static boolean dependenciesAsBundles(Set bundles, DependencyNode node, boolean versionAsInProject,
		                                             List fromBundles, Artifact parent) {
			Dependency dep = node.getDependency();
			Artifact a = null;
			if (dep != null) {
				a = dep.getArtifact();
				String groupId = a.getGroupId();
				String artifactId = a.getArtifactId();
				String type = a.getExtension();
				String classifier = a.getClassifier();
				try {
					if (// these should not be runtime dependencies -> fix in POMs
						!(groupId.equals("org.osgi") && (artifactId.equals("org.osgi.compendium") || artifactId.equals("org.osgi.core")))
						// fragment bundles not supported
						&& !(groupId.equals("org.slf4j") && artifactId.equals("slf4j-jdk14"))
						) {
						if ((classifier.equals("linux") || classifier.equals("mac") || classifier.equals("windows"))
						    && !classifier.equals(thisPlatform()));
						else {
							if (!(groupId.equals("org.daisy.xprocspec") && artifactId.equals("xprocspec")))
								validateBundle(a.getFile());
							for (MavenBundle b : fromBundles)
								if (b.groupId.equals(groupId)
								    && b.artifactId.equals(artifactId)
								    && b.type.equals(type)
								    && b.classifier.equals(classifier)) {
									if (b.versionAsInProject && !a.getVersion().equals(b.version))
										throw new Exception("Coding error");
									versionAsInProject = b.versionAsInProject;
									break; }
							if (versionAsInProject
							    && !a.getVersion().equals(MavenUtils.asInProject().getVersion(groupId, artifactId))) {
								MavenBundle b = new MavenBundle(a, true);
								logger.info("Forcing transitive dependency \"" + artifactCoords(b.asArtifact()) + "\" (version as in project) "
								            + "because it would otherwise resolve to version \"" + a.getVersion() + "\" "
								            + "(through \"" + artifactCoords(parent) + "\")");
								fromBundles.add(b);
								return false; }
							bundles.add(new MavenBundle(a)); }}}
				catch(Exception e) {}}
			for (DependencyNode n : node.getChildren())
				if (!dependenciesAsBundles(bundles, n, versionAsInProject, fromBundles, a != null ? a : parent))
					return false;
			return true;
		}
		
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("mavenBundlesWithDependencies(");
			int i = 0;
			for (MavenBundle b : fromBundles) {
				if (i > 0) sb.append(",");
				sb.append("\n	").append(b);
				i++; }
			sb.append(")");
			return sb.toString();
		}
		
		private static void validateBundle(File bundle) {
			JarFile jar = null;
			try {
				jar = new JarFile(bundle, false);
				Manifest manifest = jar.getManifest();
				if (manifest == null)
					throw new RuntimeException("[" + bundle + "] is not a valid bundle: manifest is missing");
				String bundleSymbolicName = manifest.getMainAttributes().getValue("Bundle-SymbolicName");
				String bundleName = manifest.getMainAttributes().getValue("Bundle-Name");
				if (bundleSymbolicName == null && bundleName == null) {
					throw new RuntimeException("[" + bundle + "] is not a valid bundle: Bundle-SymbolicName and Bundle-Name are missing"); }}
			catch (IOException e) {
				throw new RuntimeException("[" + bundle + "] is not a valid bundle: failed reading jar", e); }
			finally {
				if (jar != null)
					try {
						jar.close(); }
					catch (IOException e) {}}
		}
		
		public static class HttpWagonProvider implements WagonProvider {
			public Wagon lookup(String roleHint) throws Exception {
				if ("http".equals(roleHint) || "https".equals(roleHint))
					return new HttpWagon();
				return null;
			}
			public void release(Wagon wagon) {}
		}
	}
	
	private static Artifact artifactFromCoords(String coords) {
		return new DefaultArtifact(coords);
	}
	
	private static String artifactCoords(Artifact artifact) {
		String groupId = artifact.getGroupId();
		String artifactId = artifact.getArtifactId();
		String extension = artifact.getExtension();
		String classifier = artifact.getClassifier();
		String version = artifact.getVersion();
		StringBuilder b = new StringBuilder()
			.append(groupId).append(":")
			.append(artifactId).append(":");
		if (!extension.equals("jar") || !classifier.equals("")) {
			b.append(extension).append(":");
			if (!classifier.equals(""))
				b.append(classifier).append(":"); }
		b.append(version);
		return b.toString();
	}
	
	private static String thisPlatform() {
		String name = System.getProperty("os.name").toLowerCase();
		if (name.startsWith("windows"))
			return "windows";
		else if (name.startsWith("mac os x"))
			return "mac";
		else if (name.startsWith("linux"))
			return "linux";
		else
			throw new RuntimeException("Unsupported OS: " + name);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy