
org.daisy.pipeline.pax.exam.Options Maven / Gradle / Ivy
package org.daisy.pipeline.pax.exam;
import java.io.File;
import java.io.FileInputStream;
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.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.List;
import java.util.Map;
import java.util.Properties;
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.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuildingResult;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.apache.maven.settings.Settings;
import org.apache.maven.wagon.providers.file.FileWagon;
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.graph.Exclusion;
import org.sonatype.aether.repository.LocalRepository;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.repository.RepositoryPolicy;
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 DEFAULT_LOCAL_REPOSITORY = new File(System.getProperty("user.home"), ".m2/repository");
public static SystemPropertyOption logbackConfigFile() {
File logbackXml = new File(PathUtils.getBaseDir(), "src/test/resources/logback.xml");
if (logbackXml.exists())
return systemProperty("logback.configurationFile").value("file:" + logbackXml);
else
return null;
}
public static Option calabashConfigFile() {
File configFile = new File(PathUtils.getBaseDir() + "/src/test/resources/config-calabash.xml");
return composite(
configFile.exists()
? systemProperty("org.daisy.pipeline.xproc.configuration").value(configFile.toString())
: null,
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").versionAsInProject();
}
public static Option spiflyBundles() {
return composite(
mavenBundle().groupId("org.ow2.asm").artifactId("asm-all").version("5.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.2")
);
}
public static MavenBundle logbackClassic() {
// fixed version that is compatible with slf4j version used by Pipeline
// can not be put on the class path because Pax Exam itself depends on version 0.9.30
return mavenBundle("ch.qos.logback:logback-classic:1.0.11");
}
public static MavenBundleOption xprocspec() {
return mavenBundles(
mavenBundle("org.daisy.maven:xprocspec-runner:?"),
mavenBundle("org.daisy.xprocspec:xprocspec:?")
);
}
public static MavenBundle xspec() {
return mavenBundle("org.daisy.maven:xspec-runner:?");
}
// Note that thisBundle() may point to "target/classes" without really knowing whether it
// contains all the resources (it only checks for the OSGI-INF files listed in
// Service-Component). Therefore if resources are added it is important that this is done in
// such a way that they end up in target/classes. For instance, only resources that exist in
// the "generate-resources" phase are automatically copied to target/classes. If they are
// generated in the "process-resources" phase you are responsible for copying them to
// target/classes yourself.
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()) {
Properties dependencies = new Properties();
try {
dependencies.load(new FileInputStream(new File(classes, "META-INF/maven/dependencies.properties"))); }
catch (IOException e) {
throw new RuntimeException(e); }
String artifactId = dependencies.getProperty("artifactId");
String version = dependencies.getProperty("version");
// assuming JAR is named ${artifactId}-${version}.jar
return bundle("reference:" + new File(PathUtils.getBaseDir() + "/target/" + artifactId + "-" + version + ".jar").toURI()); }
return bundle("reference:" + classes.toURI());
}
public static Option systemProperties(Properties properties) {
Set keys = properties.stringPropertyNames();
SystemPropertyOption[] options = new SystemPropertyOption[keys.size()];
int i = 0;
for (String key : keys)
options[i++] = systemProperty(key).value(properties.getProperty(key));
return composite(options);
}
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)
try {
version = MavenUtils.asInProject().getVersion(groupId, artifactId); }
catch (Throwable e) {
logger.error("Could not find version of " + groupId + ":" + artifactId + " in Maven project");
throw new RuntimeException("Could not find version of " + groupId + ":" + artifactId + " in Maven project"); }
bundle.version(version);
if (startLevel > 0) {
bundle.startLevel(startLevel);
}
// special handling of xprocspec
if (groupId.equals("org.daisy.xprocspec") && artifactId.equals("xprocspec")) {
String osgiVersion = "";
int i = 0;
for (String segment : version.split("[\\.-]")) {
if (i > 3)
osgiVersion += "-";
else if (i > 0)
osgiVersion += ".";
i++;
osgiVersion += segment;
}
url = wrappedBundle(bundle)
.bundleSymbolicName("org.daisy.xprocspec")
.bundleVersion(osgiVersion)
.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;
private Set exclusions = new HashSet();
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;
}
private int startLevel = -1;
public MavenBundle startLevel(int level) {
if (level <= 0) {
throw new IllegalArgumentException("start level must be > 0");
}
this.startLevel = level;
return this;
}
public MavenBundle exclusion(String groupId, String artifactId) {
exclusions.add(new Exclusion(groupId, artifactId, null, "jar"));
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()));
if (startLevel > 0)
sb.append("(start@").append(startLevel).append(")");
sb.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());
}
}
public static MavenBundleOption mavenBundles(final MavenBundleOption... options) {
final MavenBundle[] bundles; {
List list = new ArrayList();
for (MavenBundleOption o : options)
if (o != null)
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();
}
};
}
public static MavenBundleOption mavenBundles(final String... artifactCoords) {
MavenBundle[] bundles = new MavenBundle[artifactCoords.length];
for (int i = 0; i < artifactCoords.length; i++)
bundles[i] = artifactCoords == null ? null : mavenBundle(artifactCoords[i]);
return mavenBundles(bundles);
}
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) {
if (o == null) continue;
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() {
try {
if (bundles == null) {
Set set = resolveBundles(fromBundles);
bundles = set.toArray(new MavenBundle[set.size()]); }
return bundles; }
catch (RuntimeException e) {
e.printStackTrace();
throw e; }
}
private static Set resolveBundles(List fromBundles) {
File settingsFile; {
// For now don't use "org.ops4j.pax.url.mvn.settings" because Pax Exam itself does not support
// system properties inside a settings file.
String prop = System.getProperty("org.daisy.org.ops4j.pax.url.mvn.settings");
if (prop == null)
// Also support "org.ops4j.pax.url.mvn.settings"
// Assume this is set only when no system properties are used in the settings file
prop = System.getProperty("org.ops4j.pax.url.mvn.settings");
// Also support environment variables for more flexibility
if (prop == null)
prop = System.getenv().get("org.daisy.org.ops4j.pax.url.mvn.settings");
if (prop == null) {
prop = System.getenv().get("org.ops4j.pax.url.mvn.settings");
if (prop != null)
System.setProperty("org.ops4j.pax.url.mvn.settings", prop); }
if (prop != null)
settingsFile = new File(prop);
else
settingsFile = new File(System.getProperty("user.home"), ".m2/settings.xml"); }
File localRepository;
List repositories; {
localRepository = null;
repositories = new Vector();
boolean centralRedefined = false;
String localRepositoryProp = System.getProperty("org.ops4j.pax.url.mvn.localRepository");
if (localRepositoryProp != null)
localRepository = new File(localRepositoryProp);
if (settingsFile.exists()) {
Settings settings; {
SettingsBuilder b = new DefaultSettingsBuilderFactory().newInstance();
SettingsBuildingRequest req = new DefaultSettingsBuildingRequest();
req.setGlobalSettingsFile(settingsFile);
req.setSystemProperties(System.getProperties());
try {
SettingsBuildingResult res = b.build(req);
settings = res.getEffectiveSettings(); }
catch (SettingsBuildingException e) {
throw new RuntimeException(e); }}
if (localRepository == null)
if (settings.getLocalRepository() != null)
localRepository = new File(settings.getLocalRepository());
for (Profile profile : settings.getProfiles()) {
String profileName = profile.getId();
if ((settings.getActiveProfiles() != null && settings.getActiveProfiles().contains(profileName))
|| (profile.getActivation() != null && profile.getActivation().isActiveByDefault())) {
for (Repository repo : profile.getRepositories()) {
RemoteRepository remoteRepo; {
remoteRepo = new RemoteRepository(repo.getId(), "default", repo.getUrl());
remoteRepo.setPolicy(false,
repo.getReleases() != null ?
new RepositoryPolicy(
repo.getReleases().isEnabled(),
repo.getReleases().getUpdatePolicy(),
repo.getReleases().getChecksumPolicy()) :
new RepositoryPolicy());
remoteRepo.setPolicy(true,
repo.getSnapshots() != null ?
new RepositoryPolicy(
repo.getSnapshots().isEnabled(),
repo.getSnapshots().getUpdatePolicy(),
repo.getSnapshots().getChecksumPolicy()) :
new RepositoryPolicy()); }
if ("central".equals(repo.getId()))
centralRedefined = true;
repositories.add(remoteRepo); }}}}
if (localRepository == null)
localRepository = DEFAULT_LOCAL_REPOSITORY;
if (!centralRedefined)
repositories.add(new RemoteRepository("central", "default", "http://repo1.maven.org/maven2/")); }
CollectRequest request = new CollectRequest();
for (MavenBundle bundle : fromBundles)
request.addDependency(new Dependency(bundle.asArtifact(), "runtime", false, bundle.exclusions));
for (RemoteRepository r : repositories)
request.addRepository(r);
request.setRequestContext("runtime");
DefaultServiceLocator locator = new DefaultServiceLocator();
locator.addService(WagonProvider.class, HttpAndFileWagonProvider.class);
locator.addService(RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class);
RepositorySystem system = locator.getService(RepositorySystem.class);
DefaultRepositorySystemSession session = new MavenRepositorySystemSession()
.setLocalRepositoryManager(
system.newLocalRepositoryManager(
new LocalRepository(localRepository.getAbsolutePath())))
.setOffline(false);
Set bundles = new HashSet();
DependencyNode root; {
try {
root = system.resolveDependencies(session, new DependencyRequest().setCollectRequest(request)).getRoot(); }
catch (DependencyResolutionException e) {
throw new RuntimeException(e); }}
int startLevel = 4;
for (DependencyNode n : root.getChildren())
if (!dependenciesAsBundles(bundles, n, false, fromBundles, null, startLevel++))
return resolveBundles(fromBundles);
return bundles;
}
private static boolean dependenciesAsBundles(Set bundles, DependencyNode node, boolean versionAsInProject,
List fromBundles, Artifact parent, int startLevel) {
Dependency dep = node.getDependency();
if (dep == null)
throw new RuntimeException("Coding error");
Artifact 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")))) {
if ((classifier.equals("linux") || classifier.equals("mac") || classifier.equals("windows"))
&& !classifier.equals(thisPlatform()));
else {
boolean noStart = false;
if (!(groupId.equals("org.daisy.xprocspec") && artifactId.equals("xprocspec")))
noStart = validateBundleAndIsFragmentBundle(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.getBaseVersion().equals(b.version))
throw new RuntimeException("Coding error");
versionAsInProject = b.versionAsInProject;
if (b.startLevel > 0)
startLevel = b.startLevel;
break; }
if (versionAsInProject) {
String versionInProject; {
versionInProject = null;
try {
versionInProject = MavenUtils.asInProject().getVersion(groupId, artifactId); }
catch (RuntimeException e) {
logger.info("Can not find version of transitive dependency " + groupId + ":" + artifactId
+ " in project. Assuming it was explicitly excluded, therefore ignoring it.");
return true; }}
if (!a.getBaseVersion().equals(versionInProject)) {
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.getBaseVersion() + "\" (via \"" + artifactCoords(parent) + "\")");
b.startLevel(startLevel);
fromBundles.add(b);
return false; }}
MavenBundle b = new MavenBundle(a);
if (noStart)
b.noStart();
else
b.startLevel(startLevel);
bundles.add(b); }}}
catch (InvalidBundleException e) {
logger.info("Ignoring dependency " + groupId + ":" + artifactId + ": not a valid bundle."); }
for (DependencyNode n : node.getChildren())
if (!dependenciesAsBundles(bundles, n, versionAsInProject, fromBundles, a, startLevel))
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();
}
// throw exception if bundle is not valid, and return true if it is a fragment bundle
private static boolean validateBundleAndIsFragmentBundle(File bundle) throws InvalidBundleException {
JarFile jar = null;
try {
jar = new JarFile(bundle, false);
Manifest manifest = jar.getManifest();
if (manifest == null)
throw new InvalidBundleException("[" + bundle + "] is not a valid bundle: manifest is missing");
Attributes mainAttrs = manifest.getMainAttributes();
String bundleSymbolicName = mainAttrs.getValue("Bundle-SymbolicName");
String bundleName = mainAttrs.getValue("Bundle-Name");
if (bundleSymbolicName == null && bundleName == null)
throw new InvalidBundleException("[" + bundle + "] is not a valid bundle: Bundle-SymbolicName and Bundle-Name are missing");
return (mainAttrs.getValue("Fragment-Host") != null); }
catch (IOException e) {
throw new InvalidBundleException("[" + bundle + "] is not a valid bundle: failed reading jar", e); }
finally {
if (jar != null)
try {
jar.close(); }
catch (IOException e) {}}
}
public static class HttpAndFileWagonProvider implements WagonProvider {
public Wagon lookup(String roleHint) throws Exception {
if ("http".equals(roleHint) || "https".equals(roleHint))
return new HttpWagon();
else if ("file".equals(roleHint))
return new FileWagon();
return null;
}
public void release(Wagon wagon) {}
}
private static class InvalidBundleException extends Exception {
InvalidBundleException(String message) {
super(message);
}
InvalidBundleException(String message, Throwable cause) {
super(message, cause);
}
}
}
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();
}
public 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