You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by da...@apache.org on 2011/11/18 16:51:33 UTC
svn commit: r1203707 - in /aries/trunk/spi-fly/spi-fly-core/src:
main/java/org/apache/aries/spifly/ main/java/org/apache/aries/spifly/api/
test/java/org/apache/aries/spifly/
Author: davidb
Date: Fri Nov 18 15:51:33 2011
New Revision: 1203707
URL: http://svn.apache.org/viewvc?rev=1203707&view=rev
Log:
Initial support for specifying the SPI Provider capability as
Provide-Capability: osgi.spi.provider
Retained the possibility of specifying this via the SPI-Provider header.
Added:
aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java (with props)
Modified:
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java
Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java?rev=1203707&r1=1203706&r2=1203707&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java Fri Nov 18 15:51:33 2011
@@ -132,7 +132,7 @@ public abstract class BaseActivator impl
}
public Set<WeavingData> getWeavingData(Bundle b) {
- // Simply return the value as it's already an unmovable set.
+ // Simply return the value as it's already an immutable set.
Set<WeavingData> wd = bundleWeavingData.get(b);
if (wd == null)
return null;
Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java?rev=1203707&r1=1203706&r2=1203707&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java Fri Nov 18 15:51:33 2011
@@ -24,6 +24,7 @@ import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
@@ -60,7 +61,15 @@ public class ProviderBundleTrackerCustom
return null;
}
- if (bundle.getHeaders().get(SpiFlyConstants.SPI_PROVIDER_HEADER) == null) {
+ List<String> providedServices = null;
+ if (bundle.getHeaders().get("Provide-Capability") != null) {
+ providedServices = readProvideCapability(bundle.getHeaders());
+ }
+ if (providedServices == null && bundle.getHeaders().get(SpiFlyConstants.SPI_PROVIDER_HEADER) != null) {
+ providedServices = new ArrayList<String>();
+ }
+
+ if (providedServices == null) {
log(LogService.LOG_INFO, "No '"
+ SpiFlyConstants.SPI_PROVIDER_HEADER
+ "' Manifest header. Skipping bundle: "
@@ -106,36 +115,83 @@ public class ProviderBundleTrackerCustom
new InputStreamReader(serviceFile.openStream()));
String className = null;
while((className = reader.readLine()) != null) {
- Class<?> cls = bundle.loadClass(className);
- Object o = cls.newInstance();
- log(LogService.LOG_INFO, "Instantiated SPI provider: " + o);
-
- Hashtable<String, Object> props = new Hashtable<String, Object>();
- props.put(SpiFlyConstants.SPI_PROVIDER_URL, serviceFile);
-
- String s = serviceFile.toExternalForm();
- int idx = s.lastIndexOf('/');
- String registrationClassName = className;
- if (s.length() > idx) {
- registrationClassName = s.substring(idx + 1);
+ try {
+ if (className.startsWith("#"))
+ continue; // a comment
+
+ Class<?> cls = bundle.loadClass(className);
+ Object o = cls.newInstance();
+ log(LogService.LOG_INFO, "Instantiated SPI provider: " + o);
+
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(SpiFlyConstants.SPI_PROVIDER_URL, serviceFile);
+
+ String s = serviceFile.toExternalForm();
+ int idx = s.lastIndexOf('/');
+ String registrationClassName = className;
+ if (s.length() > idx) {
+ registrationClassName = s.substring(idx + 1);
+ }
+
+ ServiceRegistration reg = bundle.getBundleContext()
+ .registerService(registrationClassName, o, props);
+ registrations.add(reg);
+
+ activator.registerProviderBundle(registrationClassName, bundle);
+ log(LogService.LOG_INFO, "Registered service: " + reg);
+ } catch (Exception e) {
+ log(LogService.LOG_WARNING,
+ "Could not load SPI implementation referred from " + serviceFile, e);
}
-
- ServiceRegistration reg = bundle.getBundleContext()
- .registerService(registrationClassName, o, props);
- registrations.add(reg);
-
- activator.registerProviderBundle(registrationClassName, bundle);
- log(LogService.LOG_INFO, "Registered service: " + reg);
}
- } catch (Exception e) {
- log(LogService.LOG_WARNING,
- "Could not load SPI implementation referred from " + serviceFile, e);
+ } catch (IOException e) {
+ log(LogService.LOG_WARNING, "Could not read SPI metadata from " + serviceFile, e);
}
}
return registrations;
}
+ // An empty list returned means 'all SPIs'
+ // A return value of null means no SPIs
+ // A populated list means: only these SPIs
+ private List<String> readProvideCapability(Dictionary<?,?> headers) {
+ if (headers.get(SpiFlyConstants.PROVIDE_CAPABILITY) == null)
+ return null;
+
+ String pc = headers.get(SpiFlyConstants.PROVIDE_CAPABILITY).toString();
+ for (String c : pc.split(",")) { // TODO cover situation where ',' is inside a string (e.g. service:List<String>).
+ c = c.trim();
+ int idx = c.indexOf(';');
+ if (idx < 0)
+ continue;
+
+ String ns = c.substring(0, idx);
+ if (!SpiFlyConstants.SPI_CAPABILITY_NAMESPACE.equals(ns))
+ continue;
+
+ List<String> providedServices = new ArrayList<String>();
+ String attrs = c.substring(idx);
+ for (String attr : attrs.split(";")) {
+ attr = attr.trim();
+ if (attr.startsWith("service=")) {
+ String val = attr.substring("service=".length());
+ providedServices.add(val);
+ } else if (attr.startsWith("service:String=")) {
+ String val = attr.substring("service:String=".length());
+ providedServices.add(val);
+ } else if (attr.startsWith("service:List<String>=")) {
+ String val = attr.substring("service:List<String>=".length());
+ for (String v : val.split(",")) {
+ providedServices.add(v.trim());
+ }
+ }
+ }
+ return providedServices; // An empty array means all SPIs
+ }
+ return null;
+ }
+
private List<URL> getMetaInfServiceURLsFromJar(URL url) {
List<URL> urls = new ArrayList<URL>();
try {
Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java?rev=1203707&r1=1203706&r2=1203707&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java Fri Nov 18 15:51:33 2011
@@ -19,9 +19,15 @@
package org.apache.aries.spifly.api;
public interface SpiFlyConstants {
+ String PROVIDE_CAPABILITY = "Provide-Capability";
+ String REQUIRE_CAPABILITY = "Require-Capability";
+
String SPI_CONSUMER_HEADER = "SPI-Consumer";
String SPI_PROVIDER_HEADER = "SPI-Provider";
+ String SPI_CAPABILITY_NAMESPACE = "osgi.spi.provider";
+ String EXTENDER_CAPABILITY_NAMESPACE = "osgi.jse.serviceloader";
+
String PROCESSED_SPI_CONSUMER_HEADER = "X-SpiFly-Processed-SPI-Consumer";
String SPI_PROVIDER_URL = "spi.provider.url";
Added: aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java?rev=1203707&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java Fri Nov 18 15:51:33 2011
@@ -0,0 +1,205 @@
+/**
+ * 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.aries.spifly;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.aries.spifly.impl1.MySPIImpl1;
+import org.apache.aries.spifly.impl2.MySPIImpl2a;
+import org.apache.aries.spifly.impl2.MySPIImpl2b;
+import org.apache.aries.spifly.impl3.MySPIImpl3;
+import org.easymock.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+public class ProviderBundleTrackerCustomizerTest2 {
+ @Test
+ public void testAddingRemovedBundle() throws Exception {
+ Bundle spiBundle = EasyMock.createMock(Bundle.class);
+ EasyMock.replay(spiBundle);
+ BaseActivator activator = new BaseActivator() {
+ @Override
+ public void start(BundleContext context) throws Exception {}
+ };
+
+ ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, spiBundle);
+
+ ServiceRegistration sreg = EasyMock.createMock(ServiceRegistration.class);
+ sreg.unregister();
+ EasyMock.expectLastCall();
+ EasyMock.replay(sreg);
+
+ BundleContext implBC = mockSPIBundleContext(sreg);
+ Bundle implBundle = mockSPIBundle(implBC);
+
+ assertEquals("Precondition", 0, activator.findProviderBundles("org.apache.aries.mytest.MySPI").size());
+ // Call addingBundle();
+ List<ServiceRegistration> registrations = customizer.addingBundle(implBundle, null);
+ Collection<Bundle> bundles = activator.findProviderBundles("org.apache.aries.mytest.MySPI");
+ assertEquals(1, bundles.size());
+ assertSame(implBundle, bundles.iterator().next());
+
+ // The bc.registerService() call should now have been made
+ EasyMock.verify(implBC);
+
+ // Call removedBundle();
+ customizer.removedBundle(implBundle, null, registrations);
+ // sreg.unregister() should have been called.
+ EasyMock.verify(sreg);
+ }
+
+ @Test
+ public void testAddingBundleSPIBundle() throws Exception {
+ BundleContext implBC = mockSPIBundleContext(EasyMock.createNiceMock(ServiceRegistration.class));
+ Bundle spiBundle = mockSPIBundle(implBC);
+
+ ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(EasyMock.createNiceMock(BaseActivator.class), spiBundle);
+ assertNull("The SpiFly bundle itself should be ignored", customizer.addingBundle(spiBundle, null));
+ }
+
+ @Test
+ public void testAddingNonOptInBundle() throws Exception {
+ BundleContext implBC = mockSPIBundleContext(EasyMock.createNiceMock(ServiceRegistration.class));
+ Bundle implBundle = mockSPIBundle(implBC, null);
+
+ ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(EasyMock.createNiceMock(BaseActivator.class), null);
+ assertNull("Bundle doesn't opt-in so should be ignored", customizer.addingBundle(implBundle, null));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testAddingBundleWithBundleClassPath() throws Exception {
+ Bundle spiBundle = EasyMock.createMock(Bundle.class);
+ EasyMock.replay(spiBundle);
+ BaseActivator activator = new BaseActivator() {
+ @Override
+ public void start(BundleContext context) throws Exception {}
+ };
+
+ ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, spiBundle);
+
+ BundleContext implBC = EasyMock.createMock(BundleContext.class);
+ EasyMock.<Object>expect(implBC.registerService(
+ EasyMock.eq("org.apache.aries.mytest.MySPI"),
+ EasyMock.isA(MySPIImpl2a.class),
+ (Dictionary<String,?>) EasyMock.anyObject())).andReturn(EasyMock.createNiceMock(ServiceRegistration.class));
+ EasyMock.<Object>expect(implBC.registerService(
+ EasyMock.eq("org.apache.aries.mytest.MySPI"),
+ EasyMock.isA(MySPIImpl2b.class),
+ (Dictionary<String,?>) EasyMock.anyObject())).andReturn(EasyMock.createNiceMock(ServiceRegistration.class));
+ EasyMock.<Object>expect(implBC.registerService(
+ EasyMock.eq("org.apache.aries.mytest.MySPI"),
+ EasyMock.isA(MySPIImpl3.class),
+ (Dictionary<String,?>) EasyMock.anyObject())).andReturn(EasyMock.createNiceMock(ServiceRegistration.class));
+ EasyMock.replay(implBC);
+
+
+ Bundle implBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(implBundle.getBundleContext()).andReturn(implBC).anyTimes();
+
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put("Provide-Capability", "osgi.spi.provider; effective:=active;");
+ headers.put(Constants.BUNDLE_CLASSPATH, ".,non-jar.jar,embedded.jar,embedded2.jar");
+ EasyMock.expect(implBundle.getHeaders()).andReturn(headers).anyTimes();
+
+ URL embeddedJar = getClass().getResource("/embedded.jar");
+ assertNotNull("precondition", embeddedJar);
+ EasyMock.expect(implBundle.getResource("embedded.jar")).andReturn(embeddedJar).anyTimes();
+ URL embedded2Jar = getClass().getResource("/embedded2.jar");
+ assertNotNull("precondition", embedded2Jar);
+ EasyMock.expect(implBundle.getResource("embedded2.jar")).andReturn(embedded2Jar).anyTimes();
+ URL dir = new URL("jar:" + embeddedJar + "!/META-INF/services");
+ assertNotNull("precondition", dir);
+ EasyMock.expect(implBundle.getResource("/META-INF/services")).andReturn(dir).anyTimes();
+ EasyMock.expect(implBundle.findEntries((String) EasyMock.anyObject(), (String) EasyMock.anyObject(), EasyMock.anyBoolean())).
+ andReturn(null).anyTimes();
+
+ ClassLoader cl = new URLClassLoader(new URL [] {embeddedJar}, getClass().getClassLoader());
+ Class<?> clsA = cl.loadClass("org.apache.aries.spifly.impl2.MySPIImpl2a");
+ EasyMock.<Object>expect(implBundle.loadClass("org.apache.aries.spifly.impl2.MySPIImpl2a")).andReturn(clsA).anyTimes();
+ Class<?> clsB = cl.loadClass("org.apache.aries.spifly.impl2.MySPIImpl2b");
+ EasyMock.<Object>expect(implBundle.loadClass("org.apache.aries.spifly.impl2.MySPIImpl2b")).andReturn(clsB).anyTimes();
+ ClassLoader cl2 = new URLClassLoader(new URL [] {embedded2Jar}, getClass().getClassLoader());
+ Class<?> clsC = cl2.loadClass("org.apache.aries.spifly.impl3.MySPIImpl3");
+ EasyMock.<Object>expect(implBundle.loadClass("org.apache.aries.spifly.impl3.MySPIImpl3")).andReturn(clsC).anyTimes();
+ EasyMock.replay(implBundle);
+
+ assertEquals("Precondition", 0, activator.findProviderBundles("org.apache.aries.mytest.MySPI").size());
+ // Call addingBundle();
+ List<ServiceRegistration> registrations = customizer.addingBundle(implBundle, null);
+ Collection<Bundle> bundles = activator.findProviderBundles("org.apache.aries.mytest.MySPI");
+ assertEquals(1, bundles.size());
+ assertSame(implBundle, bundles.iterator().next());
+
+ // The bc.registerService() call should now have been made
+ EasyMock.verify(implBC);
+ }
+
+ @SuppressWarnings("unchecked")
+ private BundleContext mockSPIBundleContext(ServiceRegistration sreg) {
+ BundleContext implBC = EasyMock.createMock(BundleContext.class);
+ EasyMock.<Object>expect(implBC.registerService(
+ EasyMock.eq("org.apache.aries.mytest.MySPI"),
+ EasyMock.isA(MySPIImpl1.class),
+ (Dictionary<String,?>) EasyMock.anyObject())).andReturn(sreg);
+ EasyMock.replay(implBC);
+ return implBC;
+ }
+
+ private Bundle mockSPIBundle(BundleContext implBC) throws ClassNotFoundException {
+ return mockSPIBundle(implBC, "osgi.spi.provider; effective:=active;");
+ }
+
+ private Bundle mockSPIBundle(BundleContext implBC, String spiProviderHeader) throws ClassNotFoundException {
+ Bundle implBundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(implBundle.getBundleContext()).andReturn(implBC).anyTimes();
+
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ if (spiProviderHeader != null)
+ headers.put("Provide-Capability", spiProviderHeader);
+ EasyMock.expect(implBundle.getHeaders()).andReturn(headers).anyTimes();
+
+ // List the resources found at META-INF/services in the test bundle
+ URL dir = getClass().getResource("impl1/META-INF/services");
+ assertNotNull("precondition", dir);
+ EasyMock.expect(implBundle.getResource("/META-INF/services")).andReturn(dir).anyTimes();
+ URL res = getClass().getResource("impl1/META-INF/services/org.apache.aries.mytest.MySPI");
+ assertNotNull("precondition", res);
+ EasyMock.expect(implBundle.findEntries("META-INF/services", "*", false)).andReturn(
+ Collections.enumeration(Collections.singleton(res))).anyTimes();
+ Class<?> cls = getClass().getClassLoader().loadClass("org.apache.aries.spifly.impl1.MySPIImpl1");
+ EasyMock.<Object>expect(implBundle.loadClass("org.apache.aries.spifly.impl1.MySPIImpl1")).andReturn(cls).anyTimes();
+ EasyMock.replay(implBundle);
+ return implBundle;
+ }
+}
Propchange: aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java
------------------------------------------------------------------------------
svn:mime-type = text/plain