Temporarily include bndlib 1.47 for testing purposes (not yet on central)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/BsnToMavenPath.java b/bundleplugin/src/main/java/aQute/bnd/maven/BsnToMavenPath.java
new file mode 100644
index 0000000..6dc716c
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/BsnToMavenPath.java
@@ -0,0 +1,5 @@
+package aQute.bnd.maven;
+
+public interface BsnToMavenPath {
+ String[] getGroupAndArtifact(String bsn);
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java
new file mode 100644
index 0000000..c274ca5
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenCommand.java
@@ -0,0 +1,615 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.bnd.maven.support.*;
+import aQute.bnd.maven.support.Pom.*;
+import aQute.bnd.settings.*;
+import aQute.lib.collections.*;
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+import aQute.libg.command.*;
+import aQute.libg.header.*;
+
+public class MavenCommand extends Processor {
+ final Settings settings = new Settings();
+ File temp;
+
+ public MavenCommand() {
+ }
+
+ public MavenCommand(Processor p) {
+ super(p);
+ }
+
+ /**
+ * maven deploy [-url repo] [-passphrase passphrase] [-homedir homedir]
+ * [-keyname keyname] bundle ...
+ *
+ * @param args
+ * @param i
+ * @throws Exception
+ */
+ public void run(String args[], int i) throws Exception {
+ temp = new File("maven-bundle");
+
+ if (i >= args.length) {
+ help();
+ return;
+ }
+
+ while (i < args.length && args[i].startsWith("-")) {
+ String option = args[i];
+ trace("option " + option);
+ if (option.equals("-temp"))
+ temp = getFile(args[++i]);
+ else {
+ help();
+ error("Invalid option " + option);
+ }
+ i++;
+ }
+
+ String cmd = args[i++];
+
+ trace("temp dir " + temp);
+ IO.delete(temp);
+ temp.mkdirs();
+ if (!temp.isDirectory())
+ throw new IOException("Cannot create temp directory");
+
+ if (cmd.equals("settings"))
+ settings();
+ else if (cmd.equals("help"))
+ help();
+ else if (cmd.equals("bundle"))
+ bundle(args, i);
+ else if (cmd.equals("view"))
+ view(args, i);
+ else
+ error("No such command %s, type help", cmd);
+ }
+
+ private void help() {
+ System.err.println("Usage:\n");
+ System.err
+ .println(" maven \n" //
+ + " [-temp <dir>] use as temp directory\n" //
+ + " settings show maven settings\n" //
+ + " bundle turn a bundle into a maven bundle\n" //
+ + " [-properties <file>] provide properties, properties starting with javadoc are options for javadoc, like javadoc-tag=...\n"
+ + " [-javadoc <file|url>] where to find the javadoc (zip/dir), otherwise generated\n" //
+ + " [-source <file|url>] where to find the source (zip/dir), otherwise from OSGI-OPT/src\n" //
+ + " [-scm <url>] required scm in pom, otherwise from Bundle-SCM\n" //
+ + " [-url <url>] required project url in pom\n" //
+ + " [-bsn bsn] overrides bsn\n" //
+ + " [-version <version>] overrides version\n" //
+ + " [-developer <email>] developer email\n" //
+ + " [-nodelete] do not delete temp files\n" //
+ + " [-passphrase <gpgp passphrase>] signer password\n"//
+ + " <file|url> ");
+ }
+
+ /**
+ * Show the maven settings
+ *
+ * @throws FileNotFoundException
+ * @throws Exception
+ */
+ private void settings() throws FileNotFoundException, Exception {
+ File userHome = new File(System.getProperty("user.home"));
+ File m2 = new File(userHome, ".m2");
+ if (!m2.isDirectory()) {
+ error("There is no m2 directory at %s", userHome);
+ return;
+ }
+ File settings = new File(m2, "settings.xml");
+ if (!settings.isFile()) {
+ error("There is no settings file at '%s'", settings.getAbsolutePath());
+ return;
+ }
+
+ FileReader rdr = new FileReader(settings);
+
+ LineCollection lc = new LineCollection(new BufferedReader(rdr));
+ while (lc.hasNext()) {
+ System.out.println(lc.next());
+ }
+ }
+
+ /**
+ * Create a maven bundle.
+ *
+ * @param args
+ * @param i
+ * @throws Exception
+ */
+ private void bundle(String args[], int i) throws Exception {
+ List<String> developers = new ArrayList<String>();
+ Properties properties = new Properties();
+
+ String scm = null;
+ String passphrase = null;
+ String javadoc = null;
+ String source = null;
+ String output = "bundle.jar";
+ String url = null;
+ String artifact = null;
+ String group = null;
+ String version = null;
+ boolean nodelete = false;
+
+ while (i < args.length && args[i].startsWith("-")) {
+ String option = args[i++];
+ trace("bundle option %s", option);
+ if (option.equals("-scm"))
+ scm = args[i++];
+ else if (option.equals("-group"))
+ group = args[i++];
+ else if (option.equals("-artifact"))
+ artifact = args[i++];
+ else if (option.equals("-version"))
+ version = args[i++];
+ else if (option.equals("-developer"))
+ developers.add(args[i++]);
+ else if (option.equals("-passphrase")) {
+ passphrase = args[i++];
+ } else if (option.equals("-url")) {
+ url = args[i++];
+ } else if (option.equals("-javadoc"))
+ javadoc = args[i++];
+ else if (option.equals("-source"))
+ source = args[i++];
+ else if (option.equals("-output"))
+ output = args[i++];
+ else if (option.equals("-nodelete"))
+ nodelete=true;
+ else if (option.startsWith("-properties")) {
+ InputStream in = new FileInputStream(args[i++]);
+ try {
+ properties.load(in);
+ } catch (Exception e) {
+ in.close();
+ }
+ }
+ }
+
+ if (developers.isEmpty()) {
+ String email = settings.globalGet(Settings.EMAIL, null);
+ if (email == null)
+ error("No developer email set, you can set global default email with: bnd global email Peter.Kriens@aQute.biz");
+ else
+ developers.add(email);
+ }
+
+ if (i == args.length) {
+ error("too few arguments, no bundle specified");
+ return;
+ }
+
+ if (i != args.length - 1) {
+ error("too many arguments, only one bundle allowed");
+ return;
+ }
+
+ String input = args[i++];
+
+ Jar binaryJar = getJarFromFileOrURL(input);
+ trace("got %s", binaryJar);
+ if (binaryJar == null) {
+ error("JAR does not exist: %s", input);
+ return;
+ }
+
+ File original = getFile(temp, "original");
+ original.mkdirs();
+ binaryJar.expand(original);
+ binaryJar.calcChecksums(null);
+
+ Manifest manifest = binaryJar.getManifest();
+ trace("got manifest");
+
+ PomFromManifest pom = new PomFromManifest(manifest);
+
+ if (scm != null)
+ pom.setSCM(scm);
+ if (url != null)
+ pom.setURL(url);
+ if (artifact != null)
+ pom.setArtifact(artifact);
+ if (artifact != null)
+ pom.setGroup(group);
+ if (version != null)
+ pom.setVersion(version);
+ trace(url);
+ for (String d : developers)
+ pom.addDeveloper(d);
+
+ Set<String> exports = OSGiHeader.parseHeader(
+ manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE)).keySet();
+
+ Jar sourceJar;
+ if (source == null) {
+ trace("Splitting source code");
+ sourceJar = new Jar("source");
+ for (Map.Entry<String, Resource> entry : binaryJar.getResources().entrySet()) {
+ if (entry.getKey().startsWith("OSGI-OPT/src")) {
+ sourceJar.putResource(entry.getKey().substring("OSGI-OPT/src/".length()),
+ entry.getValue());
+ }
+ }
+ copyInfo(binaryJar, sourceJar, "source");
+ } else {
+ sourceJar = getJarFromFileOrURL(source);
+ }
+ sourceJar.calcChecksums(null);
+
+ Jar javadocJar;
+ if (javadoc == null) {
+ trace("creating javadoc because -javadoc not used");
+ javadocJar = javadoc(getFile(original, "OSGI-OPT/src"), exports, manifest, properties);
+ if (javadocJar == null) {
+ error("Cannot find source code in OSGI-OPT/src to generate Javadoc");
+ return;
+ }
+ copyInfo(binaryJar, javadocJar, "javadoc");
+ } else {
+ trace("Loading javadoc externally %s", javadoc);
+ javadocJar = getJarFromFileOrURL(javadoc);
+ }
+ javadocJar.calcChecksums(null);
+
+ addClose(binaryJar);
+ addClose(sourceJar);
+ addClose(javadocJar);
+
+ trace("creating bundle dir");
+ File bundle = new File(temp, "bundle");
+ bundle.mkdirs();
+
+ String prefix = pom.getArtifactId() + "-" + pom.getVersion();
+ File binaryFile = new File(bundle, prefix + ".jar");
+ File sourceFile = new File(bundle, prefix + "-sources.jar");
+ File javadocFile = new File(bundle, prefix + "-javadoc.jar");
+ File pomFile = new File(bundle, "pom.xml").getAbsoluteFile();
+ trace("creating output files %s, %s,%s, and %s", binaryFile, sourceFile, javadocFile,
+ pomFile);
+
+ IO.copy(pom.openInputStream(), pomFile);
+ trace("copied pom");
+
+ trace("writing binary %s", binaryFile);
+ binaryJar.write(binaryFile);
+
+ trace("writing source %s", sourceFile);
+ sourceJar.write(sourceFile);
+
+ trace("writing javadoc %s", javadocFile);
+ javadocJar.write(javadocFile);
+
+ sign(binaryFile, passphrase);
+ sign(sourceFile, passphrase);
+ sign(javadocFile, passphrase);
+ sign(pomFile, passphrase);
+
+ trace("create bundle");
+ Jar bundleJar = new Jar(bundle);
+ addClose(bundleJar);
+ File outputFile = getFile(output);
+ bundleJar.write(outputFile);
+ trace("created bundle %s", outputFile);
+
+ binaryJar.close();
+ sourceJar.close();
+ javadocJar.close();
+ bundleJar.close();
+ if (!nodelete)
+ IO.delete(temp);
+ }
+
+ private void copyInfo(Jar source, Jar dest, String type) throws Exception {
+ source.ensureManifest();
+ dest.ensureManifest();
+ copyInfoResource( source, dest, "LICENSE");
+ copyInfoResource( source, dest, "LICENSE.html");
+ copyInfoResource( source, dest, "about.html");
+
+ Manifest sm = source.getManifest();
+ Manifest dm = dest.getManifest();
+ copyInfoHeader( sm, dm, "Bundle-Description","");
+ copyInfoHeader( sm, dm, "Bundle-Vendor","");
+ copyInfoHeader( sm, dm, "Bundle-Copyright","");
+ copyInfoHeader( sm, dm, "Bundle-DocURL","");
+ copyInfoHeader( sm, dm, "Bundle-License","");
+ copyInfoHeader( sm, dm, "Bundle-Name", " " + type);
+ copyInfoHeader( sm, dm, "Bundle-SymbolicName", "." + type);
+ copyInfoHeader( sm, dm, "Bundle-Version", "");
+ }
+
+ private void copyInfoHeader(Manifest sm, Manifest dm, String key, String value) {
+ String v = sm.getMainAttributes().getValue(key);
+ if ( v == null) {
+ trace("no source for " + key);
+ return;
+ }
+
+ if ( dm.getMainAttributes().getValue(key) != null) {
+ trace("already have " + key );
+ return;
+ }
+
+ dm.getMainAttributes().putValue(key, v + value);
+ }
+
+ private void copyInfoResource(Jar source, Jar dest, String type) {
+ if ( source.getResources().containsKey(type) && !dest.getResources().containsKey(type))
+ dest.putResource(type, source.getResource(type));
+ }
+
+ /**
+ * @return
+ * @throws IOException
+ * @throws MalformedURLException
+ */
+ protected Jar getJarFromFileOrURL(String spec) throws IOException, MalformedURLException {
+ Jar jar;
+ File jarFile = getFile(spec);
+ if (jarFile.exists()) {
+ jar = new Jar(jarFile);
+ } else {
+ URL url = new URL(spec);
+ InputStream in = url.openStream();
+ try {
+ jar = new Jar(url.getFile(), in);
+ } finally {
+ in.close();
+ }
+ }
+ addClose(jar);
+ return jar;
+ }
+
+ private void sign(File file, String passphrase) throws Exception {
+ trace("signing %s", file);
+ File asc = new File(file.getParentFile(), file.getName() + ".asc");
+ asc.delete();
+
+ Command command = new Command();
+ command.setTrace();
+
+ command.add(getProperty("gpgp", "gpg"));
+ if (passphrase != null)
+ command.add("--passphrase", passphrase);
+ command.add("-ab", "--sign"); // not the -b!!
+ command.add(file.getAbsolutePath());
+ System.out.println(command);
+ StringBuffer stdout = new StringBuffer();
+ StringBuffer stderr = new StringBuffer();
+ int result = command.execute(stdout, stderr);
+ if (result != 0) {
+ error("gpg signing %s failed because %s", file, "" + stdout + stderr);
+ }
+ }
+
+ private Jar javadoc(File source, Set<String> exports, Manifest m, Properties p)
+ throws Exception {
+ File tmp = new File(temp, "javadoc");
+ tmp.mkdirs();
+
+ Command command = new Command();
+ command.add(getProperty("javadoc", "javadoc"));
+ command.add("-quiet");
+ command.add("-protected");
+ // command.add("-classpath");
+ // command.add(binary.getAbsolutePath());
+ command.add("-d");
+ command.add(tmp.getAbsolutePath());
+ command.add("-charset");
+ command.add("UTF-8");
+ command.add("-sourcepath");
+ command.add(source.getAbsolutePath());
+
+ Attributes attr = m.getMainAttributes();
+ Properties pp = new Properties(p);
+ set(pp, "-doctitle", description(attr));
+ set(pp, "-header", description(attr));
+ set(pp, "-windowtitle", name(attr));
+ set(pp, "-bottom", copyright(attr));
+ set(pp, "-footer", license(attr));
+
+ command.add("-tag");
+ command.add("Immutable:t:Immutable");
+ command.add("-tag");
+ command.add("ThreadSafe:t:ThreadSafe");
+ command.add("-tag");
+ command.add("NotThreadSafe:t:NotThreadSafe");
+ command.add("-tag");
+ command.add("GuardedBy:mf:Guarded By:");
+ command.add("-tag");
+ command.add("security:m:Required Permissions");
+ command.add("-tag");
+ command.add("noimplement:t:Consumers of this API must not implement this interface");
+
+ for (Enumeration<?> e = pp.propertyNames(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ String value = pp.getProperty(key);
+
+ if (key.startsWith("javadoc")) {
+ key = key.substring("javadoc".length());
+ removeDuplicateMarker(key);
+ command.add(key);
+ command.add(value);
+ }
+ }
+ for (String packageName : exports) {
+ command.add(packageName);
+ }
+
+ StringBuffer out = new StringBuffer();
+ StringBuffer err = new StringBuffer();
+
+ System.out.println(command);
+
+ int result = command.execute(out, err);
+ if (result != 0) {
+ warning("Error during execution of javadoc command: %s\n******************\n%s", out,
+ err);
+ }
+ Jar jar = new Jar(tmp);
+ addClose(jar);
+ return jar;
+ }
+
+ /**
+ * Generate a license string
+ *
+ * @param attr
+ * @return
+ */
+ private String license(Attributes attr) {
+ Map<String, Map<String, String>> map = Processor.parseHeader(
+ attr.getValue(Constants.BUNDLE_LICENSE), null);
+ if (map.isEmpty())
+ return null;
+
+ StringBuilder sb = new StringBuilder();
+ String sep = "Licensed under ";
+ for (Map.Entry<String, Map<String, String>> entry : map.entrySet()) {
+ sb.append(sep);
+ String key = entry.getKey();
+ String link = entry.getValue().get("link");
+ String description = entry.getValue().get("description");
+
+ if (description == null)
+ description = key;
+
+ if (link != null) {
+ sb.append("<a href='");
+ sb.append(link);
+ sb.append("'>");
+ }
+ sb.append(description);
+ if (link != null) {
+ sb.append("</a>");
+ }
+ sep = ",<br/>";
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Generate the copyright statement.
+ *
+ * @param attr
+ * @return
+ */
+ private String copyright(Attributes attr) {
+ return attr.getValue(Constants.BUNDLE_COPYRIGHT);
+ }
+
+ private String name(Attributes attr) {
+ String name = attr.getValue(Constants.BUNDLE_NAME);
+ if (name == null)
+ name = attr.getValue(Constants.BUNDLE_SYMBOLICNAME);
+ return name;
+ }
+
+ private String description(Attributes attr) {
+ String descr = attr.getValue(Constants.BUNDLE_DESCRIPTION);
+ if (descr == null)
+ descr = attr.getValue(Constants.BUNDLE_NAME);
+ if (descr == null)
+ descr = attr.getValue(Constants.BUNDLE_SYMBOLICNAME);
+ return descr;
+ }
+
+ private void set(Properties pp, String option, String defaultValue) {
+ String key = "javadoc" + option;
+ String existingValue = pp.getProperty(key);
+ if (existingValue != null)
+ return;
+
+ pp.setProperty(key, defaultValue);
+ }
+
+
+ /**
+ * View - Show the dependency details of an artifact
+ */
+
+
+ static Executor executor = Executors.newCachedThreadPool();
+ static Pattern GROUP_ARTIFACT_VERSION = Pattern.compile("([^+]+)\\+([^+]+)\\+([^+]+)");
+ void view( String args[], int i) throws Exception {
+ Maven maven = new Maven(executor);
+ OutputStream out = System.out;
+
+ List<URI> urls = new ArrayList<URI>();
+
+ while ( i < args.length && args[i].startsWith("-")) {
+ if( "-r".equals(args[i])) {
+ URI uri = new URI(args[++i]);
+ urls.add( uri );
+ System.out.println("URI for repo " + uri);
+ }
+ else
+ if ( "-o".equals(args[i])) {
+ out = new FileOutputStream(args[++i]);
+ }
+ else
+ throw new IllegalArgumentException("Unknown option: " + args[i]);
+
+ i++;
+ }
+
+ URI[] urls2 = urls.toArray(new URI[urls.size()]);
+ PrintWriter pw = new PrintWriter(out);
+
+ while ( i < args.length) {
+ String ref = args[i++];
+ pw.println("Ref " + ref);
+
+ Matcher matcher = GROUP_ARTIFACT_VERSION.matcher(ref);
+ if (matcher.matches()) {
+
+ String group = matcher.group(1);
+ String artifact = matcher.group(2);
+ String version = matcher.group(3);
+ CachedPom pom = maven.getPom(group, artifact, version, urls2);
+
+ Builder a = new Builder();
+ a.setProperty("Private-Package", "*");
+ Set<Pom> dependencies = pom.getDependencies(Scope.compile, urls2);
+ for ( Pom dep : dependencies ) {
+ System.out.printf( "%20s %-20s %10s\n", dep.getGroupId(), dep.getArtifactId(), dep.getVersion());
+ a.addClasspath(dep.getArtifact());
+ }
+ pw.println(a.getClasspath());
+ a.build();
+
+ TreeSet<String> sorted = new TreeSet<String>( a.getImports().keySet());
+ for ( String p :sorted) {
+ pw.printf("%-40s\n",p);
+ }
+// for ( Map.Entry<String, Set<String>> entry : a.getUses().entrySet()) {
+// String from = entry.getKey();
+// for ( String uses : entry.getValue()) {
+// System.out.printf("%40s %s\n", from, uses);
+// from = "";
+// }
+// }
+ a.close();
+ } else
+ System.err.println("Wrong, must look like group+artifact+version, is " + ref);
+
+ }
+ }
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java
new file mode 100644
index 0000000..6278be2
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDependencyGraph.java
@@ -0,0 +1,139 @@
+package aQute.bnd.maven;
+
+import java.net.*;
+import java.util.*;
+
+import javax.xml.parsers.*;
+import javax.xml.xpath.*;
+
+import org.w3c.dom.*;
+
+public class MavenDependencyGraph {
+ final static DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ final static XPathFactory xpathFactory = XPathFactory.newInstance();
+ final List<Artifact> dependencies = new ArrayList<Artifact>();
+ final List<URL> repositories = new ArrayList<URL>();
+ final XPath xpath = xpathFactory.newXPath();
+ final Map<URL, Artifact> cache = new HashMap<URL, Artifact>();
+ Artifact root;
+
+ enum Scope {
+ COMPILE, RUNTIME, TEST, PROVIDED, SYSTEM, IMPORT,
+ };
+
+
+ public class Artifact {
+
+ String groupId;
+ String artifactId;
+ String version;
+ Scope scope = Scope.COMPILE;
+ boolean optional;
+ String type;
+ URL url;
+ List<Artifact> dependencies = new ArrayList<Artifact>();
+
+ public Artifact(URL url) throws Exception {
+ if (url != null) {
+ this.url = url;
+ DocumentBuilder db = docFactory.newDocumentBuilder();
+ Document doc = db.parse(url.toString());
+ Node node = (Node) xpath.evaluate("/project", doc, XPathConstants.NODE);
+
+ groupId = xpath.evaluate("groupId", node);
+ artifactId = xpath.evaluate("artifactId", node);
+ version = xpath.evaluate("version", node);
+ type = xpath.evaluate("type", node);
+ optional = (Boolean) xpath.evaluate("optinal", node, XPathConstants.BOOLEAN);
+ String scope = xpath.evaluate("scope", node);
+ if (scope != null && scope.length() > 0) {
+ this.scope = Scope.valueOf(scope.toUpperCase());
+ }
+ NodeList evaluate = (NodeList) xpath.evaluate("//dependencies/dependency", doc,
+ XPathConstants.NODESET);
+
+ for (int i = 0; i < evaluate.getLength(); i++) {
+ Node childNode = evaluate.item(i);
+ Artifact artifact = getArtifact(xpath.evaluate("groupId", childNode), xpath
+ .evaluate("artifactId", childNode), xpath.evaluate("version", childNode));
+ add(artifact);
+ }
+ }
+ }
+
+
+
+ public void add(Artifact artifact) {
+ dependencies.add(artifact);
+ }
+
+
+
+ public String toString() {
+ return groupId + "." + artifactId + "-" + version + "[" + scope + "," + optional + "]";
+ }
+
+ public String getPath() throws URISyntaxException {
+ return groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + artifactId
+ + "-" + version;
+ }
+
+ }
+
+ public void addRepository(URL repository) {
+ repositories.add(repository);
+ }
+
+ /**
+ * @param xp
+ * @param node
+ * @param d
+ * @throws XPathExpressionException
+ */
+
+ public Artifact getArtifact(String groupId, String artifactId, String version) {
+ for (URL repository : repositories) {
+ String path = getPath(repository.toString(), groupId, artifactId, version);
+
+ try {
+ URL url = new URL(path + ".pom");
+ if (cache.containsKey(url)) {
+ return cache.get(url);
+ } else {
+ return new Artifact(url);
+ }
+ } catch (Exception e) {
+ System.err.println("Failed to get " + artifactId + " from " + repository);
+ }
+ }
+ return null;
+ }
+
+ private String getPath(String path, String groupId, String artifactId, String version) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(path);
+ if (!path.endsWith("/"))
+ sb.append("/");
+
+ sb.append(groupId.replace('.', '/'));
+ sb.append('/');
+ sb.append(artifactId);
+ sb.append('/');
+ sb.append(version);
+ sb.append('/');
+ sb.append(artifactId);
+ sb.append('-');
+ sb.append(version);
+ return null;
+ }
+
+
+
+ public void addArtifact( Artifact artifact ) throws Exception {
+ if ( root == null)
+ root = new Artifact(null);
+ root.add(artifact);
+ }
+
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
new file mode 100644
index 0000000..24981ca
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeploy.java
@@ -0,0 +1,186 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.bnd.build.*;
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.command.*;
+import aQute.libg.reporter.*;
+
+public class MavenDeploy implements Deploy, Plugin {
+
+ String repository;
+ String url;
+ String homedir;
+ String keyname;
+
+ String passphrase;
+ Reporter reporter;
+
+ public void setProperties(Map<String, String> map) {
+ repository = map.get("repository");
+ url = map.get("url");
+ passphrase = map.get("passphrase");
+ homedir = map.get("homedir");
+ keyname = map.get("keyname");
+
+ if (url == null)
+ throw new IllegalArgumentException("MavenDeploy plugin must get a repository URL");
+ if (repository == null)
+ throw new IllegalArgumentException("MavenDeploy plugin must get a repository name");
+ }
+
+ public void setReporter(Reporter processor) {
+ this.reporter = processor;
+ }
+
+ /**
+ */
+ public boolean deploy(Project project, Jar original) throws Exception {
+ Map<String, Map<String, String>> deploy = project.parseHeader(project
+ .getProperty(Constants.DEPLOY));
+
+ Map<String, String> maven = deploy.get(repository);
+ if (maven == null)
+ return false; // we're not playing for this bundle
+
+ project.progress("deploying %s to Maven repo: %s", original, repository);
+ File target = project.getTarget();
+ File tmp = Processor.getFile(target, repository);
+ tmp.mkdirs();
+
+ Manifest manifest = original.getManifest();
+ if (manifest == null)
+ project.error("Jar has no manifest: %s", original);
+ else {
+ project.progress("Writing pom.xml");
+ PomResource pom = new PomResource(manifest);
+ pom.setProperties(maven);
+ File pomFile = write(tmp, pom, "pom.xml");
+
+ Jar main = new Jar("main");
+ Jar src = new Jar("src");
+ try {
+ split(original, main, src);
+ Map<String, Map<String, String>> exports = project.parseHeader(manifest
+ .getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
+ File jdoc = new File(tmp, "jdoc");
+ jdoc.mkdirs();
+ project.progress("Generating Javadoc for: " + exports.keySet());
+ Jar javadoc = javadoc(jdoc, project, exports.keySet());
+ project.progress("Writing javadoc jar");
+ File javadocFile = write(tmp, new JarResource(javadoc), "javadoc.jar");
+ project.progress("Writing main file");
+ File mainFile = write(tmp, new JarResource(main), "main.jar");
+ project.progress("Writing sources file");
+ File srcFile = write(tmp, new JarResource(main), "src.jar");
+
+ project.progress("Deploying main file");
+ maven_gpg_sign_and_deploy(project, mainFile, null, pomFile);
+ project.progress("Deploying main sources file");
+ maven_gpg_sign_and_deploy(project, srcFile, "sources", null);
+ project.progress("Deploying main javadoc file");
+ maven_gpg_sign_and_deploy(project, javadocFile, "javadoc", null);
+
+ } finally {
+ main.close();
+ src.close();
+ }
+ }
+ return true;
+ }
+
+ private void split(Jar original, Jar main, Jar src) {
+ for (Map.Entry<String, Resource> e : original.getResources().entrySet()) {
+ String path = e.getKey();
+ if (path.startsWith("OSGI-OPT/src/")) {
+ src.putResource(path.substring("OSGI-OPT/src/".length()), e.getValue());
+ } else {
+ main.putResource(path, e.getValue());
+ }
+ }
+ }
+
+ // gpg:sign-and-deploy-file \
+ // -Durl=http://oss.sonatype.org/service/local/staging/deploy/maven2
+ // \
+ // -DrepositoryId=sonatype-nexus-staging \
+ // -DupdateReleaseInfo=true \
+ // -DpomFile=pom.xml \
+ // -Dfile=/Ws/bnd/biz.aQute.bndlib/tmp/biz.aQute.bndlib.jar \
+ // -Dpassphrase=a1k3v3t5x3
+
+ private void maven_gpg_sign_and_deploy(Project b, File file, String classifier, File pomFile)
+ throws Exception {
+ Command command = new Command();
+ command.setTrace();
+ command.add(b.getProperty("mvn", "mvn"));
+ command.add("gpg:sign-and-deploy-file", "-DreleaseInfo=true", "-DpomFile=pom.xml");
+ command.add("-Dfile=" + file.getAbsolutePath());
+ command.add("-DrepositoryId=" + repository);
+ command.add("-Durl=" + url);
+ optional(command, "passphrase", passphrase);
+ optional(command, "keyname", keyname);
+ optional(command, "homedir", homedir);
+ optional(command, "classifier", classifier);
+ optional(command, "pomFile", pomFile == null ? null : pomFile.getAbsolutePath());
+
+ StringBuffer stdout = new StringBuffer();
+ StringBuffer stderr = new StringBuffer();
+
+ int result = command.execute(stdout, stderr);
+ if (result != 0) {
+ b.error("Maven deploy to %s failed to sign and transfer %s because %s", repository,
+ file, "" + stdout + stderr);
+ }
+ }
+
+ private void optional(Command command, String key, String value) {
+ if (value == null)
+ return;
+
+ command.add("-D=" + value);
+ }
+
+ private Jar javadoc(File tmp, Project b, Set<String> exports) throws Exception {
+ Command command = new Command();
+
+ command.add(b.getProperty("javadoc", "javadoc"));
+ command.add("-d");
+ command.add(tmp.getAbsolutePath());
+ command.add("-sourcepath");
+ command.add( Processor.join(b.getSourcePath(),File.pathSeparator));
+
+ for (String packageName : exports) {
+ command.add(packageName);
+ }
+
+ StringBuffer out = new StringBuffer();
+ StringBuffer err = new StringBuffer();
+ Command c = new Command();
+ c.setTrace();
+ int result = c.execute(out, err);
+ if (result == 0) {
+ Jar jar = new Jar(tmp);
+ b.addClose(jar);
+ return jar;
+ }
+ b.error("Error during execution of javadoc command: %s / %s", out, err);
+ return null;
+ }
+
+ private File write(File base, Resource r, String fileName) throws Exception {
+ File f = Processor.getFile(base, fileName);
+ OutputStream out = new FileOutputStream(f);
+ try {
+ r.write(out);
+ } finally {
+ out.close();
+ }
+ return f;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
new file mode 100644
index 0000000..aaa23ff
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenDeployCmd.java
@@ -0,0 +1,221 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import aQute.bnd.build.*;
+import aQute.lib.osgi.*;
+import aQute.libg.command.*;
+import aQute.libg.reporter.*;
+
+public class MavenDeployCmd extends Processor {
+
+ String repository = "nexus";
+ String url = "http://oss.sonatype.org/service/local/staging/deploy/maven2";
+ String homedir;
+ String keyname;
+
+ String passphrase;
+ Reporter reporter;
+
+ /**
+ * maven deploy [-url repo] [-passphrase passphrase] [-homedir homedir]
+ * [-keyname keyname] bundle ...
+ *
+ * @param args
+ * @param i
+ * @throws Exception
+ */
+ void run(String args[], int i) throws Exception {
+ if (i >= args.length) {
+ System.err
+ .println("Usage:\n");
+ System.err.println(" deploy [-url repo] [-passphrase passphrase] [-homedir homedir] [-keyname keyname] bundle ...");
+ System.err.println(" settings");
+ return;
+ }
+
+ @SuppressWarnings("unused") String cmd = args[i++];
+
+ while (i < args.length && args[i].startsWith("-")) {
+ String option = args[i];
+ if (option.equals("-url"))
+ repository = args[++i];
+ else if (option.equals("-passphrase"))
+ passphrase = args[++i];
+ else if (option.equals("-url"))
+ homedir = args[++i];
+ else if (option.equals("-keyname"))
+ keyname = args[++i];
+ else
+ error("Invalid command ");
+ }
+
+
+ }
+
+ public void setProperties(Map<String, String> map) {
+ repository = map.get("repository");
+ url = map.get("url");
+ passphrase = map.get("passphrase");
+ homedir = map.get("homedir");
+ keyname = map.get("keyname");
+
+ if (url == null)
+ throw new IllegalArgumentException("MavenDeploy plugin must get a repository URL");
+ if (repository == null)
+ throw new IllegalArgumentException("MavenDeploy plugin must get a repository name");
+ }
+
+ public void setReporter(Reporter processor) {
+ this.reporter = processor;
+ }
+
+ /**
+ */
+ public boolean deploy(Project project, Jar original) throws Exception {
+ Map<String, Map<String, String>> deploy = project.parseHeader(project
+ .getProperty(Constants.DEPLOY));
+
+ Map<String, String> maven = deploy.get(repository);
+ if (maven == null)
+ return false; // we're not playing for this bundle
+
+ project.progress("deploying %s to Maven repo: %s", original, repository);
+ File target = project.getTarget();
+ File tmp = Processor.getFile(target, repository);
+ tmp.mkdirs();
+
+ Manifest manifest = original.getManifest();
+ if (manifest == null)
+ project.error("Jar has no manifest: %s", original);
+ else {
+ project.progress("Writing pom.xml");
+ PomResource pom = new PomResource(manifest);
+ pom.setProperties(maven);
+ File pomFile = write(tmp, pom, "pom.xml");
+
+ Jar main = new Jar("main");
+ Jar src = new Jar("src");
+ try {
+ split(original, main, src);
+ Map<String, Map<String, String>> exports = project.parseHeader(manifest
+ .getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
+ File jdoc = new File(tmp, "jdoc");
+ jdoc.mkdirs();
+ project.progress("Generating Javadoc for: " + exports.keySet());
+ Jar javadoc = javadoc(jdoc, project, exports.keySet());
+ project.progress("Writing javadoc jar");
+ File javadocFile = write(tmp, new JarResource(javadoc), "javadoc.jar");
+ project.progress("Writing main file");
+ File mainFile = write(tmp, new JarResource(main), "main.jar");
+ project.progress("Writing sources file");
+ File srcFile = write(tmp, new JarResource(main), "src.jar");
+
+ project.progress("Deploying main file");
+ maven_gpg_sign_and_deploy(project, mainFile, null, pomFile);
+ project.progress("Deploying main sources file");
+ maven_gpg_sign_and_deploy(project, srcFile, "sources", null);
+ project.progress("Deploying main javadoc file");
+ maven_gpg_sign_and_deploy(project, javadocFile, "javadoc", null);
+
+ } finally {
+ main.close();
+ src.close();
+ }
+ }
+ return true;
+ }
+
+ private void split(Jar original, Jar main, Jar src) {
+ for (Map.Entry<String, Resource> e : original.getResources().entrySet()) {
+ String path = e.getKey();
+ if (path.startsWith("OSGI-OPT/src/")) {
+ src.putResource(path.substring("OSGI-OPT/src/".length()), e.getValue());
+ } else {
+ main.putResource(path, e.getValue());
+ }
+ }
+ }
+
+ // gpg:sign-and-deploy-file \
+ // -Durl=http://oss.sonatype.org/service/local/staging/deploy/maven2
+ // \
+ // -DrepositoryId=sonatype-nexus-staging \
+ // -DupdateReleaseInfo=true \
+ // -DpomFile=pom.xml \
+ // -Dfile=/Ws/bnd/biz.aQute.bndlib/tmp/biz.aQute.bndlib.jar \
+ // -Dpassphrase=a1k3v3t5x3
+
+ private void maven_gpg_sign_and_deploy(Project b, File file, String classifier, File pomFile)
+ throws Exception {
+ Command command = new Command();
+ command.setTrace();
+ command.add(b.getProperty("mvn", "mvn"));
+ command.add("gpg:sign-and-deploy-file", "-DreleaseInfo=true", "-DpomFile=pom.xml");
+ command.add("-Dfile=" + file.getAbsolutePath());
+ command.add("-DrepositoryId=" + repository);
+ command.add("-Durl=" + url);
+ optional(command, "passphrase", passphrase);
+ optional(command, "keyname", keyname);
+ optional(command, "homedir", homedir);
+ optional(command, "classifier", classifier);
+ optional(command, "pomFile", pomFile == null ? null : pomFile.getAbsolutePath());
+
+ StringBuffer stdout = new StringBuffer();
+ StringBuffer stderr = new StringBuffer();
+
+ int result = command.execute(stdout, stderr);
+ if (result != 0) {
+ b.error("Maven deploy to %s failed to sign and transfer %s because %s", repository,
+ file, "" + stdout + stderr);
+ }
+ }
+
+ private void optional(Command command, String key, String value) {
+ if (value == null)
+ return;
+
+ command.add("-D=" + value);
+ }
+
+ private Jar javadoc(File tmp, Project b, Set<String> exports) throws Exception {
+ Command command = new Command();
+
+ command.add(b.getProperty("javadoc", "javadoc"));
+ command.add("-d");
+ command.add(tmp.getAbsolutePath());
+ command.add("-sourcepath");
+ command.add(Processor.join(b.getSourcePath(), File.pathSeparator));
+
+ for (String packageName : exports) {
+ command.add(packageName);
+ }
+
+ StringBuffer out = new StringBuffer();
+ StringBuffer err = new StringBuffer();
+ Command c = new Command();
+ c.setTrace();
+ int result = c.execute(out, err);
+ if (result == 0) {
+ Jar jar = new Jar(tmp);
+ b.addClose(jar);
+ return jar;
+ }
+ b.error("Error during execution of javadoc command: %s / %s", out, err);
+ return null;
+ }
+
+ private File write(File base, Resource r, String fileName) throws Exception {
+ File f = Processor.getFile(base, fileName);
+ OutputStream out = new FileOutputStream(f);
+ try {
+ r.write(out);
+ } finally {
+ out.close();
+ }
+ return f;
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java
new file mode 100644
index 0000000..2d49be6
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenGroup.java
@@ -0,0 +1,27 @@
+package aQute.bnd.maven;
+
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.libg.reporter.*;
+
+public class MavenGroup implements BsnToMavenPath, Plugin {
+ String groupId = "";
+
+ public String[] getGroupAndArtifact(String bsn) {
+ String[] result = new String[2];
+ result[0] = groupId;
+ result[1] = bsn;
+ return result;
+ }
+
+ public void setProperties(Map<String, String> map) {
+ if (map.containsKey("groupId")) {
+ groupId = map.get("groupId");
+ }
+ }
+
+ public void setReporter(Reporter processor) {
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
new file mode 100644
index 0000000..506ea53
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/MavenRepository.java
@@ -0,0 +1,194 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.osgi.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class MavenRepository implements RepositoryPlugin, Plugin, BsnToMavenPath {
+
+ public static String NAME = "name";
+
+ File root;
+ Reporter reporter;
+ String name;
+
+ public String toString() {
+ return "maven:" + root;
+ }
+
+ public boolean canWrite() {
+ return false;
+ }
+
+ public File[] get(String bsn, String version) throws Exception {
+ VersionRange range = new VersionRange("0");
+ if (version != null)
+ range = new VersionRange(version);
+
+ List<BsnToMavenPath> plugins = ((Processor) reporter).getPlugins(BsnToMavenPath.class);
+ if ( plugins.isEmpty())
+ plugins.add(this);
+
+ for (BsnToMavenPath cvr : plugins) {
+ String[] paths = cvr.getGroupAndArtifact(bsn);
+ if (paths != null) {
+ File[] files = find(paths[0], paths[1], range);
+ if (files != null)
+ return files;
+ }
+ }
+ reporter.trace("Cannot find in maven: %s-%s", bsn, version);
+ return null;
+ }
+
+ File[] find(String groupId, String artifactId, VersionRange range) {
+ String path = groupId.replace(".", "/");
+ File vsdir = Processor.getFile(root, path);
+ if (!vsdir.isDirectory())
+ return null;
+
+ vsdir = Processor.getFile(vsdir, artifactId);
+
+ List<File> result = new ArrayList<File>();
+ if (vsdir.isDirectory()) {
+ String versions[] = vsdir.list();
+ for (String v : versions) {
+ String vv = Analyzer.cleanupVersion(v);
+ if (Verifier.isVersion(vv)) {
+ Version vvv = new Version(vv);
+ if (range.includes(vvv)) {
+ File file = Processor.getFile(vsdir, v + "/" + artifactId + "-" + v
+ + ".jar");
+ if (file.isFile())
+ result.add(file);
+ else
+ reporter.warning("Expected maven entry was not a valid file %s ", file);
+ }
+ } else {
+ reporter
+ .warning(
+ "Expected a version directory in maven: dir=%s raw-version=%s cleaned-up-version=%s",
+ vsdir, vv, v);
+ }
+ }
+ } else
+ return null;
+
+ return result.toArray(new File[result.size()]);
+ }
+
+ public List<String> list(String regex) {
+ List<String> bsns = new ArrayList<String>();
+ Pattern match = Pattern.compile(".*");
+ if (regex != null)
+ match = Pattern.compile(regex);
+ find(bsns, match, root, "");
+ return bsns;
+ }
+
+ void find(List<String> bsns, Pattern pattern, File base, String name) {
+ if (base.isDirectory()) {
+ String list[] = base.list();
+ boolean found = false;
+ for (String entry : list) {
+ char c = entry.charAt(0);
+ if (c >= '0' && c <= '9') {
+ if (pattern.matcher(name).matches())
+ found = true;
+ } else {
+ String nextName = entry;
+ if (name.length() != 0)
+ nextName = name + "." + entry;
+
+ File next = Processor.getFile(base, entry);
+ find(bsns, pattern, next, nextName);
+ }
+ }
+ if (found)
+ bsns.add(name);
+ }
+ }
+
+ public File put(Jar jar) throws Exception {
+ throw new IllegalStateException("Maven does not support the put command");
+ }
+
+ public List<Version> versions(String bsn) throws Exception {
+
+ File files[] = get( bsn, null);
+ List<Version> versions = new ArrayList<Version>();
+ for ( File f : files ) {
+ Version v = new Version( f.getParentFile().getName());
+ versions.add(v);
+ }
+ return versions;
+ }
+
+ public void setProperties(Map<String, String> map) {
+ File home = new File("");
+ String root = map.get("root");
+ if (root == null) {
+ home = new File( System.getProperty("user.home") );
+ this.root = Processor.getFile(home , ".m2/repository").getAbsoluteFile();
+ } else
+ this.root = Processor.getFile(home, root).getAbsoluteFile();
+
+ if (!this.root.isDirectory()) {
+ reporter.error("Maven repository did not get a proper URL to the repository %s", root);
+ }
+ name = (String) map.get(NAME);
+
+ }
+
+ public void setReporter(Reporter processor) {
+ this.reporter = processor;
+ }
+
+ public String[] getGroupAndArtifact(String bsn) {
+ String groupId;
+ String artifactId;
+ int n = bsn.indexOf('.');
+
+ while ( n > 0 ) {
+ artifactId = bsn.substring(n+1);
+ groupId = bsn.substring(0,n);
+
+ File gdir = new File(root, groupId.replace('.',File.separatorChar)).getAbsoluteFile();
+ File adir = new File( gdir, artifactId).getAbsoluteFile();
+ if ( adir.isDirectory() )
+ return new String[] {groupId, artifactId};
+
+ n = bsn.indexOf('.',n+1);
+ }
+ return null;
+ }
+
+ public String getName() {
+ if (name == null) {
+ return toString();
+ }
+ return name;
+ }
+
+ public File get(String bsn, String range, Strategy strategy, Map<String,String> properties) throws Exception {
+ File[] files = get(bsn, range);
+ if (files.length >= 0) {
+ switch (strategy) {
+ case LOWEST:
+ return files[0];
+ case HIGHEST:
+ return files[files.length - 1];
+ }
+ }
+ return null;
+ }
+
+ public void setRoot( File f ) {
+ root = f;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/PomFromManifest.java b/bundleplugin/src/main/java/aQute/bnd/maven/PomFromManifest.java
new file mode 100644
index 0000000..3ed560d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/PomFromManifest.java
@@ -0,0 +1,247 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.lib.osgi.*;
+import aQute.lib.tag.*;
+import aQute.libg.version.*;
+
+public class PomFromManifest extends WriteResource {
+ final Manifest manifest;
+ private List<String> scm = new ArrayList<String>();
+ private List<String> developers = new ArrayList<String>();
+ final static Pattern NAME_URL = Pattern.compile("(.*)(http://.*)");
+ String xbsn;
+ String xversion;
+ String xgroupId;
+ String xartifactId;
+ private String projectURL;
+
+ public String getBsn() {
+ if (xbsn == null)
+ xbsn = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ if (xbsn == null)
+ throw new RuntimeException("Cannot create POM unless bsn is set");
+
+ xbsn = xbsn.trim();
+ int n = xbsn.lastIndexOf('.');
+ if (n < 0) {
+ n = xbsn.length();
+ xbsn = xbsn + "." + xbsn;
+ }
+
+ if (xgroupId == null)
+ xgroupId = xbsn.substring(0, n);
+ if (xartifactId == null) {
+ xartifactId = xbsn.substring(n + 1);
+ n = xartifactId.indexOf(';');
+ if (n > 0)
+ xartifactId = xartifactId.substring(0, n).trim();
+ }
+
+ return xbsn;
+ }
+
+ public String getGroupId() {
+ getBsn();
+ return xgroupId;
+ }
+
+ public String getArtifactId() {
+ getBsn();
+ return xartifactId;
+ }
+
+ public Version getVersion() {
+ if (xversion != null)
+ return new Version(xversion);
+ String version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ Version v = new Version(version);
+ return new Version(v.getMajor(), v.getMinor(), v.getMicro());
+ }
+
+ public PomFromManifest(Manifest manifest) {
+ this.manifest = manifest;
+ }
+
+ @Override public long lastModified() {
+ return 0;
+ }
+
+ @Override public void write(OutputStream out) throws IOException {
+ PrintWriter ps = new PrintWriter(out);
+
+ String name = manifest.getMainAttributes().getValue(Analyzer.BUNDLE_NAME);
+
+ String description = manifest.getMainAttributes().getValue(Constants.BUNDLE_DESCRIPTION);
+ String docUrl = manifest.getMainAttributes().getValue(Constants.BUNDLE_DOCURL);
+ String bundleVendor = manifest.getMainAttributes().getValue(Constants.BUNDLE_VENDOR);
+
+ String licenses = manifest.getMainAttributes().getValue(Constants.BUNDLE_LICENSE);
+
+ Tag project = new Tag("project");
+ project.addAttribute("xmlns", "http://maven.apache.org/POM/4.0.0");
+ project.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ project.addAttribute("xsi:schemaLocation",
+ "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd");
+
+ project.addContent(new Tag("modelVersion").addContent("4.0.0"));
+ project.addContent(new Tag("groupId").addContent(getGroupId()));
+ project.addContent(new Tag("artifactId").addContent(getArtifactId()));
+ project.addContent(new Tag("version").addContent(getVersion().toString()));
+
+ if (description != null) {
+ new Tag(project, "description").addContent(description);
+ }
+ if (name != null) {
+ new Tag(project, "name").addContent(name);
+ }
+
+ if (projectURL != null)
+ new Tag(project, "url").addContent(projectURL);
+ else if (docUrl != null) {
+ new Tag(project, "url").addContent(docUrl);
+ } else
+ new Tag(project, "url").addContent("http://no-url");
+
+ String scmheader = manifest.getMainAttributes().getValue("Bundle-SCM");
+ if (scmheader != null)
+ scm.add(scmheader);
+
+ Tag scmtag = new Tag(project, "scm");
+ if (scm != null && !scm.isEmpty()) {
+ for (String cm : this.scm) {
+ new Tag(scmtag, "url").addContent(cm);
+ new Tag(scmtag, "connection").addContent(cm);
+ new Tag(scmtag, "developerConnection").addContent(cm);
+ }
+ } else {
+ new Tag(scmtag, "url").addContent("private");
+ new Tag(scmtag, "connection").addContent("private");
+ new Tag(scmtag, "developerConnection").addContent("private");
+ }
+
+ if (bundleVendor != null) {
+ Matcher m = NAME_URL.matcher(bundleVendor);
+ String namePart = bundleVendor;
+ String urlPart = this.projectURL;
+ if (m.matches()) {
+ namePart = m.group(1);
+ urlPart = m.group(2);
+ }
+ Tag organization = new Tag(project, "organization");
+ new Tag(organization, "name").addContent(namePart.trim());
+ if (urlPart != null) {
+ new Tag(organization, "url").addContent(urlPart.trim());
+ }
+ }
+ if (!developers.isEmpty()) {
+ Tag d = new Tag(project, "developers");
+ for (String email : developers) {
+ String id = email;
+ String xname = email;
+ String organization = null;
+
+ Matcher m = Pattern.compile("([^@]+)@([\\d\\w\\-_\\.]+)\\.([\\d\\w\\-_\\.]+)")
+ .matcher(email);
+ if (m.matches()) {
+ xname = m.group(1);
+ organization = m.group(2);
+ }
+
+ Tag developer = new Tag(d, "developer");
+ new Tag(developer, "id").addContent(id);
+ new Tag(developer, "name").addContent(xname);
+ new Tag(developer, "email").addContent(email);
+ if (organization != null)
+ new Tag(developer, "organization").addContent(organization);
+ }
+ }
+ if (licenses != null) {
+ Tag ls = new Tag(project, "licenses");
+
+ Map<String, Map<String, String>> map = Processor.parseHeader(licenses, null);
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = map.entrySet().iterator(); e
+ .hasNext();) {
+
+ // Bundle-License:
+ // http://www.opensource.org/licenses/apache2.0.php; \
+ // description="${Bundle-Copyright}"; \
+ // link=LICENSE
+ //
+ // <license>
+ // <name>This material is licensed under the Apache
+ // Software License, Version 2.0</name>
+ // <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+ // <distribution>repo</distribution>
+ // </license>
+
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ Tag l = new Tag(ls, "license");
+ Map<String, String> values = entry.getValue();
+ String url = entry.getKey();
+
+ if (values.containsKey("description"))
+ tagFromMap(l, values, "description", "name", url);
+ else
+ tagFromMap(l, values, "name", "name", url);
+
+ tagFromMap(l, values, "url", "url", url);
+ tagFromMap(l, values, "distribution", "distribution", "repo");
+ }
+ }
+ project.print(0, ps);
+ ps.flush();
+ }
+
+ /**
+ * Utility function to print a tag from a map
+ *
+ * @param ps
+ * @param values
+ * @param string
+ * @param tag
+ * @param object
+ */
+ private Tag tagFromMap(Tag parent, Map<String, String> values, String string, String tag,
+ String object) {
+ String value = (String) values.get(string);
+ if (value == null)
+ value = object;
+ if (value == null)
+ return parent;
+ new Tag(parent, tag).addContent(value.trim());
+ return parent;
+ }
+
+ public void setSCM(String scm) {
+ this.scm.add(scm);
+ }
+
+ public void setURL(String url) {
+ this.projectURL = url;
+ }
+
+ public void setBsn(String bsn) {
+ this.xbsn = bsn;
+ }
+
+ public void addDeveloper(String email) {
+ this.developers.add(email);
+ }
+
+ public void setVersion(String version) {
+ this.xversion = version;
+ }
+
+ public void setArtifact(String artifact) {
+ this.xartifactId = artifact;
+ }
+
+ public void setGroup(String group) {
+ this.xgroupId = group;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/PomParser.java b/bundleplugin/src/main/java/aQute/bnd/maven/PomParser.java
new file mode 100644
index 0000000..e529141
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/PomParser.java
@@ -0,0 +1,144 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+
+import javax.xml.parsers.*;
+import javax.xml.xpath.*;
+
+import org.w3c.dom.*;
+
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+
+/**
+ * Provides a way to parse a maven pom as properties.
+ *
+ * This provides most of the maven elements as properties. It also
+ * provides pom.scope.[compile|test|runtime|provided|system] properties
+ * that can be appended to the build and run path. That is, they are
+ * in the correct format for this.
+ */
+public class PomParser extends Processor {
+ static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ static XPathFactory xpathf = XPathFactory.newInstance();
+ static Set<String> multiple = new HashSet<String>();
+ static Set<String> skip = new HashSet<String>();
+
+ static {
+ dbf.setNamespaceAware(false);
+
+ // Set all elements that need enumeration of their elements
+ // these will not use the name of the subelement but instead
+ // they use an index from 0..n
+ multiple.add("mailingLists");
+ multiple.add("pluginRepositories");
+ multiple.add("repositories");
+ multiple.add("resources");
+ multiple.add("executions");
+ multiple.add("goals");
+ multiple.add("includes");
+ multiple.add("excludes");
+
+ // These properties are not very useful and
+ // pollute the property space.
+ skip.add("plugins");
+ skip.add("dependencies");
+ skip.add("reporting");
+ skip.add("extensions");
+
+ }
+
+ public Properties getProperties(File pom) throws Exception {
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ XPath xpath = xpathf.newXPath();
+ pom = pom.getAbsoluteFile();
+ Document doc = db.parse(pom);
+ Properties p = new Properties();
+
+ // Check if there is a parent pom
+ String relativePath = xpath.evaluate("project/parent/relativePath", doc);
+ if (relativePath != null && relativePath.length()!=0) {
+ File parentPom = IO.getFile(pom.getParentFile(), relativePath);
+ if (parentPom.isFile()) {
+ Properties parentProps = getProperties(parentPom);
+ p.putAll(parentProps);
+ } else {
+ error("Parent pom for %s is not an existing file (could be directory): %s", pom, parentPom);
+ }
+ }
+
+ Element e = doc.getDocumentElement();
+ traverse("pom", e, p);
+
+ String scopes[] = { "provided", "runtime", "test", "system" };
+ NodeList set = (NodeList) xpath.evaluate("//dependency[not(scope) or scope='compile']", doc,
+ XPathConstants.NODESET);
+ if (set.getLength() != 0)
+ p.put("pom.scope.compile", toBsn(set));
+
+ for (String scope : scopes) {
+ set = (NodeList) xpath.evaluate("//dependency[scope='" + scope + "']", doc,
+ XPathConstants.NODESET);
+ if (set.getLength() != 0)
+ p.put("pom.scope." + scope, toBsn(set));
+ }
+
+ return p;
+ }
+
+ private Object toBsn(NodeList set) throws XPathExpressionException {
+ XPath xpath = xpathf.newXPath();
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+ for (int i = 0; i < set.getLength(); i++) {
+ Node child = set.item(i);
+ String version = xpath.evaluate("version", child);
+ sb.append(del);
+ sb.append(xpath.evaluate("groupId", child));
+ sb.append(".");
+ sb.append(xpath.evaluate("artifactId", child));
+ if (version != null && version.trim().length()!=0) {
+ sb.append(";version=");
+ sb.append( Analyzer.cleanupVersion(version));
+ }
+ del = ", ";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * The maven POM is quite straightforward, it is basically a structured property file.
+ * @param name
+ * @param parent
+ * @param p
+ */
+ static void traverse(String name, Node parent, Properties p) {
+ if ( skip.contains(parent.getNodeName()))
+ return;
+
+ NodeList children = parent.getChildNodes();
+ if (multiple.contains(parent.getNodeName())) {
+ int n = 0;
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (!(child instanceof Text)) {
+
+ traverse(name + "." + n++, child, p);
+ }
+ }
+ } else {
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child instanceof Text) {
+ String value = child.getNodeValue().trim();
+ if (value.length()!=0) {
+ p.put(name, value);
+ }
+ } else {
+ traverse(name + "." + child.getNodeName(), child, p);
+ }
+ }
+ }
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/PomResource.java b/bundleplugin/src/main/java/aQute/bnd/maven/PomResource.java
new file mode 100644
index 0000000..e5e6b8d
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/PomResource.java
@@ -0,0 +1,159 @@
+package aQute.bnd.maven;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.regex.*;
+
+import aQute.lib.osgi.*;
+import aQute.lib.tag.*;
+
+public class PomResource extends WriteResource {
+ final Manifest manifest;
+ private Map<String, String> scm;
+ final static Pattern NAME_URL = Pattern.compile("(.*)(http://.*)");
+
+ public PomResource(Manifest manifest) {
+ this.manifest = manifest;
+ }
+
+ @Override public long lastModified() {
+ return 0;
+ }
+
+ @Override public void write(OutputStream out) throws IOException {
+ PrintWriter ps = new PrintWriter(out);
+
+ String name = manifest.getMainAttributes().getValue(Analyzer.BUNDLE_NAME);
+
+ String description = manifest.getMainAttributes().getValue(Constants.BUNDLE_DESCRIPTION);
+ String docUrl = manifest.getMainAttributes().getValue(Constants.BUNDLE_DOCURL);
+ String version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ String bundleVendor = manifest.getMainAttributes().getValue(Constants.BUNDLE_VENDOR);
+
+ String bsn = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ String licenses = manifest.getMainAttributes().getValue(Constants.BUNDLE_LICENSE);
+
+ if (bsn == null) {
+ throw new RuntimeException("Cannot create POM unless bsn is set");
+ }
+
+ bsn = bsn.trim();
+ int n = bsn.lastIndexOf('.');
+ if (n <= 0)
+ throw new RuntimeException(
+ "Can not create POM unless Bundle-SymbolicName contains a . to separate group and artifact id");
+
+ if (version == null)
+ version = "0";
+
+ String groupId = bsn.substring(0, n);
+ String artifactId = bsn.substring(n + 1);
+ n = artifactId.indexOf(';');
+ if (n > 0)
+ artifactId = artifactId.substring(0, n).trim();
+
+ Tag project = new Tag("project");
+ project.addAttribute("xmlns", "http://maven.apache.org/POM/4.0.0");
+ project.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ project.addAttribute("xmlns:xsi", "");
+ project.addAttribute("xsi:schemaLocation",
+ "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd");
+
+ project.addContent(new Tag("modelVersion").addContent("4.0.0"));
+ project.addContent(new Tag("groupId").addContent(groupId));
+ project.addContent(new Tag("artifactId").addContent(artifactId));
+ project.addContent(new Tag("version").addContent(version));
+
+ if (description != null) {
+ new Tag(project, "description").addContent(description);
+ }
+ if (name != null) {
+ new Tag(project, "name").addContent(name);
+ }
+ if (docUrl != null) {
+ new Tag(project, "url").addContent(docUrl);
+ }
+
+ if (scm != null) {
+ Tag scm = new Tag(project, "scm");
+ for (Map.Entry<String, String> e : this.scm.entrySet()) {
+ new Tag(scm, e.getKey()).addContent(e.getValue());
+ }
+ }
+
+ if (bundleVendor != null) {
+ Matcher m = NAME_URL.matcher(bundleVendor);
+ String namePart = bundleVendor;
+ String urlPart = null;
+ if (m.matches()) {
+ namePart = m.group(1);
+ urlPart = m.group(2);
+ }
+ Tag organization = new Tag(project, "organization");
+ new Tag(organization, "name").addContent(namePart.trim());
+ if (urlPart != null) {
+ new Tag(organization, "url").addContent(urlPart.trim());
+ }
+ }
+ if (licenses != null) {
+ Tag ls = new Tag(project, "licenses");
+
+ Map<String, Map<String, String>> map = Processor.parseHeader(licenses, null);
+ for (Iterator<Map.Entry<String, Map<String, String>>> e = map.entrySet().iterator(); e
+ .hasNext();) {
+
+ // Bundle-License:
+ // http://www.opensource.org/licenses/apache2.0.php; \
+ // description="${Bundle-Copyright}"; \
+ // link=LICENSE
+ //
+ // <license>
+ // <name>This material is licensed under the Apache
+ // Software License, Version 2.0</name>
+ // <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+ // <distribution>repo</distribution>
+ // </license>
+
+ Map.Entry<String, Map<String, String>> entry = e.next();
+ Tag l = new Tag(ls, "license");
+ Map<String, String> values = entry.getValue();
+ String url = entry.getKey();
+
+ if (values.containsKey("description"))
+ tagFromMap(l, values, "description", "name", url);
+ else
+ tagFromMap(l, values, "name", "name", url);
+
+ tagFromMap(l, values, "url", "url", url);
+ tagFromMap(l, values, "distribution", "distribution", "repo");
+ }
+ }
+ project.print(0, ps);
+ ps.flush();
+ }
+
+ /**
+ * Utility function to print a tag from a map
+ *
+ * @param ps
+ * @param values
+ * @param string
+ * @param tag
+ * @param object
+ */
+ private Tag tagFromMap(Tag parent, Map<String, String> values, String string, String tag,
+ String object) {
+ String value = (String) values.get(string);
+ if (value == null)
+ value = object;
+ if (value == null)
+ return parent;
+ new Tag(parent, tag).addContent(value.trim());
+ return parent;
+ }
+
+ public void setProperties(Map<String, String> scm) {
+ this.scm = scm;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/CachedPom.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/CachedPom.java
new file mode 100644
index 0000000..02449fa
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/CachedPom.java
@@ -0,0 +1,18 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+
+public class CachedPom extends Pom {
+ final MavenEntry maven;
+
+ CachedPom(MavenEntry mavenEntry, URI repo) throws Exception {
+ super(mavenEntry.maven, mavenEntry.getPomFile(), repo);
+ this.maven = mavenEntry;
+ }
+
+ public File getArtifact() throws Exception {
+ return maven.getArtifact();
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/Maven.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/Maven.java
new file mode 100644
index 0000000..2124a05
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/Maven.java
@@ -0,0 +1,88 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.regex.*;
+
+/*
+ http://repository.springsource.com/maven/bundles/external/org/apache/coyote/com.springsource.org.apache.coyote/6.0.24/com.springsource.org.apache.coyote-6.0.24.pom
+ http://repository.springsource.com/maven/bundles/external/org/apache/coyote/com.springsource.org.apache.coyote/6.0.24/com.springsource.org.apache.coyote-6.0.24.pom
+ */
+public class Maven {
+ final File userHome = new File(System.getProperty("user.home"));
+ final Map<String, MavenEntry> entries = new ConcurrentHashMap<String, MavenEntry>();
+ final static String[] ALGORITHMS = { "md5", "sha1" };
+ boolean usecache = false;
+ final Executor executor;
+ File m2 = new File(userHome, ".m2");
+ File repository = new File(m2, "repository");
+
+ public Maven(Executor executor) {
+ if ( executor == null)
+ this.executor = Executors.newCachedThreadPool();
+ else
+ this.executor = executor;
+ }
+
+ //http://repo1.maven.org/maven2/junit/junit/maven-metadata.xml
+
+ static Pattern MAVEN_RANGE = Pattern.compile("(\\[|\\()(.+)(,(.+))(\\]|\\))");
+ public CachedPom getPom(String groupId, String artifactId, String version, URI... extra)
+ throws Exception {
+ MavenEntry entry = getEntry(groupId, artifactId, version);
+ return entry.getPom(extra);
+ }
+
+ /**
+ * @param groupId
+ * @param artifactId
+ * @param version
+ * @param extra
+ * @return
+ * @throws Exception
+ */
+ public MavenEntry getEntry(String groupId, String artifactId, String version) throws Exception {
+ String path = path(groupId, artifactId, version);
+
+ MavenEntry entry;
+ synchronized (entries) {
+ entry = entries.get(path);
+ if (entry != null)
+ return entry;
+
+ entry = new MavenEntry(this, path);
+ entries.put(path, entry);
+ }
+ return entry;
+ }
+
+ private String path(String groupId, String artifactId, String version) {
+ return groupId.replace('.', '/') + '/' + artifactId + '/' + version + "/" + artifactId
+ + "-" + version;
+ }
+
+ public void schedule(Runnable runnable) {
+ if (executor == null)
+ runnable.run();
+ else
+ executor.execute(runnable);
+ }
+
+ public ProjectPom createProjectModel(File file) throws Exception {
+ ProjectPom pom = new ProjectPom(this, file);
+ pom.parse();
+ return pom;
+ }
+
+ public MavenEntry getEntry(Pom pom) throws Exception {
+ return getEntry(pom.getGroupId(), pom.getArtifactId(), pom.getVersion());
+ }
+
+ public void setM2(File dir) {
+ this.m2 = dir;
+ this.repository = new File(dir,"repository");
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenEntry.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenEntry.java
new file mode 100644
index 0000000..f23ba82
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenEntry.java
@@ -0,0 +1,338 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import aQute.lib.hex.*;
+import aQute.lib.io.*;
+import aQute.libg.filelock.*;
+
+/**
+ * An entry (a group/artifact) in the maven cache in the .m2/repository
+ * directory. It provides methods to get the pom and the artifact.
+ *
+ */
+public class MavenEntry implements Closeable {
+ final Maven maven;
+ final File root;
+ final File dir;
+ final String path;
+ final DirectoryLock lock;
+ final Map<URI, CachedPom> poms = new HashMap<URI, CachedPom>();
+ final File pomFile;
+ final File artifactFile;
+ final String pomPath;
+ final File propertiesFile;
+ Properties properties;
+ private boolean propertiesChanged;
+ FutureTask<File> artifact;
+ private String artifactPath;
+
+ /**
+ * Constructor.
+ *
+ * @param maven
+ * @param path
+ */
+ MavenEntry(Maven maven, String path) {
+ this.root = maven.repository;
+ this.maven = maven;
+ this.path = path;
+ this.pomPath = path + ".pom";
+ this.artifactPath = path + ".jar";
+ this.dir = IO.getFile(maven.repository, path).getParentFile();
+ this.dir.mkdirs();
+ this.pomFile = new File(maven.repository, pomPath);
+ this.artifactFile = new File(maven.repository, artifactPath);
+ this.propertiesFile = new File(dir, "bnd.properties");
+ this.lock = new DirectoryLock(dir, 5 * 60000); // 5 mins
+ }
+
+ /**
+ * This is the method to get the POM for a cached entry.
+ *
+ * @param urls
+ * The allowed URLs
+ * @return a CachedPom for this maven entry
+ *
+ * @throws Exception
+ * If something goes haywire
+ */
+ public CachedPom getPom(URI[] urls) throws Exception {
+
+ // First check if we have the pom cached in memory
+ synchronized (this) {
+ // Try to find it in the in-memory cache
+ for (URI url : urls) {
+ CachedPom pom = poms.get(url);
+ if (pom != null)
+ return pom;
+ }
+ }
+
+ // Ok, we need to see if it exists on disk
+
+ // lock.lock();
+ try {
+
+ if (isValid()) {
+ // Check if one of our repos had the correct file.
+ for (URI url : urls) {
+ String valid = getProperty(url.toASCIIString());
+ if (valid != null)
+ return createPom(url);
+ }
+
+ // we have the info, but have to verify that it
+ // exists in one of our repos but we do not have
+ // to download it as our cache is already ok.
+ for (URI url : urls) {
+ if (verify(url, pomPath)) {
+ return createPom(url);
+ }
+ }
+
+ // It does not exist in out repo
+ // so we have to fail even though we do have
+ // the file.
+
+ } else {
+ dir.mkdirs();
+ // We really do not have the file
+ // so we have to find out who has it.
+ for (final URI url : urls) {
+
+ if (download(url, pomPath)) {
+ if (verify(url, pomPath)) {
+ artifact = new FutureTask<File>(new Callable<File>() {
+
+ public File call() throws Exception {
+ if (download(url, artifactPath)) {
+ verify(url, artifactPath);
+ }
+ return artifactFile;
+ }
+
+ });
+ maven.executor.execute(artifact);
+ return createPom(url);
+ }
+ }
+ }
+ }
+ return null;
+ } finally {
+ saveProperties();
+ // lock.release();
+ }
+ }
+
+ /**
+ * Download a resource from the given repo.
+ *
+ * @param url
+ * The base url for the repo
+ * @param path
+ * The path part
+ * @return
+ * @throws MalformedURLException
+ */
+ private boolean download(URI repo, String path) throws MalformedURLException {
+ try {
+ URL url = toURL(repo, path);
+ System.out.println("Downloading " + repo + " path " + path + " url " + url);
+ File file = new File(root, path);
+ IO.copy(url.openStream(), file);
+ System.out.println("Downloaded " + url);
+ return true;
+ } catch (Exception e) {
+ System.err.println("debug: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Converts a repo + path to a URL..
+ *
+ * @param base
+ * The base repo
+ * @param path
+ * The path in the directory + url
+ * @return a URL that points to the file in the repo
+ *
+ * @throws MalformedURLException
+ */
+ URL toURL(URI base, String path) throws MalformedURLException {
+ StringBuilder r = new StringBuilder();
+ r.append(base.toString());
+ if (r.charAt(r.length() - 1) != '/')
+ r.append('/');
+ r.append(path);
+ return new URL(r.toString());
+ }
+
+ /**
+ * Check if this is a valid cache directory, might probably need some more
+ * stuff.
+ *
+ * @return true if valid
+ */
+ private boolean isValid() {
+ return pomFile.isFile() && pomFile.length() > 100 && artifactFile.isFile()
+ && artifactFile.length() > 100;
+ }
+
+ /**
+ * We maintain a set of bnd properties in the cache directory.
+ *
+ * @param key
+ * The key for the property
+ * @param value
+ * The value for the property
+ */
+ private void setProperty(String key, String value) {
+ Properties properties = getProperties();
+ properties.setProperty(key, value);
+ propertiesChanged = true;
+ }
+
+ /**
+ * Answer the properties, loading if needed.
+ */
+ protected Properties getProperties() {
+ if (properties == null) {
+ properties = new Properties();
+ File props = new File(dir, "bnd.properties");
+ if (props.exists()) {
+ try {
+ FileInputStream in = new FileInputStream(props);
+ properties.load(in);
+ } catch (Exception e) {
+ // we ignore for now, will handle it on safe
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Answer a property.
+ *
+ * @param key
+ * The key
+ * @return The value
+ */
+ private String getProperty(String key) {
+ Properties properties = getProperties();
+ return properties.getProperty(key);
+ }
+
+ private void saveProperties() throws IOException {
+ if (propertiesChanged) {
+ FileOutputStream fout = new FileOutputStream(propertiesFile);
+ try {
+ properties.store(fout, "");
+ } finally {
+ properties = null;
+ propertiesChanged = false;
+ fout.close();
+ }
+ }
+ }
+
+ /**
+ * Help function to create the POM and record its source.
+ *
+ * @param url
+ * the repo from which it was constructed
+ * @return the new pom
+ * @throws Exception
+ */
+ private CachedPom createPom(URI url) throws Exception {
+ CachedPom pom = new CachedPom(this, url);
+ pom.parse();
+ poms.put(url, pom);
+ setProperty(url.toASCIIString(), "true");
+ return pom;
+ }
+
+ /**
+ * Verify that the repo has a checksum file for the given path and that this
+ * checksum matchs.
+ *
+ * @param repo
+ * The repo
+ * @param path
+ * The file id
+ * @return true if there is a digest and it matches one of the algorithms
+ * @throws Exception
+ */
+ boolean verify(URI repo, String path) throws Exception {
+ for (String algorithm : Maven.ALGORITHMS) {
+ if (verify(repo, path, algorithm))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Verify the path against its digest for the given algorithm.
+ *
+ * @param repo
+ * @param path
+ * @param algorithm
+ * @return
+ * @throws Exception
+ */
+ private boolean verify(URI repo, String path, String algorithm) throws Exception {
+ String digestPath = path + "." + algorithm;
+ File actualFile = new File(root, path);
+
+ if (download(repo, digestPath)) {
+ File digestFile = new File(root, digestPath);
+ final MessageDigest md = MessageDigest.getInstance(algorithm);
+ IO.copy(actualFile, new OutputStream() {
+ @Override public void write(int c) throws IOException {
+ md.update((byte) c);
+ }
+
+ @Override public void write(byte[] buffer, int offset, int length) {
+ md.update(buffer, offset, length);
+ }
+ });
+ byte[] digest = md.digest();
+ String source = IO.collect(digestFile).toUpperCase();
+ String hex = Hex.toHexString(digest).toUpperCase();
+ if (source.startsWith(hex)) {
+ System.out.println("Verified ok " + actualFile + " digest " + algorithm);
+ return true;
+ }
+ }
+ System.out.println("Failed to verify " + actualFile + " for digest " + algorithm);
+ return false;
+ }
+
+ public File getArtifact() throws Exception {
+ if (artifact == null )
+ return artifactFile;
+ return artifact.get();
+ }
+
+ public File getPomFile() {
+ return pomFile;
+ }
+
+ public void close() throws IOException {
+
+ }
+
+ public void remove() {
+ if (dir.getParentFile() != null) {
+ IO.delete(dir);
+ }
+ }
+
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
new file mode 100644
index 0000000..bbe2411
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/MavenRemoteRepository.java
@@ -0,0 +1,130 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import aQute.bnd.service.*;
+import aQute.lib.io.*;
+import aQute.lib.osgi.*;
+import aQute.libg.reporter.*;
+import aQute.libg.version.*;
+
+public class MavenRemoteRepository implements RepositoryPlugin, RegistryPlugin, Plugin {
+ Reporter reporter;
+ URI[] repositories;
+ Registry registry;
+ Maven maven;
+
+ public File[] get(String bsn, String range) throws Exception {
+ File f = get(bsn, range, Strategy.HIGHEST, null);
+ if (f == null)
+ return null;
+
+ return new File[] { f };
+ }
+
+ public File get(String bsn, String version, Strategy strategy, Map<String, String> properties)
+ throws Exception {
+ String groupId = null;
+
+ if (properties != null)
+ groupId = properties.get("groupId");
+
+ if (groupId == null) {
+ int n = bsn.indexOf('+');
+ if ( n < 0)
+ return null;
+
+ groupId = bsn.substring(0,n);
+ bsn = bsn.substring(n+1);
+ }
+
+ String artifactId = bsn;
+
+ if (version == null) {
+ if (reporter != null)
+ reporter.error("Maven dependency version not set for %s - %s", groupId, artifactId);
+ return null;
+ }
+
+ CachedPom pom = getMaven().getPom(groupId, artifactId, version, repositories);
+
+ String value = properties == null ? null : properties.get("scope");
+ if (value == null)
+ return pom.getArtifact();
+
+ Pom.Scope action = null;
+
+ try {
+ action = Pom.Scope.valueOf(value);
+ return pom.getLibrary(action, repositories);
+ } catch (Exception e) {
+ return pom.getArtifact();
+ }
+ }
+
+ public Maven getMaven() {
+ if ( maven != null)
+ return maven;
+
+ maven = registry.getPlugin(Maven.class);
+ return maven;
+ }
+
+ public boolean canWrite() {
+ return false;
+ }
+
+ public File put(Jar jar) throws Exception {
+ throw new UnsupportedOperationException("cannot do put");
+ }
+
+ public List<String> list(String regex) throws Exception {
+ throw new UnsupportedOperationException("cannot do list");
+ }
+
+ public List<Version> versions(String bsn) throws Exception {
+ throw new UnsupportedOperationException("cannot do versions");
+ }
+
+ public String getName() {
+ return "maven";
+ }
+
+ public void setRepositories(URI... urls) {
+ repositories = urls;
+ }
+
+ public void setProperties(Map<String, String> map) {
+ String repoString = map.get("repositories");
+ if (repoString != null) {
+ String[] repos = repoString.split("\\s*,\\s*");
+ repositories = new URI[repos.length];
+ int n = 0;
+ for (String repo : repos) {
+ try {
+ URI uri = new URI(repo);
+ if ( !uri.isAbsolute())
+ uri = IO.getFile( new File(""),repo).toURI();
+ repositories[n++] = uri;
+ } catch (Exception e) {
+ if (reporter != null)
+ reporter.error("Invalid repository %s for maven plugin, %s", repo, e);
+ }
+ }
+ }
+ }
+
+ public void setReporter(Reporter reporter) {
+ this.reporter = reporter;
+ }
+
+ public void setRegistry(Registry registry) {
+ this.registry = registry;
+ }
+
+ public void setMaven(Maven maven) {
+ this.maven = maven;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/Pom.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/Pom.java
new file mode 100644
index 0000000..2b7ab46
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/Pom.java
@@ -0,0 +1,350 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import javax.xml.parsers.*;
+import javax.xml.xpath.*;
+
+import org.w3c.dom.*;
+
+public abstract class Pom {
+ static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ static XPathFactory xpf = XPathFactory.newInstance();
+
+ static {
+ dbf.setNamespaceAware(false);
+ }
+
+ public enum Scope {
+ compile, runtime, system, import_, provided, test, ;
+
+ private boolean includes(Scope other) {
+ if (other == this) return true;
+ switch (this) {
+ case compile:
+ return other == provided || other == test;
+ default:
+ return false;
+ }
+ }
+ };
+
+ final Maven maven;
+ final URI home;
+
+ String groupId;
+ String artifactId;
+ String version;
+ List<Dependency> dependencies = new ArrayList<Dependency>();
+ Exception exception;
+ File pomFile;
+ String description="";
+ String name;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public class Dependency {
+ Scope scope;
+ String type;
+ boolean optional;
+ String groupId;
+ String artifactId;
+ String version;
+ Set<String> exclusions = new HashSet<String>();
+
+ public Scope getScope() {
+ return scope;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public String getGroupId() {
+ return replace(groupId);
+ }
+
+ public String getArtifactId() {
+ return replace(artifactId);
+ }
+
+ public String getVersion() {
+ return replace(version);
+ }
+
+ public Set<String> getExclusions() {
+ return exclusions;
+ }
+
+ public Pom getPom() throws Exception {
+ return maven.getPom(groupId, artifactId, version);
+ }
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Dependency [");
+ if (groupId != null)
+ builder.append("groupId=").append(groupId).append(", ");
+ if (artifactId != null)
+ builder.append("artifactId=").append(artifactId).append(", ");
+ if (version != null)
+ builder.append("version=").append(version).append(", ");
+ if (type != null)
+ builder.append("type=").append(type).append(", ");
+ if (scope != null)
+ builder.append("scope=").append(scope).append(", ");
+ builder.append("optional=").append(optional).append(", ");
+ if (exclusions != null)
+ builder.append("exclusions=").append(exclusions);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ public Pom(Maven maven, File pomFile, URI home) throws Exception {
+ this.maven = maven;
+ this.home = home;
+ this.pomFile = pomFile;
+ }
+
+ void parse() throws Exception {
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ System.out.println("Parsing " + pomFile.getAbsolutePath());
+ Document doc = db.parse(pomFile);
+ XPath xp = xpf.newXPath();
+ parse(doc, xp);
+ }
+
+ protected void parse(Document doc, XPath xp) throws XPathExpressionException, Exception {
+
+ this.artifactId = replace(xp.evaluate("project/artifactId", doc).trim(), this.artifactId);
+ this.groupId = replace(xp.evaluate("project/groupId", doc).trim(), this.groupId);
+ this.version = replace(xp.evaluate("project/version", doc).trim(), this.version);
+
+ String nextDescription = xp.evaluate("project/description", doc).trim();
+ if ( this.description.length() != 0 && nextDescription.length() != 0)
+ this.description += "\n";
+ this.description += replace(nextDescription);
+
+ this.name = replace(xp.evaluate("project/name", doc).trim(), this.name);
+
+ NodeList list = (NodeList) xp.evaluate("project/dependencies/dependency", doc,
+ XPathConstants.NODESET);
+ for (int i = 0; i < list.getLength(); i++) {
+ Node node = list.item(i);
+ Dependency dep = new Dependency();
+ String scope = xp.evaluate("scope", node).trim();
+ if (scope.length() == 0)
+ dep.scope = Scope.compile;
+ else
+ dep.scope = Scope.valueOf(scope);
+ dep.type = xp.evaluate("type", node).trim();
+
+ String opt = xp.evaluate("optional", node).trim();
+ dep.optional = opt != null && opt.equalsIgnoreCase("true");
+ dep.groupId = replace(xp.evaluate("groupId", node));
+ dep.artifactId = replace(xp.evaluate("artifactId", node).trim());
+
+ dep.version = replace(xp.evaluate("version", node).trim());
+ dependencies.add(dep);
+
+ NodeList exclusions = (NodeList) xp
+ .evaluate("exclusions", node, XPathConstants.NODESET);
+ for (int e = 0; e < exclusions.getLength(); e++) {
+ Node exc = exclusions.item(e);
+ String exclGroupId = xp.evaluate("groupId", exc).trim();
+ String exclArtifactId = xp.evaluate("artifactId", exc).trim();
+ dep.exclusions.add(exclGroupId + "+" + exclArtifactId);
+ }
+ }
+
+ }
+
+ private String replace(String key, String dflt) {
+ if ( key == null || key.length() == 0)
+ return dflt;
+
+ return replace(key);
+ }
+
+ public String getArtifactId() throws Exception {
+ return replace(artifactId);
+ }
+
+ public String getGroupId() throws Exception {
+ return replace(groupId);
+ }
+
+ public String getVersion() throws Exception {
+ if ( version == null)
+ return "<not set>";
+ return replace(version);
+ }
+
+ public List<Dependency> getDependencies() throws Exception {
+ return dependencies;
+ }
+
+ class Rover {
+
+ public Rover(Rover rover, Dependency d) {
+ this.previous = rover;
+ this.dependency = d;
+ }
+
+ final Rover previous;
+ final Dependency dependency;
+
+ public boolean excludes(String name) {
+ return dependency.exclusions.contains(name) && previous != null
+ && previous.excludes(name);
+ }
+ }
+
+ public Set<Pom> getDependencies(Scope scope, URI... urls) throws Exception {
+ Set<Pom> result = new LinkedHashSet<Pom>();
+
+ List<Rover> queue = new ArrayList<Rover>();
+ for (Dependency d : dependencies) {
+ queue.add(new Rover(null, d));
+ }
+
+ while (!queue.isEmpty()) {
+ Rover rover = queue.remove(0);
+ Dependency dep = rover.dependency;
+ String groupId = replace(dep.groupId);
+ String artifactId = replace(dep.artifactId);
+ String version = replace(dep.version);
+
+ String name = groupId + "+" + artifactId;
+
+ if (rover.excludes(name) || dep.optional)
+ continue;
+
+ if (dep.scope == scope && !dep.optional) {
+ try {
+ Pom sub = maven.getPom(groupId, artifactId, version, urls);
+ if (sub != null) {
+ if (!result.contains(sub)) {
+ result.add(sub);
+ for (Dependency subd : sub.dependencies) {
+ queue.add(new Rover(rover, subd));
+ }
+ }
+ } else
+ if (rover.previous != null)
+ System.out.println("Cannot find " + dep + " from "
+ + rover.previous.dependency);
+ else
+ System.out.println("Cannot find " + dep + " from top");
+ } catch (Exception e) {
+ if (rover.previous != null)
+ System.out.println("Cannot find " + dep + " from "
+ + rover.previous.dependency);
+ else
+ System.out.println("Cannot find " + dep + " from top");
+
+// boolean include = false;
+// if (dep.scope == Scope.compile) {
+// include = true;
+// } else if (dep.scope == Scope.test) {
+// include = rover.previous == null && (action == Action.compile || action == Action.test);
+// } else if (dep.scope == Scope.runtime) {
+// include = action == Action.run;
+// }
+// if (include) {
+// Pom sub = maven.getPom(groupId, artifactId, version, urls);
+// if (!result.contains(sub)) {
+// result.add(sub);
+// for (Dependency subd : sub.dependencies) {
+// queue.add(new Rover(rover, subd));
+// }
+
+ }
+ }
+ }
+ return result;
+ }
+
+ protected String replace(String in) {
+ System.out.println("replace: " + in);
+ if (in == null)
+ return "null";
+
+ in = in.trim();
+ if ("${pom.version}".equals(in) || "${version}".equals(in)
+ || "${project.version}".equals(in))
+ return version;
+
+ if ("${basedir}".equals(in))
+ return pomFile.getParentFile().getAbsolutePath();
+
+ if ("${pom.name}".equals(in) || "${project.name}".equals(in))
+ return name;
+
+ if ("${pom.artifactId}".equals(in) || "${project.artifactId}".equals(in))
+ return artifactId;
+ if ("${pom.groupId}".equals(in) || "${project.groupId}".equals(in))
+ return groupId;
+
+ return in;
+ }
+
+ public String toString() {
+ return groupId + "+" + artifactId + "-" + version;
+ }
+
+ public File getLibrary(Scope action, URI... repositories) throws Exception {
+ MavenEntry entry = maven.getEntry(this);
+ File file = new File(entry.dir, action + ".lib");
+
+ if (file.isFile() && file.lastModified() >= getPomFile().lastModified())
+ return file;
+
+ file.delete();
+
+ Writer writer = new FileWriter(file);
+ doEntry(writer, this);
+ try {
+ for (Pom dep : getDependencies(action, repositories)) {
+ doEntry(writer, dep);
+ }
+ } finally {
+ writer.close();
+ }
+ return file;
+ }
+
+ /**
+ * @param writer
+ * @param dep
+ * @throws IOException
+ * @throws Exception
+ */
+ private void doEntry(Writer writer, Pom dep) throws IOException, Exception {
+ writer.append(dep.getGroupId());
+ writer.append("+");
+ writer.append(dep.getArtifactId());
+ writer.append(";version=\"");
+ writer.append(dep.getVersion());
+ writer.append("\"\n");
+ }
+
+ public File getPomFile() {
+ return pomFile;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public abstract java.io.File getArtifact() throws Exception;
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/ProjectPom.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/ProjectPom.java
new file mode 100644
index 0000000..8187954
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/ProjectPom.java
@@ -0,0 +1,204 @@
+package aQute.bnd.maven.support;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.xpath.*;
+
+import org.w3c.dom.*;
+
+import aQute.lib.io.*;
+
+public class ProjectPom extends Pom {
+
+ final List<URI> repositories = new ArrayList<URI>();
+ final Properties properties = new Properties();
+ String packaging;
+ String url;
+
+ ProjectPom(Maven maven, File pomFile) throws Exception {
+ super(maven, pomFile, pomFile.toURI());
+ }
+
+ @Override protected void parse(Document doc, XPath xp) throws Exception {
+
+ packaging = xp.evaluate("project/packaging", doc);
+ url = xp.evaluate("project/url", doc);
+
+ Node parent = (Node) xp.evaluate("project/parent", doc, XPathConstants.NODE);
+ if (parent != null && parent.hasChildNodes()) {
+ File parentFile = IO.getFile(getPomFile().getParentFile(), "../pom.xml");
+
+ String parentGroupId = xp.evaluate("groupId", parent).trim();
+ String parentArtifactId = xp.evaluate("artifactId", parent).trim();
+ String parentVersion = xp.evaluate("version", parent).trim();
+ String parentPath = xp.evaluate("relativePath", parent).trim();
+ if (parentPath != null && parentPath.length()!=0) {
+ parentFile = IO.getFile(getPomFile().getParentFile(), parentPath);
+ }
+ if (parentFile.isFile()) {
+ ProjectPom parentPom = new ProjectPom(maven, parentFile);
+ parentPom.parse();
+ dependencies.addAll(parentPom.dependencies);
+ for ( Enumeration<?> e = parentPom.properties.propertyNames(); e.hasMoreElements(); ) {
+ String key = (String) e.nextElement();
+ if ( ! properties.contains(key))
+ properties.put(key, parentPom.properties.get(key));
+ }
+ repositories.addAll(parentPom.repositories);
+
+ setNames(parentPom);
+ } else {
+ // This seems to be a bit bizarre, extending an external pom?
+ CachedPom parentPom = maven.getPom(parentGroupId, parentArtifactId, parentVersion);
+ dependencies.addAll(parentPom.dependencies);
+ setNames(parentPom);
+ }
+ }
+
+ NodeList propNodes = (NodeList) xp.evaluate("project/properties/*", doc,
+ XPathConstants.NODESET);
+ for (int i = 0; i < propNodes.getLength(); i++) {
+ Node node = propNodes.item(i);
+ String key = node.getNodeName();
+ String value = node.getTextContent();
+ if ( key == null || key.length()==0)
+ throw new IllegalArgumentException("Pom has an empty or null key");
+ if ( value == null || value.length()==0)
+ throw new IllegalArgumentException("Pom has an empty or null value for property " + key);
+ properties.setProperty(key, value.trim());
+ }
+
+ NodeList repos = (NodeList) xp.evaluate("project/repositories/repository/url", doc,
+ XPathConstants.NODESET);
+ for (int i = 0; i < repos.getLength(); i++) {
+ Node node = repos.item(i);
+ String URIString = node.getTextContent().trim();
+ URI uri = new URI(URIString);
+ if ( uri.getScheme() ==null )
+ uri = IO.getFile(pomFile.getParentFile(),URIString).toURI();
+ repositories.add(uri);
+ }
+
+ super.parse(doc, xp);
+ }
+
+// private void print(Node node, String indent) {
+// System.out.print(indent);
+// System.out.println(node.getNodeName());
+// Node rover = node.getFirstChild();
+// while ( rover != null) {
+// print( rover, indent+" ");
+// rover = rover.getNextSibling();
+// }
+// }
+
+ /**
+ * @param parentArtifactId
+ * @param parentGroupId
+ * @param parentVersion
+ * @throws Exception
+ */
+ private void setNames(Pom pom) throws Exception {
+ if (artifactId == null || artifactId.length()==0)
+ artifactId = pom.getArtifactId();
+ if (groupId == null || groupId.length()==0)
+ groupId = pom.getGroupId();
+ if (version == null || version.length()==0)
+ version = pom.getVersion();
+ if ( description == null )
+ description = pom.getDescription();
+ else
+ description = pom.getDescription() + "\n" + description;
+
+ }
+
+ class Rover {
+
+ public Rover(Rover rover, Dependency d) {
+ this.previous = rover;
+ this.dependency = d;
+ }
+
+ final Rover previous;
+ final Dependency dependency;
+
+ public boolean excludes(String name) {
+ return dependency.exclusions.contains(name) && previous != null
+ && previous.excludes(name);
+ }
+ }
+
+ public Set<Pom> getDependencies(Scope action) throws Exception {
+ return getDependencies(action, repositories.toArray(new URI[0]));
+ }
+
+ // Match any macros
+ final static Pattern MACRO = Pattern.compile("(\\$\\{\\s*([^}\\s]+)\\s*\\})");
+
+ protected String replace(String in) {
+ System.out.println("Replce: " + in);
+ if ( in == null) {
+ System.out.println("null??");
+ }
+ Matcher matcher = MACRO.matcher(in);
+ int last = 0;
+ StringBuilder sb = new StringBuilder();
+ while (matcher.find()) {
+ int n = matcher.start();
+ sb.append( in, last, n);
+ String replacement = get(matcher.group(2));
+ if ( replacement == null )
+ sb.append( matcher.group(1));
+ else
+ sb.append( replacement );
+ last = matcher.end();
+ }
+ if ( last == 0)
+ return in;
+
+ sb.append( in, last, in.length());
+ return sb.toString();
+ }
+
+ private String get(String key) {
+ if (key.equals("pom.artifactId"))
+ return artifactId;
+ if (key.equals("pom.groupId"))
+ return groupId;
+ if (key.equals("pom.version"))
+ return version;
+
+ if (key.equals("pom.name"))
+ return name;
+
+ String prop = properties.getProperty(key);
+ if ( prop != null )
+ return prop;
+
+ return System.getProperty(key);
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public String getPackaging() {
+ return packaging;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public String getProperty(String key) {
+ String s = properties.getProperty(key);
+ return replace(s);
+ }
+
+ @Override public File getArtifact() throws Exception {
+ return null;
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/bnd/maven/support/Repo.java b/bundleplugin/src/main/java/aQute/bnd/maven/support/Repo.java
new file mode 100644
index 0000000..dd1939f
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/bnd/maven/support/Repo.java
@@ -0,0 +1,5 @@
+package aQute.bnd.maven.support;
+
+public class Repo {
+
+}