FELIX-1771: Feature deployer is broken on windows
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@826649 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/deployer/blueprint/src/main/resources/OSGI-INF/blueprint/blueprint-deployer.xml b/karaf/deployer/blueprint/src/main/resources/OSGI-INF/blueprint/blueprint-deployer.xml
index b8d3aac..4726e44 100644
--- a/karaf/deployer/blueprint/src/main/resources/OSGI-INF/blueprint/blueprint-deployer.xml
+++ b/karaf/deployer/blueprint/src/main/resources/OSGI-INF/blueprint/blueprint-deployer.xml
@@ -19,11 +19,11 @@
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
- <service auto-export="interfaces">
- <bean class="org.apache.felix.karaf.deployer.blueprint.BlueprintDeploymentListener"/>
+ <service auto-export="interfaces" depends-on="blueprintUrlHandler">
+ <bean class="org.apache.felix.karaf.deployer.blueprint.BlueprintDeploymentListener" />
</service>
- <service interface="org.osgi.service.url.URLStreamHandlerService">
+ <service id="blueprintUrlHandler" interface="org.osgi.service.url.URLStreamHandlerService">
<service-properties>
<entry key="url.handler.protocol" value="blueprint"/>
</service-properties>
diff --git a/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureDeploymentListener.java b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureDeploymentListener.java
index dfefe60..51f6d0a 100644
--- a/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureDeploymentListener.java
+++ b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureDeploymentListener.java
@@ -16,27 +16,13 @@
*/
package org.apache.felix.karaf.deployer.features;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.URL;
import java.util.Arrays;
-import java.util.Collections;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
-import java.util.jar.JarFile;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -45,23 +31,22 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.felix.fileinstall.ArtifactUrlTransformer;
import org.apache.felix.karaf.features.Feature;
import org.apache.felix.karaf.features.FeaturesService;
import org.apache.felix.karaf.features.Repository;
-import org.apache.felix.fileinstall.ArtifactTransformer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
-import org.osgi.framework.Constants;
import org.osgi.framework.SynchronousBundleListener;
import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXParseException;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
/**
* A deployment listener able to hot deploy a feature descriptor
*/
-public class FeatureDeploymentListener implements ArtifactTransformer, SynchronousBundleListener {
+public class FeatureDeploymentListener implements ArtifactUrlTransformer, SynchronousBundleListener {
public static final String FEATURE_PATH = "org.apache.felix.karaf.shell.features";
@@ -114,7 +99,7 @@
return false;
}
- public File transform(File artifact, File tmpDir) {
+ public URL transform(URL artifact) {
// We can't really install the feature right now and just return nothing.
// We would not be aware of the fact that the bundle has been uninstalled
// and therefore require the feature to be uninstalled.
@@ -122,43 +107,9 @@
// this deployer: installation / uninstallation of the feature will be done
// while the bundle is installed / uninstalled.
try {
- File destFile = new File(tmpDir, artifact.getName() + ".jar");
- OutputStream os = new BufferedOutputStream(new FileOutputStream(destFile));
-
- String name = artifact.getCanonicalPath();
- int idx = name.lastIndexOf('/');
- if (idx >= 0) {
- name = name.substring(idx + 1);
- }
- String[] str = extractNameVersionType(name);
- // Create manifest
- Manifest m = new Manifest();
- m.getMainAttributes().putValue("Manifest-Version", "2");
- m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
- m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
- m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
- // Put content
- JarOutputStream out = new JarOutputStream(os);
- ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
- out.putNextEntry(e);
- m.write(out);
- out.closeEntry();
- e = new ZipEntry("META-INF/");
- out.putNextEntry(e);
- e = new ZipEntry("META-INF/" + FEATURE_PATH + "/");
- out.putNextEntry(e);
- out.closeEntry();
- e = new ZipEntry("META-INF/" + FEATURE_PATH + "/" + name);
- out.putNextEntry(e);
- InputStream fis = new BufferedInputStream(new FileInputStream(artifact));
- copyInputStream(fis, out);
- fis.close();
- out.closeEntry();
- out.close();
- os.close();
- return destFile;
+ return new URL("feature", null, artifact.toString());
} catch (Exception e) {
- LOGGER.error("Unable to build spring application bundle", e);
+ LOGGER.error("Unable to build feature bundle", e);
return null;
}
}
@@ -223,73 +174,4 @@
return db.parse(artifact);
}
- private static void copyInputStream(InputStream in, OutputStream out) throws IOException {
- byte[] buffer = new byte[8192];
- int len = in.read(buffer);
- while (len >= 0) {
- out.write(buffer, 0, len);
- len = in.read(buffer);
- }
- }
- private static final String DEFAULT_VERSION = "0.0.0";
-
- private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
- private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
-
- public static String[] extractNameVersionType(String url) {
- Matcher m = ARTIFACT_MATCHER.matcher(url);
- if (!m.matches()) {
- return new String[] { url, DEFAULT_VERSION };
- }
- else {
- //System.err.println(m.groupCount());
- //for (int i = 1; i <= m.groupCount(); i++) {
- // System.err.println("Group " + i + ": " + m.group(i));
- //}
-
- StringBuffer v = new StringBuffer();
- String d1 = m.group(1);
- String d2 = m.group(2);
- String d3 = m.group(3);
- String d4 = m.group(4);
- String d5 = m.group(5);
- String d6 = m.group(6);
- if (d2 != null) {
- v.append(d2);
- if (d3 != null) {
- v.append('.');
- v.append(d3);
- if (d4 != null) {
- v.append('.');
- v.append(d4);
- if (d5 != null) {
- v.append(".");
- cleanupModifier(v, d5);
- }
- } else if (d5 != null) {
- v.append(".0.");
- cleanupModifier(v, d5);
- }
- } else if (d5 != null) {
- v.append(".0.0.");
- cleanupModifier(v, d5);
- }
- }
- return new String[] { d1, v.toString(), d6 };
- }
- }
-
- private static void cleanupModifier(StringBuffer result, String modifier) {
- Matcher m = FUZZY_MODIFIDER.matcher(modifier);
- if (m.matches()) {
- modifier = m.group(1);
- }
- for (int i = 0; i < modifier.length(); i++) {
- char c = modifier.charAt(i);
- if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
- result.append(c);
- }
- }
- }
-
}
diff --git a/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureTransformer.java b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureTransformer.java
new file mode 100644
index 0000000..050bec3
--- /dev/null
+++ b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureTransformer.java
@@ -0,0 +1,146 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.deployer.features;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.Constants;
+
+/**
+ * Transform a feature descriptor into an osgi bundle
+ */
+public class FeatureTransformer {
+
+ public static void transform(URL url, OutputStream os) throws Exception {
+ // Heuristicly retrieve name and version
+ String name = url.getPath();
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0) {
+ name = name.substring(idx + 1);
+ }
+ String[] str = extractNameVersionType(name);
+ // Create manifest
+ Manifest m = new Manifest();
+ m.getMainAttributes().putValue("Manifest-Version", "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
+ m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
+ // Put content
+ JarOutputStream out = new JarOutputStream(os);
+ ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
+ out.putNextEntry(e);
+ m.write(out);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/");
+ out.putNextEntry(e);
+ e = new ZipEntry("META-INF/" + FeatureDeploymentListener.FEATURE_PATH + "/");
+ out.putNextEntry(e);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/" + FeatureDeploymentListener.FEATURE_PATH + "/" + name);
+ out.putNextEntry(e);
+ InputStream fis = url.openStream();
+ try {
+ copyInputStream(fis, out);
+ } finally {
+ fis.close();
+ }
+ out.closeEntry();
+ out.close();
+ os.close();
+ }
+
+ private static void copyInputStream(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[8192];
+ int len = in.read(buffer);
+ while (len >= 0) {
+ out.write(buffer, 0, len);
+ len = in.read(buffer);
+ }
+ }
+
+ private static final String DEFAULT_VERSION = "0.0.0";
+
+ private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
+ private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
+
+ public static String[] extractNameVersionType(String url) {
+ Matcher m = ARTIFACT_MATCHER.matcher(url);
+ if (!m.matches()) {
+ return new String[] { url, DEFAULT_VERSION };
+ }
+ else {
+ //System.err.println(m.groupCount());
+ //for (int i = 1; i <= m.groupCount(); i++) {
+ // System.err.println("Group " + i + ": " + m.group(i));
+ //}
+
+ StringBuffer v = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(2);
+ String d3 = m.group(3);
+ String d4 = m.group(4);
+ String d5 = m.group(5);
+ String d6 = m.group(6);
+ if (d2 != null) {
+ v.append(d2);
+ if (d3 != null) {
+ v.append('.');
+ v.append(d3);
+ if (d4 != null) {
+ v.append('.');
+ v.append(d4);
+ if (d5 != null) {
+ v.append(".");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.0.");
+ cleanupModifier(v, d5);
+ }
+ }
+ return new String[] { d1, v.toString(), d6 };
+ }
+ }
+
+ private static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = FUZZY_MODIFIDER.matcher(modifier);
+ if (m.matches()) {
+ modifier = m.group(1);
+ }
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
+ result.append(c);
+ }
+ }
+ }
+
+}
diff --git a/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureURLHandler.java b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureURLHandler.java
new file mode 100644
index 0000000..dfa1afb
--- /dev/null
+++ b/karaf/deployer/features/src/main/java/org/apache/felix/karaf/deployer/features/FeatureURLHandler.java
@@ -0,0 +1,73 @@
+package org.apache.felix.karaf.deployer.features;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+
+/**
+ * URL handler for features
+ */
+public class FeatureURLHandler extends AbstractURLStreamHandlerService {
+
+ private static Log logger = LogFactory.getLog(FeatureURLHandler.class);
+
+ private static String SYNTAX = "feature: xml-uri";
+
+ private URL featureXmlURL;
+
+ /**
+ * Open the connection for the given URL.
+ *
+ * @param url the url from which to open a connection.
+ * @return a connection on the specified URL.
+ * @throws java.io.IOException if an error occurs or if the URL is malformed.
+ */
+ @Override
+ public URLConnection openConnection(URL url) throws IOException {
+ if (url.getPath() == null || url.getPath().trim().length() == 0) {
+ throw new MalformedURLException("Path can not be null or empty. Syntax: " + SYNTAX );
+ }
+ featureXmlURL = new URL(url.getPath());
+
+ logger.debug("Blueprint xml URL is: [" + featureXmlURL + "]");
+ return new Connection(url);
+ }
+
+ public URL getFeatureXmlURL() {
+ return featureXmlURL;
+ }
+
+ public class Connection extends URLConnection {
+
+ public Connection(URL url) {
+ super(url);
+ }
+
+ @Override
+ public void connect() throws IOException {
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ FeatureTransformer.transform(featureXmlURL, os);
+ os.close();
+ return new ByteArrayInputStream(os.toByteArray());
+ } catch (Exception e) {
+ logger.error("Error opening blueprint xml url", e);
+ throw (IOException) new IOException("Error opening blueprint xml url").initCause(e);
+ }
+ }
+ }
+
+
+}
diff --git a/karaf/deployer/features/src/main/resources/OSGI-INF/blueprint/features-deployer.xml b/karaf/deployer/features/src/main/resources/OSGI-INF/blueprint/features-deployer.xml
index 12ba2d9..faad6b0 100644
--- a/karaf/deployer/features/src/main/resources/OSGI-INF/blueprint/features-deployer.xml
+++ b/karaf/deployer/features/src/main/resources/OSGI-INF/blueprint/features-deployer.xml
@@ -20,7 +20,7 @@
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
default-activation="lazy">
- <service ref="featureDeploymentListener" auto-export="interfaces"/>
+ <service ref="featureDeploymentListener" auto-export="interfaces" depends-on="featureUrlHandler"/>
<bean id="featureDeploymentListener" class="org.apache.felix.karaf.deployer.features.FeatureDeploymentListener"
init-method="init" destroy-method="destroy" activation="lazy">
@@ -30,4 +30,11 @@
</property>
</bean>
+ <service id="featureUrlHandler" interface="org.osgi.service.url.URLStreamHandlerService">
+ <service-properties>
+ <entry key="url.handler.protocol" value="feature"/>
+ </service-properties>
+ <bean class="org.apache.felix.karaf.deployer.features.FeatureURLHandler"/>
+ </service>
+
</blueprint>
diff --git a/karaf/deployer/spring/src/main/resources/OSGI-INF/blueprint/spring-deployer.xml b/karaf/deployer/spring/src/main/resources/OSGI-INF/blueprint/spring-deployer.xml
index 3e04d1e..558e003 100644
--- a/karaf/deployer/spring/src/main/resources/OSGI-INF/blueprint/spring-deployer.xml
+++ b/karaf/deployer/spring/src/main/resources/OSGI-INF/blueprint/spring-deployer.xml
@@ -19,11 +19,11 @@
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
- <service auto-export="interfaces">
+ <service auto-export="interfaces" depends-on="springUrlHandler">
<bean class="org.apache.felix.karaf.deployer.spring.SpringDeploymentListener"/>
</service>
- <service interface="org.osgi.service.url.URLStreamHandlerService">
+ <service id="springUrlHandler" interface="org.osgi.service.url.URLStreamHandlerService">
<service-properties>
<entry key="url.handler.protocol" value="spring"/>
</service-properties>