blob: 84c35e69100298b25c829ec651699b9d67116dfa [file] [log] [blame]
/* Copyright 2006 aQute SARL
* Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
package aQute.lib.osgi;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.*;
import java.util.*;
import java.util.jar.*;
import java.util.regex.*;
import java.util.zip.*;
import aQute.bnd.make.*;
import aQute.lib.signing.*;
/**
* Include-Resource: ( [name '=' ] file )+
*
* Private-Package: package-decl ( ',' package-decl )*
*
* Export-Package: package-decl ( ',' package-decl )*
*
* Import-Package: package-decl ( ',' package-decl )*
*
* @version $Revision: 1.4 $
*/
public class Builder extends Analyzer {
private static final int SPLIT_MERGE_LAST = 1;
private static final int SPLIT_MERGE_FIRST = 2;
private static final int SPLIT_ERROR = 3;
private static final int SPLIT_FIRST = 4;
private static final int SPLIT_DEFAULT = 0;
List<File> sourcePath = new ArrayList<File>();
Pattern NAME_URL = Pattern
.compile("(.*)(http://.*)");
Make make = new Make(this);
private KeyStore keystore;
public Builder(Processor parent) {
super(parent);
}
public Builder() {
}
public Jar build() throws Exception {
if (getProperty(NOPE) != null)
return null;
String sub = getProperty(SUB);
if (sub != null && sub.trim().length() > 0)
error("Specified "
+ SUB
+ " but calls build() instead of builds() (might be a programmer error)");
if (getProperty(CONDUIT) != null)
error("Specified "
+ CONDUIT
+ " but calls build() instead of builds() (might be a programmer error");
dot = new Jar("dot");
addClose(dot);
try {
long modified = Long.parseLong(getProperty("base.modified"));
dot.updateModified(modified, "Base modified");
} catch (Exception e) {
}
doExpand(dot);
doIncludeResources(dot);
doConditional(dot);
// NEW!
// Check if we override the calculation of the
// manifest. We still need to calculated it because
// we need to have analyzed the classpath.
Manifest manifest = calcManifest();
String mf = getProperty(MANIFEST);
if (mf != null) {
File mff = getFile(mf);
if (mff.isFile()) {
try {
InputStream in = new FileInputStream(mff);
manifest = new Manifest(in);
in.close();
} catch (Exception e) {
error(MANIFEST + " while reading manifest file", e);
}
} else {
error(MANIFEST + ", no such file " + mf);
}
}
dot.setManifest(manifest);
// This must happen after we analyzed so
// we know what it is on the classpath
addSources(dot);
if (getProperty(POM) != null)
doPom(dot);
doVerify(dot);
if (dot.getResources().isEmpty())
error("The JAR is empty");
dot.updateModified(lastModified(), "Last Modified Processor");
dot.setName(getBsn());
sign(dot);
return dot;
}
/**
* Sign the jar file.
*
* -sign : <alias> [ ';' 'password:=' <password> ] [ ';' 'keystore:='
* <keystore> ] [ ';' 'sign-password:=' <pw> ] ( ',' ... )*
*
* @return
*/
void sign(Jar jar) throws Exception {
String signing = getProperty("-sign");
if (signing == null)
return;
trace("Signing %s, with %s", getBsn(), signing);
Map<String, Map<String, String>> infos = parseHeader(signing);
for (Map.Entry<String, Map<String, String>> entry : infos.entrySet()) {
String alias = entry.getKey();
String keystoreLocation = entry.getValue().get(
KEYSTORE_LOCATION_DIRECTIVE);
String keystoreProvider = entry.getValue().get(
KEYSTORE_PROVIDER_DIRECTIVE);
String password = entry.getValue().get(KEYSTORE_PASSWORD_DIRECTIVE);
String signpassword = entry.getValue().get(SIGN_PASSWORD_DIRECTIVE);
KeyStore keystore = getKeystore(keystoreLocation, keystoreProvider,
password);
if (keystore == null) {
error(
"Cannot find keystore to sign bundle: location=%s, provider=%s",
keystoreLocation, keystoreProvider);
} else {
if (signpassword == null && !"-none".equals(signpassword))
signpassword = password;
X509Certificate chain[] = getChain(keystore, alias);
if (chain == null) {
error(
"Trying to sign bundle but no signing certificate found: %s",
alias);
continue;
}
try {
Key key = keystore.getKey(alias,
(signpassword == null ? null : signpassword
.toCharArray()));
KeyFactory keyFactory = KeyFactory.getInstance(key
.getAlgorithm());
KeySpec keySpec = keyFactory.getKeySpec(key,
RSAPrivateKeySpec.class);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
JarSigner signer = new JarSigner(alias, privateKey, chain);
signer.signJar(jar);
} catch (UnrecoverableKeyException uke) {
error(
"Cannot get key to sign, likely invalid password: %s, for %s : %s",
signpassword, alias, uke);
}
}
}
}
X509Certificate[] getChain(KeyStore keystore, String alias)
throws Exception {
java.security.cert.Certificate[] chain = keystore
.getCertificateChain(alias);
X509Certificate certChain[] = new X509Certificate[chain.length];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
for (int count = 0; count < chain.length; count++) {
ByteArrayInputStream certIn = new ByteArrayInputStream(chain[count]
.getEncoded());
X509Certificate cert = (X509Certificate) cf
.generateCertificate(certIn);
certChain[count] = cert;
}
return certChain;
}
KeyStore getKeystore(String keystoreLocation, String keystoreProvider,
String password) throws Exception {
if (keystoreLocation == null) {
return getKeystore();
}
if (keystoreProvider == null)
keystoreProvider = "JKS";
KeyStore keystore = KeyStore.getInstance(keystoreProvider);
FileInputStream in = new FileInputStream(keystoreLocation);
try {
keystore.load(in, password == null ? null : password.toCharArray());
} finally {
in.close();
}
return keystore;
}
KeyStore getKeystore() throws Exception {
if (keystore != null) {
return keystore;
}
Map<String, Map<String, String>> header = parseHeader(getProperty(
"-keystore", "../cnf/keystore"));
if (header.size() == 0) {
error("Keystore needed but no -keystore specified");
return null;
}
if (header.size() > 1) {
warning("Multiple keystores specified, can only specify one: %s",
header);
}
for (Map.Entry<String, Map<String, String>> entry : header.entrySet()) {
return keystore = getKeystore(entry.getKey(), entry.getValue().get(
"provider:"), entry.getValue().get("password:"));
}
return null;
}
public boolean hasSources() {
return isTrue(getProperty(SOURCES));
}
protected String getImportPackages() {
String ip = super.getImportPackages();
if (ip != null)
return ip;
return "*";
}
private void doConditional(Jar dot) throws IOException {
Map<String, Map<String, String>> conditionals = getHeader(CONDITIONAL_PACKAGE);
int size;
do {
size = dot.getDirectories().size();
analyze();
analyzed = false;
Map<String, Map<String, String>> imports = getImports();
// Match the packages specified in conditionals
// against the imports. Any match must become a
// Private-Package
Map<String, Map<String, String>> filtered = merge(
CONDITIONAL_PACKAGE, conditionals, imports,
new HashSet<String>(), null);
// Imports can also specify a private import. These
// packages must also be copied to the bundle
for (Map.Entry<String, Map<String, String>> entry : getImports()
.entrySet()) {
String type = entry.getValue().get("import:");
if (type != null && type.equals("private"))
filtered.put(entry.getKey(), entry.getValue());
}
// remove existing packages to prevent merge errors
filtered.keySet().removeAll(dot.getPackages());
doExpand(dot, CONDITIONAL_PACKAGE + " Private imports",
replaceWitInstruction(filtered, CONDITIONAL_PACKAGE), false);
} while (dot.getDirectories().size() > size);
analyzed = true;
}
/**
* Intercept the call to analyze and cleanup versions after we have analyzed
* the setup. We do not want to cleanup if we are going to verify.
*/
public void analyze() throws IOException {
super.analyze();
cleanupVersion(imports);
cleanupVersion(exports);
String version = getProperty(BUNDLE_VERSION);
if (version != null)
setProperty(BUNDLE_VERSION, cleanupVersion(version));
}
public void cleanupVersion(Map<String, Map<String, String>> mapOfMap) {
for (Iterator<Map.Entry<String, Map<String, String>>> e = mapOfMap
.entrySet().iterator(); e.hasNext();) {
Map.Entry<String, Map<String, String>> entry = e.next();
Map<String, String> attributes = entry.getValue();
if (attributes.containsKey("version")) {
attributes.put("version", cleanupVersion(attributes
.get("version")));
}
}
}
/**
*
*/
private void addSources(Jar dot) {
if (!hasSources())
return;
Set<String> packages = new HashSet<String>();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
getProperties().store(out, "Generated by BND, at " + new Date());
dot.putResource("OSGI-OPT/bnd.bnd", new EmbeddedResource(out
.toByteArray(), 0));
out.close();
} catch (Exception e) {
error("Can not embed bnd file in JAR: " + e);
}
for (Iterator<String> cpe = classspace.keySet().iterator(); cpe
.hasNext();) {
String path = cpe.next();
path = path.substring(0, path.length() - ".class".length())
+ ".java";
String pack = getPackage(path).replace('.', '/');
if (pack.length() > 1)
pack = pack + "/";
boolean found = false;
String[] fixed = { "packageinfo", "package.html",
"module-info.java", "package-info.java" };
for (Iterator<File> i = getSourcePath().iterator(); i.hasNext();) {
File root = i.next();
File f = getFile(root, path);
if (f.exists()) {
found = true;
if (!packages.contains(pack)) {
packages.add(pack);
File bdir = getFile(root, pack);
for (int j = 0; j < fixed.length; j++) {
File ff = getFile(bdir, fixed[j]);
if (ff.isFile()) {
dot.putResource("OSGI-OPT/src/" + pack
+ fixed[j], new FileResource(ff));
}
}
}
dot
.putResource("OSGI-OPT/src/" + path,
new FileResource(f));
}
}
if (!found) {
for (Jar jar : classpath) {
Resource resource = jar.getResource(path);
if (resource != null) {
dot.putResource("OSGI-OPT/src", resource);
} else {
resource = jar.getResource("OSGI-OPT/src/" + path);
if (resource != null) {
dot.putResource("OSGI-OPT/src", resource);
}
}
}
}
if (getSourcePath().isEmpty())
warning("Including sources but " + SOURCEPATH
+ " does not contain any source directories ");
// TODO copy from the jars where they came from
}
}
boolean firstUse = true;
public Collection<File> getSourcePath() {
if (firstUse) {
firstUse = false;
String sp = getProperty(SOURCEPATH);
if (sp != null) {
Map<String, Map<String, String>> map = parseHeader(sp);
for (Iterator<String> i = map.keySet().iterator(); i.hasNext();) {
String file = i.next();
if (!isDuplicate(file)) {
File f = getFile(file);
if (!f.isDirectory()) {
error("Adding a sourcepath that is not a directory: "
+ f);
} else {
sourcePath.add(f);
}
}
}
}
}
return sourcePath;
}
private void doVerify(Jar dot) throws Exception {
Verifier verifier = new Verifier(dot, getProperties());
verifier.setPedantic(isPedantic());
// Give the verifier the benefit of our analysis
// prevents parsing the files twice
verifier.setClassSpace(classspace, contained, referred, uses);
verifier.verify();
getInfo(verifier);
}
private void doExpand(Jar jar) throws IOException {
if (getClasspath().size() == 0
&& (getProperty(EXPORT_PACKAGE) != null || getProperty(PRIVATE_PACKAGE) != null))
warning("Classpath is empty. Private-Package and Export-Package can only expand from the classpath when there is one");
Map<Instruction, Map<String, String>> all = newMap();
all.putAll(replaceWitInstruction(getHeader(PRIVATE_PACKAGE),
PRIVATE_PACKAGE));
all.putAll(replaceWitInstruction(getHeader(EXPORT_PACKAGE),
EXPORT_PACKAGE));
if (isTrue(getProperty(Constants.UNDERTEST))) {
all.putAll(replaceWitInstruction(parseHeader(getProperty(
Constants.TESTPACKAGES, "test;presence:=optional")),
TESTPACKAGES));
}
if (all.isEmpty() && !isResourceOnly()) {
warning("Neither Export-Package, Private-Package, -testpackages is set, therefore no packages will be included");
}
doExpand(jar, "Export-Package, Private-Package, or -testpackages", all,
true);
}
/**
*
* @param jar
* @param name
* @param instructions
*/
private void doExpand(Jar jar, String name,
Map<Instruction, Map<String, String>> instructions,
boolean mandatory) {
Set<Instruction> superfluous = removeMarkedDuplicates(instructions
.keySet());
for (Iterator<Jar> c = getClasspath().iterator(); c.hasNext();) {
Jar now = c.next();
doExpand(jar, instructions, now, superfluous);
}
if (mandatory && superfluous.size() > 0) {
StringBuffer sb = new StringBuffer();
String del = "Instructions in " + name + " that are never used: ";
for (Iterator<Instruction> i = superfluous.iterator(); i.hasNext();) {
Instruction p = i.next();
sb.append(del);
sb.append(p.getPattern());
del = ", ";
}
warning(sb.toString());
}
}
/**
* Iterate over each directory in the class path entry and check if that
* directory is a desired package.
*
* @param included
* @param classpathEntry
*/
private void doExpand(Jar jar,
Map<Instruction, Map<String, String>> included, Jar classpathEntry,
Set<Instruction> superfluous) {
loop: for (Map.Entry<String, Map<String, Resource>> directory : classpathEntry
.getDirectories().entrySet()) {
String path = directory.getKey();
if (doNotCopy.matcher(getName(path)).matches())
continue;
if (directory.getValue() == null)
continue;
String pack = path.replace('/', '.');
Instruction instr = matches(included, pack, superfluous);
if (instr != null) {
// System.out.println("Pattern match: " + pack + " " +
// instr.getPattern() + " " + instr.isNegated());
if (!instr.isNegated()) {
Map<String, Resource> contents = directory.getValue();
// What to do with split packages? Well if this
// directory already exists, we will check the strategy
// and react accordingly.
boolean overwriteResource = true;
if (jar.hasDirectory(path)) {
Map<String, String> directives = included.get(instr);
switch (getSplitStrategy((String) directives
.get(SPLIT_PACKAGE_DIRECTIVE))) {
case SPLIT_MERGE_LAST:
overwriteResource = true;
break;
case SPLIT_MERGE_FIRST:
overwriteResource = false;
break;
case SPLIT_ERROR:
error(diagnostic(pack, getClasspath(),
classpathEntry.source));
continue loop;
case SPLIT_FIRST:
continue loop;
default:
warning(diagnostic(pack, getClasspath(),
classpathEntry.source));
overwriteResource = false;
break;
}
}
jar.addDirectory(contents, overwriteResource);
String key = path + "/bnd.info";
Resource r = jar.getResource(key);
if (r != null)
jar.putResource(key, new PreprocessResource(this, r));
if (hasSources()) {
String srcPath = "OSGI-INF/src/" + path;
Map<String, Resource> srcContents = classpathEntry
.getDirectories().get(srcPath);
if (srcContents != null) {
jar.addDirectory(srcContents, overwriteResource);
}
}
}
}
}
}
/**
* Analyze the classpath for a split package
*
* @param pack
* @param classpath
* @param source
* @return
*/
private String diagnostic(String pack, List<Jar> classpath, File source) {
// Default is like merge-first, but with a warning
// Find the culprits
pack = pack.replace('.', '/');
List<Jar> culprits = new ArrayList<Jar>();
for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) {
Jar culprit = (Jar) i.next();
if (culprit.getDirectories().containsKey(pack)) {
culprits.add(culprit);
}
}
return "Split package "
+ pack
+ "\nUse directive -split-package:=(merge-first|merge-last|error|first) on Export/Private Package instruction to get rid of this warning\n"
+ "Package found in " + culprits + "\n"
+ "Reference from " + source + "\n" + "Classpath "
+ classpath;
}
private int getSplitStrategy(String type) {
if (type == null)
return SPLIT_DEFAULT;
if (type.equals("merge-last"))
return SPLIT_MERGE_LAST;
if (type.equals("merge-first"))
return SPLIT_MERGE_FIRST;
if (type.equals("error"))
return SPLIT_ERROR;
if (type.equals("first"))
return SPLIT_FIRST;
error("Invalid strategy for split-package: " + type);
return SPLIT_DEFAULT;
}
private Map<Instruction, Map<String, String>> replaceWitInstruction(
Map<String, Map<String, String>> header, String type) {
Map<Instruction, Map<String, String>> map = newMap();
for (Iterator<Map.Entry<String, Map<String, String>>> e = header
.entrySet().iterator(); e.hasNext();) {
Map.Entry<String, Map<String, String>> entry = e.next();
String pattern = entry.getKey();
Instruction instr = Instruction.getPattern(pattern);
String presence = entry.getValue().get(PRESENCE_DIRECTIVE);
if ("optional".equals(presence))
instr.setOptional();
map.put(instr, entry.getValue());
}
return map;
}
private Instruction matches(
Map<Instruction, Map<String, String>> instructions, String pack,
Set<Instruction> superfluousPatterns) {
for (Instruction pattern : instructions.keySet()) {
if (pattern.matches(pack)) {
superfluousPatterns.remove(pattern);
return pattern;
}
}
return null;
}
private Map<String, Map<String, String>> getHeader(String string) {
if (string == null)
return Collections.emptyMap();
return parseHeader(getProperty(string));
}
/**
* Parse the Bundle-Includes header. Files in the bundles Include header are
* included in the jar. The source can be a directory or a file.
*
* @throws IOException
* @throws FileNotFoundException
*/
private void doIncludeResources(Jar jar) throws Exception {
String includes = getProperty("Bundle-Includes");
if (includes == null) {
includes = getProperty(INCLUDERESOURCE);
if ( includes == null )
includes = getProperty("Include-Resource");
}
else
warning("Please use -includeresource instead of Bundle-Includes");
if (includes == null)
return;
Map<String, Map<String, String>> clauses = parseHeader(includes);
for (Iterator<Map.Entry<String, Map<String, String>>> i = clauses
.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, Map<String, String>> entry = i.next();
doIncludeResource(jar, entry.getKey(), entry.getValue());
}
}
private void doIncludeResource(Jar jar, String name,
Map<String, String> extra) throws ZipException, IOException,
Exception {
boolean preprocess = false;
if (name.startsWith("{") && name.endsWith("}")) {
preprocess = true;
name = name.substring(1, name.length() - 1).trim();
}
if (name.startsWith("@")) {
extractFromJar(jar, name.substring(1));
} else
/*
* NEW
*/
if (extra.containsKey("literal")) {
String literal = (String) extra.get("literal");
Resource r = new EmbeddedResource(literal.getBytes("UTF-8"), 0);
String x = (String) extra.get("extra");
if (x != null)
r.setExtra(x);
jar.putResource(name, r);
} else {
String source;
File sourceFile;
String destinationPath;
String parts[] = name.split("\\s*=\\s*");
if (parts.length == 1) {
// Just a copy, destination path defined by
// source path.
source = parts[0];
sourceFile = getFile(source);
// Directories should be copied to the root
// but files to their file name ...
if (sourceFile.isDirectory())
destinationPath = "";
else
destinationPath = sourceFile.getName();
} else {
source = parts[1];
sourceFile = getFile(source);
destinationPath = parts[0];
}
// Some people insist on ending a directory with
// a slash ... it now also works if you do /=dir
if (destinationPath.endsWith("/"))
destinationPath = destinationPath.substring(0, destinationPath
.length() - 1);
if (!sourceFile.exists()) {
noSuchFile(jar, name, extra, source, destinationPath);
} else
copy(jar, destinationPath, sourceFile, preprocess, extra);
}
}
private void noSuchFile(Jar jar, String clause, Map<String, String> extra,
String source, String destinationPath) throws Exception {
Jar src = getJarFromName(source, "Include-Resource " + source);
if (src != null) {
JarResource jarResource = new JarResource(src);
jar.putResource(destinationPath, jarResource);
} else {
Resource lastChance = make.process(source);
if (lastChance != null) {
jar.putResource(destinationPath, lastChance);
} else
error("Input file does not exist: " + source);
}
}
/**
* Extra resources from a Jar and add them to the given jar. The clause is
* the
*
* @param jar
* @param clauses
* @param i
* @throws ZipException
* @throws IOException
*/
private void extractFromJar(Jar jar, String name) throws ZipException,
IOException {
// Inline all resources and classes from another jar
// optionally appended with a modified regular expression
// like @zip.jar!/META-INF/MANIFEST.MF
int n = name.lastIndexOf("!/");
Pattern filter = null;
if (n > 0) {
String fstring = name.substring(n + 2);
name = name.substring(0, n);
filter = wildcard(fstring);
}
Jar sub = getJarFromName(name, "extract from jar");
if (sub == null)
error("Can not find JAR file " + name);
else
jar.addAll(sub, filter);
}
private Pattern wildcard(String spec) {
StringBuffer sb = new StringBuffer();
for (int j = 0; j < spec.length(); j++) {
char c = spec.charAt(j);
switch (c) {
case '.':
sb.append("\\.");
break;
case '*':
// test for ** (all directories)
if (j < spec.length() - 1 && spec.charAt(j + 1) == '*') {
sb.append(".*");
j++;
} else
sb.append("[^/]*");
break;
default:
sb.append(c);
break;
}
}
String s = sb.toString();
try {
return Pattern.compile(s);
} catch (Exception e) {
error("Invalid regular expression on wildcarding: " + spec
+ " used *");
}
return null;
}
private void copy(Jar jar, String path, File from, boolean preprocess,
Map<String, String> extra) throws Exception {
if (doNotCopy.matcher(from.getName()).matches())
return;
if (from.isDirectory()) {
String next = path;
if (next.length() != 0)
next += '/';
File files[] = from.listFiles();
for (int i = 0; i < files.length; i++) {
copy(jar, next + files[i].getName(), files[i], preprocess,
extra);
}
} else {
if (from.exists()) {
Resource resource = new FileResource(from);
if (preprocess) {
resource = new PreprocessResource(this, resource);
}
jar.putResource(path, resource);
} else {
error("Input file does not exist: " + from);
}
}
}
private String getName(String where) {
int n = where.lastIndexOf('/');
if (n < 0)
return where;
return where.substring(n + 1);
}
public void setSourcepath(File[] files) {
for (int i = 0; i < files.length; i++)
addSourcepath(files[i]);
}
public void addSourcepath(File cp) {
if (!cp.exists())
warning("File on sourcepath that does not exist: " + cp);
sourcePath.add(cp);
}
/**
* Create a POM reseource for Maven containing as much information as
* possible from the manifest.
*
* @param output
* @param builder
* @throws FileNotFoundException
* @throws IOException
*/
public void doPom(Jar dot) throws FileNotFoundException, IOException {
{
Manifest manifest = dot.getManifest();
String name = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_NAME);
String description = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_DESCRIPTION);
String docUrl = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_DOCURL);
String version = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_VERSION);
String bundleVendor = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_VENDOR);
ByteArrayOutputStream s = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(s);
String bsn = manifest.getMainAttributes().getValue(
Analyzer.BUNDLE_SYMBOLICNAME);
String licenses = manifest.getMainAttributes().getValue(
BUNDLE_LICENSE);
if (bsn == null) {
error("Can not create POM unless Bundle-SymbolicName is set");
return;
}
bsn = bsn.trim();
int n = bsn.lastIndexOf('.');
if (n <= 0) {
error("Can not create POM unless Bundle-SymbolicName contains a .");
ps.close();
s.close();
return;
}
String groupId = bsn.substring(0, n);
String artifactId = bsn.substring(n + 1);
ps
.println("<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>");
ps.println(" <modelVersion>4.0.0</modelVersion>");
ps.println(" <groupId>" + groupId + "</groupId>");
n = artifactId.indexOf(';');
if (n > 0)
artifactId = artifactId.substring(0, n).trim();
ps.println(" <artifactId>" + artifactId + "</artifactId>");
ps.println(" <version>" + version + "</version>");
if (description != null) {
ps.println(" <description>");
ps.print(" ");
ps.println(description);
ps.println(" </description>");
}
if (name != null) {
ps.print(" <name>");
ps.print(name);
ps.println("</name>");
}
if (docUrl != null) {
ps.print(" <url>");
ps.print(docUrl);
ps.println("</url>");
}
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);
}
ps.println(" <organization>");
ps.print(" <name>");
ps.print(namePart.trim());
ps.println("</name>");
if (urlPart != null) {
ps.print(" <url>");
ps.print(urlPart.trim());
ps.println("</url>");
}
ps.println(" </organization>");
}
if (licenses != null) {
ps.println(" <licenses>");
Map<String, Map<String, String>> map = parseHeader(licenses);
for (Iterator<Map.Entry<String, Map<String, String>>> e = map
.entrySet().iterator(); e.hasNext();) {
Map.Entry<String, Map<String, String>> entry = e.next();
ps.println(" <license>");
Map<String, String> values = entry.getValue();
print(ps, values, "name", "name", (String) values
.get("url"));
print(ps, values, "url", "url", null);
print(ps, values, "distribution", "distribution", "repo");
ps.println(" </license>");
}
ps.println(" </licenses>");
}
ps.println("</project>");
ps.close();
s.close();
dot
.putResource("pom.xml", new EmbeddedResource(s
.toByteArray(), 0));
}
}
/**
* Utility function to print a tag from a map
*
* @param ps
* @param values
* @param string
* @param tag
* @param object
*/
private void print(PrintStream ps, 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;
ps.println(" <" + tag + ">" + value.trim() + "</" + tag + ">");
}
public void close() {
super.close();
}
/**
* Build Multiple jars. If the -sub command is set, we filter the file with
* the given patterns.
*
* @return
* @throws Exception
*/
public Jar[] builds() throws Exception {
begin();
// Are we acting as a conduit for another JAR?
String conduit = getProperty(CONDUIT);
if (conduit != null) {
Map<String, Map<String, String>> map = parseHeader(conduit);
Jar[] result = new Jar[map.size()];
int n = 0;
for (String file : map.keySet()) {
Jar c = new Jar(getFile(file));
addClose(c);
String name = map.get(file).get("name");
if (name != null)
c.setName(name);
result[n++] = c;
}
return result;
}
// If no -sub property, then reuse this builder object
// other wise, build all the sub parts.
String sub = getProperty(SUB);
if (sub == null) {
Jar jar = build();
if (jar == null)
return new Jar[0];
return new Jar[] { jar };
}
List<Jar> result = new ArrayList<Jar>();
// Get the Instruction objects that match the sub header
Set<Instruction> subs = replaceWitInstruction(parseHeader(sub), SUB)
.keySet();
// Get the member files of this directory
List<File> members = new ArrayList<File>(Arrays.asList(getBase()
.listFiles()));
getProperties().remove(SUB);
// For each member file
nextFile: while (members.size() > 0) {
File file = members.remove(0);
if (file.equals(getPropertiesFile()))
continue nextFile;
for (Iterator<Instruction> i = subs.iterator(); i.hasNext();) {
Instruction instruction = i.next();
if (instruction.matches(file.getName())) {
if (!instruction.isNegated()) {
Builder builder = null;
try {
builder = getSubBuilder();
addClose(builder);
builder.setProperties(file);
builder.setProperty(SUB, "");
// Recursively build
// TODO
Jar jar = builder.build();
jar.setName(builder.getBsn());
result.add(jar);
} catch (Exception e) {
e.printStackTrace();
error("Sub Building " + file, e);
}
if (builder != null)
getInfo(builder, file.getName() + ": ");
}
// Because we matched (even though we could be negated)
// we skip any remaining searches
continue nextFile;
}
}
}
setProperty(SUB, sub);
return result.toArray(new Jar[result.size()]);
}
protected Builder getSubBuilder() throws Exception {
return new Builder(this);
}
/**
* A macro to convert a maven version to an OSGi version
*/
public String _maven_version(String args[]) {
if (args.length > 2)
error("${maven_version} macro receives too many arguments "
+ Arrays.toString(args));
else if (args.length < 2)
error("${maven_version} macro has no arguments, use ${maven_version;1.2.3-SNAPSHOT}");
else {
return cleanupVersion(args[1]);
}
return null;
}
public String _permissions(String args[]) throws IOException {
StringBuilder sb = new StringBuilder();
for (String arg : args) {
if ("packages".equals(arg) || "all".equals(arg)) {
for (String imp : getImports().keySet()) {
if (!imp.startsWith("java.")) {
sb.append("(org.osgi.framework.PackagePermission \"");
sb.append(imp);
sb.append("\" \"import\")\r\n");
}
}
for (String exp : getExports().keySet()) {
sb.append("(org.osgi.framework.PackagePermission \"");
sb.append(exp);
sb.append("\" \"export\")\r\n");
}
} else if ("admin".equals(arg) || "all".equals(arg)) {
sb.append("(org.osgi.framework.AdminPermission)");
} else if ("permissions".equals(arg))
;
else
error("Invalid option in ${permissions}: %s", arg);
}
return sb.toString();
}
}