Latest pre-release bnd code

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1387568 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
index 88ead98..ce5cb66 100644
--- a/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
+++ b/bundleplugin/src/main/java/aQute/bnd/build/WorkspaceRepository.java
@@ -32,6 +32,8 @@
 							foundVersion.put(version, file);
 						}
 					}
+					
+					jar.close();
 				}
 			}
 		}
@@ -49,7 +51,7 @@
 		return new File[0];
 	}
 
-	public File get(String bsn, String range, Strategy strategy, Map<String,String> properties) throws Exception {
+	private File get(String bsn, String range, Strategy strategy, Map<String,String> properties) throws Exception {
 		File[] files = get(bsn, range);
 
 		if (files.length == 0) {
@@ -114,6 +116,8 @@
 							names.add(bsn);
 						}
 					}
+					
+					jar.close();
 				}
 			}
 		}
diff --git a/bundleplugin/src/main/java/aQute/bnd/component/AnnotationReader.java b/bundleplugin/src/main/java/aQute/bnd/component/AnnotationReader.java
index 09c5f25..f9c396e 100644
--- a/bundleplugin/src/main/java/aQute/bnd/component/AnnotationReader.java
+++ b/bundleplugin/src/main/java/aQute/bnd/component/AnnotationReader.java
@@ -29,18 +29,35 @@
 	final static Pattern		PROPERTY_PATTERN		= Pattern
 																.compile("\\s*([^=\\s:]+)\\s*(?::\\s*(Boolean|Byte|Character|Short|Integer|Long|Float|Double|String)\\s*)?=(.*)");
 
-	public static final Version	V1_0					= new Version("1.0.0");																												// "1.1.0"
+	public static final Version	V1_0					= new Version("1.0.0");																												// "1.0.0"
 	public static final Version	V1_1					= new Version("1.1.0");																												// "1.1.0"
-	public static final Version	V1_2					= new Version("1.2.0");																												// "1.1.0"
-	static Pattern				BINDNAME				= Pattern.compile("(set|add|bind)?(.*)");
-	static Pattern				BINDDESCRIPTOR			= Pattern
-																.compile("\\(((L([^;]+);)(Ljava/util/Map;)?|Lorg/osgi/framework/ServiceReference;)\\)V");
+	public static final Version	V1_2					= new Version("1.2.0");																												// "1.2.0"
+//	public static final Version	V1_3					= new Version("1.3.0");																												// "1.3.0"
 
-	static Pattern				LIFECYCLEDESCRIPTOR		= Pattern
-																.compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)V");
-	static Pattern				REFERENCEBINDDESCRIPTOR	= Pattern
+	public static final String FELIX_1_2				= "http://felix.apache.org/xmlns/scr/v1.2.0-felix";
+	
+	static Pattern				BINDNAME				= Pattern.compile("(set|add|bind)?(.*)");
+	
+	static Pattern				BINDDESCRIPTORDS10			= Pattern
+																.compile("\\(((L([^;]+);)|Lorg/osgi/framework/ServiceReference;)\\)V");
+	static Pattern				BINDDESCRIPTORDS11			= Pattern
+																.compile("\\(((L([^;]+);)(Ljava/util/Map;)?|Lorg/osgi/framework/ServiceReference;)\\)V");
+	static Pattern				BINDDESCRIPTORDS13			= Pattern
+																.compile("\\(((L([^;]+);)(Ljava/util/Map;)?|Lorg/osgi/framework/ServiceReference;)\\)Ljava/util/Map;");
+	static Pattern				REFERENCEBINDDESCRIPTOR		= Pattern
 																.compile("\\(Lorg/osgi/framework/ServiceReference;\\)V");
 
+	static Pattern				LIFECYCLEDESCRIPTORDS10		= Pattern
+																.compile("\\((Lorg/osgi/service/component/ComponentContext;)\\)V");
+	static Pattern				LIFECYCLEDESCRIPTORDS11		= Pattern
+																.compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)V");
+	static Pattern				LIFECYCLEDESCRIPTORDS13		= Pattern
+																.compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;))*\\)Ljava/util/Map;");
+	static Pattern				DEACTIVATEDESCRIPTORDS11	= Pattern
+																.compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;)|(Ljava/lang/Integer;)|(I))*\\)V");
+	static Pattern				DEACTIVATEDESCRIPTORDS13	= Pattern
+																.compile("\\(((Lorg/osgi/service/component/ComponentContext;)|(Lorg/osgi/framework/BundleContext;)|(Ljava/util/Map;)|(Ljava/lang/Integer;)|(I))*\\)Ljava/util/Map;");
+
 	ComponentDef				component				= new ComponentDef();
 
 	Clazz						clazz;
@@ -50,18 +67,22 @@
 	Analyzer					analyzer;
 	MultiMap<String,String>		methods					= new MultiMap<String,String>();
 	TypeRef						extendsClass;
-	boolean						inherit;
+	final boolean						inherit;
 	boolean						baseclass				= true;
+	
+	final boolean						felixExtensions;
 
-	AnnotationReader(Analyzer analyzer, Clazz clazz, boolean inherit) {
+	AnnotationReader(Analyzer analyzer, Clazz clazz, boolean inherit, boolean felixExtensions) {
 		this.analyzer = analyzer;
 		this.clazz = clazz;
 		this.inherit = inherit;
+		this.felixExtensions = felixExtensions;
 	}
 
 	public static ComponentDef getDefinition(Clazz c, Analyzer analyzer) throws Exception {
 		boolean inherit = Processor.isTrue(analyzer.getProperty("-dsannotations-inherit"));
-		AnnotationReader r = new AnnotationReader(analyzer, c, inherit);
+		boolean felixExtensions = Processor.isTrue(analyzer.getProperty("-ds-felix-extensions"));
+		AnnotationReader r = new AnnotationReader(analyzer, c, inherit, felixExtensions);
 		return r.getDef();
 	}
 
@@ -112,15 +133,36 @@
 
 		if (methods.containsKey(value)) {
 			for (String descriptor : methods.get(value)) {
-				Matcher matcher = BINDDESCRIPTOR.matcher(descriptor);
+				Matcher matcher = BINDDESCRIPTORDS10.matcher(descriptor);
 				if (matcher.matches()) {
 					String type = matcher.group(2);
-					if (rdef.service.equals(Clazz.objectDescriptorToFQN(type)) || type.equals("Ljava/util/Map;")
+					if (rdef.service.equals(Clazz.objectDescriptorToFQN(type)) 
 							|| type.equals("Lorg/osgi/framework/ServiceReference;")) {
 
 						return value;
 					}
 				}
+				matcher = BINDDESCRIPTORDS11.matcher(descriptor);
+				if (matcher.matches()) {
+					String type = matcher.group(2);
+					if (rdef.service.equals(Clazz.objectDescriptorToFQN(type)) 
+							|| type.equals("Lorg/osgi/framework/ServiceReference;")) {
+						rdef.updateVersion(V1_1);
+						return value;
+					}
+				}
+				matcher = BINDDESCRIPTORDS13.matcher(descriptor);
+				if (felixExtensions && matcher.matches()) {
+					String type = matcher.group(2);
+					if (rdef.service.equals(Clazz.objectDescriptorToFQN(type)) 
+							|| type.equals("Lorg/osgi/framework/ServiceReference;")) {
+						rdef.updateVersion(V1_2);
+						if (component.xmlns == null) {
+							component.xmlns = FELIX_1_2;
+						}
+						return value;
+					}
+				}
 			}
 			analyzer.error(
 					"A related method to %s from the reference %s has no proper prototype for class %s. Expected void %s(%s s [,Map m] | ServiceReference r)",
@@ -153,27 +195,66 @@
 	/**
 	 * 
 	 */
-	protected void doDeactivate() {
-		if (!LIFECYCLEDESCRIPTOR.matcher(method.getDescriptor().toString()).matches())
+	protected void doActivate() {
+		String methodDescriptor = method.getDescriptor().toString();
+		if ("activate".equals(method.getName()) && LIFECYCLEDESCRIPTORDS10.matcher(methodDescriptor).matches()) {
+			component.activate = method.getName();			
+		} else if (LIFECYCLEDESCRIPTORDS11.matcher(methodDescriptor).matches()) {
+			component.activate = method.getName();	
+			component.updateVersion(V1_1);
+		} else if (felixExtensions && LIFECYCLEDESCRIPTORDS13.matcher(methodDescriptor).matches()) {
+			component.activate = method.getName();	
+			component.updateVersion(V1_2);
+			if (component.xmlns == null) {
+				component.xmlns = FELIX_1_2;
+			}
+		} else 
 			analyzer.error(
-					"Deactivate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
+					"Activate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
 					clazz, method.getDescriptor());
-		else {
+		
+	}
+
+	/**
+	 * 
+	 */
+	protected void doDeactivate() {
+		String methodDescriptor = method.getDescriptor().toString();
+		if ( "deactivate".equals(method.getName()) && LIFECYCLEDESCRIPTORDS10.matcher(methodDescriptor).matches()) {
+			component.deactivate = method.getName();			
+		} else if (DEACTIVATEDESCRIPTORDS11.matcher(methodDescriptor).matches()) {
 			component.deactivate = method.getName();
-		}
+			component.updateVersion(V1_1);
+		} else if (felixExtensions && DEACTIVATEDESCRIPTORDS13.matcher(methodDescriptor).matches()) {
+			component.deactivate = method.getName();
+			component.updateVersion(V1_2);
+			if (component.xmlns == null) {
+				component.xmlns = FELIX_1_2;
+			}
+		} else
+			analyzer.error(
+					"Deactivate method for %s does not have an acceptable prototype, only Map, ComponentContext, BundleContext, int, or Integer is allowed. Found: %s",
+					clazz, method.getDescriptor());
 	}
 
 	/**
 	 * 
 	 */
 	protected void doModified() {
-		if (!LIFECYCLEDESCRIPTOR.matcher(method.getDescriptor().toString()).matches())
+		if (LIFECYCLEDESCRIPTORDS11.matcher(method.getDescriptor().toString()).matches()) {
+			component.modified = method.getName();
+			component.updateVersion(V1_1);
+		} else if (felixExtensions && LIFECYCLEDESCRIPTORDS13.matcher(method.getDescriptor().toString()).matches()) {
+			component.modified = method.getName();
+			component.updateVersion(V1_2);
+			if (component.xmlns == null) {
+				component.xmlns = FELIX_1_2;
+			}
+		} else
+
 			analyzer.error(
 					"Modified method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
 					clazz, method.getDescriptor());
-		else {
-			component.modified = method.getName();
-		}
 	}
 
 	/**
@@ -202,13 +283,29 @@
 		} else {
 			// We have to find the type of the current method to
 			// link it to the referenced service.
-			Matcher m = BINDDESCRIPTOR.matcher(method.getDescriptor().toString());
+			String methodDescriptor = method.getDescriptor().toString();
+			Matcher m = BINDDESCRIPTORDS10.matcher(methodDescriptor);
 			if (m.matches()) {
 				def.service = Descriptors.binaryToFQN(m.group(3));
-			} else
-				throw new IllegalArgumentException(
-						"Cannot detect the type of a Component Reference from the descriptor: "
-								+ method.getDescriptor());
+			} else {
+				m = BINDDESCRIPTORDS11.matcher(methodDescriptor);
+				if (m.matches()) {
+					def.service = Descriptors.binaryToFQN(m.group(3));
+					def.updateVersion(V1_1);
+				} else {
+					m = BINDDESCRIPTORDS13.matcher(methodDescriptor);
+					if (felixExtensions && m.matches()) {
+						def.service = Descriptors.binaryToFQN(m.group(3));
+						def.updateVersion(V1_2);
+						if (component.xmlns == null) {
+							component.xmlns = FELIX_1_2;
+						}
+					} else 
+						throw new IllegalArgumentException(
+							"Cannot detect the type of a Component Reference from the descriptor: "
+									+ method.getDescriptor());
+				}
+			}
 		}
 
 		// Check if we have a target, this must be a filter
@@ -227,19 +324,6 @@
 	}
 
 	/**
-	 * 
-	 */
-	protected void doActivate() {
-		if (!LIFECYCLEDESCRIPTOR.matcher(method.getDescriptor().toString()).matches())
-			analyzer.error(
-					"Activate method for %s does not have an acceptable prototype, only Map, ComponentContext, or BundleContext is allowed. Found: %s",
-					clazz, method.getDescriptor());
-		else {
-			component.activate = method.getName();
-		}
-	}
-
-	/**
 	 * @param annotation
 	 * @throws Exception
 	 */
@@ -249,7 +333,6 @@
 		if (component.implementation != null)
 			return;
 
-		component.version = V1_0;
 		component.implementation = clazz.getClassName();
 		component.name = comp.name();
 		component.factory = comp.factory();
@@ -263,8 +346,10 @@
 		if (annotation.get("servicefactory") != null)
 			component.servicefactory = comp.servicefactory();
 
-		if (annotation.get("configurationPid") != null)
+		if (annotation.get("configurationPid") != null) {
 			component.configurationPid = comp.configurationPid();
+			component.updateVersion(V1_2);
+		}
 
 		if (annotation.get("xmlns") != null)
 			component.xmlns = comp.xmlns();
diff --git a/bundleplugin/src/main/java/aQute/bnd/component/ComponentDef.java b/bundleplugin/src/main/java/aQute/bnd/component/ComponentDef.java
index b368f0b..8c4b090 100644
--- a/bundleplugin/src/main/java/aQute/bnd/component/ComponentDef.java
+++ b/bundleplugin/src/main/java/aQute/bnd/component/ComponentDef.java
@@ -50,11 +50,8 @@
 	 */
 	void prepare(Analyzer analyzer) throws Exception {
 
-		for (ReferenceDef ref : references.values()) {
-			ref.prepare(analyzer);
-			if (ref.version.compareTo(version) > 0)
-				version = ref.version;
-		}
+		prepareVersion(analyzer);
+
 
 		if (implementation == null) {
 			analyzer.error("No Implementation defined for component " + name);
@@ -72,11 +69,6 @@
 		} else if (servicefactory != null && servicefactory)
 			analyzer.warning("The servicefactory:=true directive is set but no service is provided, ignoring it");
 
-		if (configurationPolicy != null)
-			version = ReferenceDef.max(version, AnnotationReader.V1_1);
-		if (configurationPid != null)
-			version = ReferenceDef.max(version, AnnotationReader.V1_2);
-
 		for (Map.Entry<String,List<String>> kvs : property.entrySet()) {
 			Tag property = new Tag("property");
 			String name = kvs.getKey();
@@ -110,6 +102,21 @@
 			propertyTags.add(property);
 		}
 	}
+
+	private void prepareVersion(Analyzer analyzer) throws Exception {
+		
+		for (ReferenceDef ref : references.values()) {
+			ref.prepare(analyzer);
+			updateVersion(ref.version);
+		}
+		if (configurationPolicy != null)
+			updateVersion(AnnotationReader.V1_1);
+		if (configurationPid != null)
+			updateVersion(AnnotationReader.V1_2);
+		if (modified != null)
+			updateVersion(AnnotationReader.V1_1);
+			
+	}
 	
 	void sortReferences() {
 		Map<String, ReferenceDef> temp = new TreeMap<String,ReferenceDef>(references);
@@ -147,10 +154,10 @@
 		if (factory != null)
 			component.addAttribute("factory", factory);
 
-		if (activate != null)
+		if (activate != null && version != AnnotationReader.V1_0)
 			component.addAttribute("activate", activate);
 
-		if (deactivate != null)
+		if (deactivate != null && version != AnnotationReader.V1_0)
 			component.addAttribute("deactivate", deactivate);
 
 		if (modified != null)
@@ -220,4 +227,16 @@
 		}
 		return v;
 	}
+	
+	void updateVersion(Version version) {
+		this.version = max(this.version, version);
+	}
+	
+	static <T extends Comparable<T>> T max(T a, T b) {
+		int n = a.compareTo(b);
+		if (n >= 0)
+			return a;
+		return b;
+	}
+
 }
\ No newline at end of file
diff --git a/bundleplugin/src/main/java/aQute/bnd/component/HeaderReader.java b/bundleplugin/src/main/java/aQute/bnd/component/HeaderReader.java
index f57b7cb..d69b0aa 100644
--- a/bundleplugin/src/main/java/aQute/bnd/component/HeaderReader.java
+++ b/bundleplugin/src/main/java/aQute/bnd/component/HeaderReader.java
@@ -169,7 +169,7 @@
 		properties(cd, info, name);
 		reference(info, impl, cd, bindmethods);
 		//compute namespace after references, an updated method means ds 1.2.
-		cd.xmlns = getNamespace(info, cd, lifecycleMethods);
+		getNamespace(info, cd, lifecycleMethods);
 		cd.prepare(analyzer);
 		return cd.getTag();
 
@@ -194,36 +194,39 @@
 	 * @param descriptors TODO
 	 * @return
 	 */
-	private String getNamespace(Map<String, String> info, ComponentDef cd, Map<String,MethodDef> descriptors) {
+	private void getNamespace(Map<String, String> info, ComponentDef cd, Map<String,MethodDef> descriptors) {
 		String namespace = info.get(COMPONENT_NAMESPACE);
 		if (namespace != null) {
-			return namespace;
+			cd.xmlns = namespace;
 		}
 		String version = info.get(COMPONENT_VERSION);
 		if (version != null) {
 			try {
 				Version v = new Version(version);
-				return NAMESPACE_STEM + "/v" + v;
+				cd.updateVersion(v);
 			} catch (Exception e) {
 				error("version: specified on component header but not a valid version: "
 						+ version);
-				return null;
+				return;
 			}
 		}
 		for (String key : info.keySet()) {
 			if (SET_COMPONENT_DIRECTIVES_1_2.contains(key)) {
-				return NAMESPACE_STEM + "/v1.2.0";
+				cd.updateVersion(AnnotationReader.V1_2);
+				return;
 			}
 		}
 		for (ReferenceDef rd: cd.references.values()) {
 			if (rd.updated != null) {
-				return NAMESPACE_STEM + "/v1.2.0";
+				cd.updateVersion(AnnotationReader.V1_2);
+				return;
 			}
 		}
 		//among other things this picks up any specified lifecycle methods
 		for (String key : info.keySet()) {
 			if (SET_COMPONENT_DIRECTIVES_1_1.contains(key)) {
-				return NAMESPACE_STEM + "/v1.1.0";
+				cd.updateVersion(AnnotationReader.V1_1);
+				return;
 			}
 		}
 		for (String lifecycle: LIFECYCLE_METHODS) {
@@ -231,10 +234,10 @@
 			MethodDef test = descriptors.get(lifecycle);
 			if (descriptors.containsKey(lifecycle) && (!(test.isPublic() || test.isProtected()) || 
 					rateLifecycle(test, "deactivate".equals(lifecycle)? allowedDeactivate: allowed) > 1)) {
-				return NAMESPACE_STEM + "/v1.1.0";
+				cd.updateVersion(AnnotationReader.V1_1);
+				return;
 			}
 		}
-		return null;
 	}
 
 	/**
diff --git a/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java b/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
index cb4d489..255eef1 100644
--- a/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
+++ b/bundleplugin/src/main/java/aQute/bnd/component/ReferenceDef.java
@@ -34,7 +34,7 @@
 			analyzer.error("No name for a reference");
 
 		if ((updated != null && !updated.equals("-")) || policyOption != null)
-			version = max(version, AnnotationReader.V1_2);
+			updateVersion(AnnotationReader.V1_2);
 
 		if (target != null) {
 			String error = Verifier.validateFilter(target);
@@ -81,16 +81,14 @@
 		return ref;
 	}
 
-	static <T extends Comparable<T>> T max(T a, T b) {
-		int n = a.compareTo(b);
-		if (n >= 0)
-			return a;
-		return b;
-	}
-
 	@Override
 	public String toString() {
 		return name;
 	}
+	
+	void updateVersion(Version version) {
+		this.version = ComponentDef.max(this.version, version);
+	}
+
 
 }
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
index 860fe08..d6be831 100755
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Analyzer.java
@@ -35,6 +35,7 @@
 import aQute.bnd.osgi.Descriptors.PackageRef;
 import aQute.bnd.osgi.Descriptors.TypeRef;
 import aQute.bnd.service.*;
+import aQute.bnd.version.*;
 import aQute.bnd.version.Version;
 import aQute.lib.base64.*;
 import aQute.lib.collections.*;
@@ -463,7 +464,7 @@
 			// This should not really happen. The code should never throw
 			// exceptions in normal situations. So if it happens we need more
 			// information. So to help diagnostics. We do a full property dump
-			throw new IllegalStateException("Calc manifest failed, state=\n"+getFlattenedProperties(), e);
+			throw new IllegalStateException("Calc manifest failed, state=\n" + getFlattenedProperties(), e);
 		}
 	}
 
@@ -1925,7 +1926,12 @@
 		Matcher m = Verifier.VERSIONRANGE.matcher(version);
 
 		if (m.matches()) {
-			return version;
+			try {
+				VersionRange vr = new VersionRange(version);
+				return version;
+			} catch( Exception e) {
+				// ignore
+			}
 		}
 
 		m = fuzzyVersionRange.matcher(version);
@@ -1945,6 +1951,15 @@
 			String micro = removeLeadingZeroes(m.group(5));
 			String qualifier = m.group(7);
 
+			if (qualifier == null) {
+				if (!isInteger(minor)) {
+					qualifier = minor;
+					minor = "0";
+				} else if (!isInteger(micro)) {
+					qualifier = micro;
+					micro = "0";
+				}
+			}
 			if (major != null) {
 				result.append(major);
 				if (minor != null) {
@@ -1971,6 +1986,20 @@
 		return version;
 	}
 
+	/**
+	 * TRhe cleanup version got confused when people used numeric dates like
+	 * 201209091230120 as qualifiers. These are too large for Integers. This
+	 * method checks if the all digit string fits in an integer.
+	 * <pre>
+	 * maxint = 2,147,483,647 = 10 digits
+	 * </pre>	 
+	 * @param integer
+	 * @return if this fits in an integer
+	 */
+	private static boolean isInteger(String minor) {
+		return minor.length() < 10 || (minor.length() == 10 && minor.compareTo("2147483647") < 0);
+	}
+
 	private static String removeLeadingZeroes(String group) {
 		if (group == null)
 			return null;
@@ -2329,15 +2358,19 @@
 			// We assume the user knows what he is
 			// doing and inserted a literal. So
 			// we ignore any not matched literals
-			if (instruction.isLiteral()) {
+			// #252, we should not be negated to make it a constant
+			if (instruction.isLiteral() && !instruction.isNegated()) {
 				result.merge(getPackageRef(instruction.getLiteral()), true, instructions.get(instruction));
 				i.remove();
 				continue;
 			}
 
 			// Not matching a negated instruction looks
-			// like an error ...
+			// like an error ... Though so, but
+			// in the second phase of Export-Package
+			// the !package will never match anymore.
 			if (instruction.isNegated()) {
+				i.remove();
 				continue;
 			}
 
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/CommandResource.java b/bundleplugin/src/main/java/aQute/bnd/osgi/CommandResource.java
index 0e73945..19c1a21 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/CommandResource.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/CommandResource.java
@@ -21,7 +21,6 @@
 	public void write(OutputStream out) throws IOException, Exception {
 		StringBuilder errors = new StringBuilder();
 		StringBuilder stdout = new StringBuilder();
-		try {
 			domain.trace("executing command %s", command);
 			Command cmd = new Command("sh");
 			cmd.setCwd(wd);
@@ -40,12 +39,8 @@
 			osw.append(stdout);
 			osw.flush();
 			if (result != 0) {
-				domain.error("executing command failed %s %s", command, stdout + "\n" + errors);
+				throw new Exception("executing command failed" + command + "\n"+ stdout + "\n" + errors);
 			}
-		}
-		catch (Exception e) {
-			domain.error("executing command failed %s %s", command, e.getMessage());
-		}
 	}
 
 	@Override
diff --git a/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java b/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
index 153674e..1b17aaf 100644
--- a/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
+++ b/bundleplugin/src/main/java/aQute/bnd/osgi/Domain.java
@@ -167,6 +167,7 @@
 	public Parameters getIncludeResource() {
 		Parameters ic = getParameters(INCLUDE_RESOURCE);
 		ic.putAll(getParameters(INCLUDERESOURCE));
+		ic.putAll(getParameters(WAB));
 		return ic;
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
index a227d71..72b380c 100644
--- a/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
+++ b/bundleplugin/src/main/java/aQute/lib/converter/Converter.java
@@ -22,7 +22,8 @@
 	}
 
 	boolean			fatal	= true;
-	Map<Type,Hook>	hooks	= new HashMap<Type,Converter.Hook>();
+	Map<Type,Hook>	hooks;
+	List<Hook>		allHooks;
 
 	public <T> T convert(Class<T> type, Object o) throws Exception {
 		// Is it a compatible type?
@@ -44,11 +45,21 @@
 			return null; // compatible with any
 		}
 
-		Hook hook = hooks.get(type);
-		if (hook != null) {
-			Object value = hook.convert(type, o);
-			if (value != null)
-				return value;
+		if (allHooks != null) {
+			for (Hook hook : allHooks) {
+				Object r = hook.convert(type, o);
+				if (r != null)
+					return r;
+			}
+		}
+
+		if (hooks != null) {
+			Hook hook = hooks.get(type);
+			if (hook != null) {
+				Object value = hook.convert(type, o);
+				if (value != null)
+					return value;
+			}
 		}
 
 		Class< ? > actualType = o.getClass();
@@ -243,22 +254,23 @@
 						f.set(instance, value);
 					}
 					catch (Exception ee) {
-						
+
 						// We cannot find the key, so try the __extra field
 						Field f = resultType.getField("__extra");
 						Map<String,Object> extra = (Map<String,Object>) f.get(instance);
-						if ( extra == null) {
+						if (extra == null) {
 							extra = new HashMap<String,Object>();
 							f.set(instance, extra);
 						}
-						extra.put(key, convert(Object.class,e.getValue()));
-						
+						extra.put(key, convert(Object.class, e.getValue()));
+
 					}
 				}
 				return instance;
 			}
 			catch (Exception e) {
-				return error("No conversion found for " + o.getClass() + " to " + type + ", error " + e + " on key " + key);
+				return error("No conversion found for " + o.getClass() + " to " + type + ", error " + e + " on key "
+						+ key);
 			}
 		}
 
@@ -441,7 +453,16 @@
 	}
 
 	public Converter hook(Type type, Hook hook) {
-		this.hooks.put(type, hook);
+		if (type != null) {
+			if (hooks == null)
+				hooks = new HashMap<Type,Converter.Hook>();
+			this.hooks.put(type, hook);
+		} else {
+			if (allHooks == null)
+				allHooks = new ArrayList<Converter.Hook>();
+			allHooks.add(hook);
+		}
+
 		return this;
 	}
 
diff --git a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
index 7fa7bb3..08e5d02 100644
--- a/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
+++ b/bundleplugin/src/main/java/aQute/lib/deployer/FileRepo.java
@@ -294,15 +294,15 @@
 	protected File putArtifact(File tmpFile, byte[] digest) throws Exception {
 		assert (tmpFile != null);
 
-		Jar jar = new Jar(tmpFile);
+		Jar tmpJar = new Jar(tmpFile);
 		try {
 			dirty = true;
 
-			String bsn = jar.getBsn();
+			String bsn = tmpJar.getBsn();
 			if (bsn == null)
 				throw new IllegalArgumentException("No bsn set in jar: " + tmpFile);
 
-			String versionString = jar.getVersion();
+			String versionString = tmpJar.getVersion();
 			if (versionString == null)
 				versionString = "0";
 			else if (!Verifier.isVersion(versionString))
@@ -322,9 +322,12 @@
 
 			reporter.trace("updating %s ", file.getAbsolutePath());
 
+			// An open jar on file will fail rename on windows
+			tmpJar.close();
+
 			IO.rename(tmpFile, file);
 
-			fireBundleAdded(jar, file);
+			fireBundleAdded(file);
 			afterPut(file, bsn, version, Hex.toHexString(digest));
 
 			// TODO like to beforeGet rid of the latest option. This is only
@@ -338,7 +341,7 @@
 			return file;
 		}
 		finally {
-			jar.close();
+			tmpJar.close();
 		}
 	}
 
@@ -676,18 +679,25 @@
 		exec(beforeGet, root.getAbsolutePath(), bsn, version);
 	}
 
-	protected void fireBundleAdded(Jar jar, File file) {
+	protected void fireBundleAdded(File file) {
 		if (registry == null)
 			return;
 		List<RepositoryListenerPlugin> listeners = registry.getPlugins(RepositoryListenerPlugin.class);
+		Jar jar = null;
 		for (RepositoryListenerPlugin listener : listeners) {
 			try {
+				if (jar == null)
+					jar = new Jar(file);
 				listener.bundleAdded(this, jar, file);
 			}
 			catch (Exception e) {
 				if (reporter != null)
 					reporter.warning("Repository listener threw an unexpected exception: %s", e);
 			}
+			finally {
+				if (jar != null)
+					jar.close();
+			}
 		}
 	}
 
@@ -699,34 +709,53 @@
 	 * @param target
 	 */
 	void exec(String line, Object... args) {
-		if (line == null)
+		if (line == null) {
 			return;
+		}
 
 		try {
-			if (args != null)
+			if (args != null) {
 				for (int i = 0; i < args.length; i++) {
-					if (i == 0)
-						line = line.replaceAll("\\$\\{@\\}", args[0].toString());
-					line = line.replaceAll("\\$" + i, args[i].toString());
+					if (i == 0) {
+						// replaceAll backslash magic ensures windows paths
+						// remain intact
+						line = line.replaceAll("\\$\\{@\\}", args[0].toString().replaceAll("\\\\", "\\\\\\\\"));
+					}
+					// replaceAll backslash magic ensures windows paths remain
+					// intact
+					line = line.replaceAll("\\$" + i, args[i].toString().replaceAll("\\\\", "\\\\\\\\"));
 				}
-
-			if (shell == null) {
-				shell = System.getProperty("os.name").toLowerCase().indexOf("win") > 0 ? "cmd.exe" : "sh";
 			}
-			Command cmd = new Command(shell);
+			// purge remaining placeholders
+			line = line.replaceAll("\\s*\\$[0-9]\\s*", "");
 
-			if (path != null) {
-				cmd.inherit();
-				String oldpath = cmd.var("PATH");
-				path = path.replaceAll("\\s*,\\s*", File.pathSeparator);
-				path = path.replaceAll("\\$\\{@\\}", oldpath);
-				cmd.var("PATH", path);
-			}
-
-			cmd.setCwd(getRoot());
+			int result = 0;
 			StringBuilder stdout = new StringBuilder();
 			StringBuilder stderr = new StringBuilder();
-			int result = cmd.execute(line, stdout, stderr);
+			if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
+
+				// FIXME ignoring possible shell setting stdin approach used
+				// below does not work in windows
+				Command cmd = new Command("cmd.exe /C " + line);
+				cmd.setCwd(getRoot());
+				result = cmd.execute(stdout, stderr);
+
+			} else {
+				if (shell == null) {
+					shell = "sh";
+				}
+				Command cmd = new Command(shell);
+				cmd.setCwd(getRoot());
+
+				if (path != null) {
+					cmd.inherit();
+					String oldpath = cmd.var("PATH");
+					path = path.replaceAll("\\s*,\\s*", File.pathSeparator);
+					path = path.replaceAll("\\$\\{@\\}", oldpath);
+					cmd.var("PATH", path);
+				}
+				result = cmd.execute(line, stdout, stderr);
+			}
 			if (result != 0) {
 				reporter.error("Command %s failed with %s %s %s", line, result, stdout, stderr);
 			}
diff --git a/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java b/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
index 7622ae7..349b381 100644
--- a/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
+++ b/bundleplugin/src/main/java/aQute/lib/json/ByteArrayHandler.java
@@ -3,15 +3,18 @@
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.regex.*;
 
+import aQute.lib.base64.*;
 import aQute.lib.hex.*;
 
 /**
- * 
  * Will now use hex for encoding byte arrays
- *
  */
 public class ByteArrayHandler extends Handler {
+	Pattern	ENCODING	= Pattern
+								.compile("((:?[\\dA-Za-z][\\dA-Za-z])*)|((:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/)+={1,3})");
+
 	@Override
 	void encode(Encoder app, Object object, Map<Object,Type> visited) throws IOException, Exception {
 		StringHandler.string(app, Hex.toHexString((byte[]) object));
@@ -31,6 +34,47 @@
 
 	@Override
 	Object decode(Decoder dec, String s) throws Exception {
-		return Hex.toByteArray(s);
+		boolean hex = true;
+		StringBuilder sb = new StringBuilder(s);
+		for (int i = sb.length() - 1; i >= 0; i--) {
+			char c = sb.charAt(i);
+			if (Character.isWhitespace(c))
+				sb.delete(i, i + 1);
+			else {
+				switch (c) {
+					case '0' :
+					case '1' :
+					case '2' :
+					case '3' :
+					case '4' :
+					case '5' :
+					case '6' :
+					case '7' :
+					case '8' :
+					case '9' :
+					case 'A' :
+					case 'B' :
+					case 'C' :
+					case 'D' :
+					case 'E' :
+					case 'F' :
+					case 'a' :
+					case 'b' :
+					case 'c' :
+					case 'd' :
+					case 'e' :
+					case 'f' :
+						break;
+
+					default :
+						hex = false;
+						break;
+				}
+			}
+		}
+		if ( hex)
+			return Hex.toByteArray(sb.toString());
+		else
+			return Base64.decodeBase64(sb.toString());
 	}
 }