Author: sseifert Date: Thu Feb 19 22:54:45 2015 New Revision: 1661030 URL: http://svn.apache.org/r1661030 Log: SLING-4439 implement dynamic service registration
Added: sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/serviceComponents.xml - copied, changed from r1660969, sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest.xml Removed: sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest.xml Modified: sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java sling/trunk/testing/mocks/osgi-mock/src/test/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtilTest.java sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml Modified: sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java?rev=1661030&r1=1661029&r2=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java (original) +++ sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/MockBundleContext.java Thu Feb 19 22:54:45 2015 @@ -94,7 +94,7 @@ class MockBundleContext implements Bundl * @param registration */ private void handleRefsUpdateOnRegister(MockServiceRegistration registration) { - List<ReferenceInfo> affectedReferences = OsgiServiceUtil.getMatchingReferences(registeredServices, registration); + List<ReferenceInfo> affectedReferences = OsgiServiceUtil.getMatchingDynamicReferences(registeredServices, registration); for (ReferenceInfo referenceInfo : affectedReferences) { Reference reference = referenceInfo.getReference(); switch (reference.getCardinality()) { @@ -125,7 +125,7 @@ class MockBundleContext implements Bundl * @param registration */ private void handleRefsUpdateOnUnregister(MockServiceRegistration registration) { - List<ReferenceInfo> affectedReferences = OsgiServiceUtil.getMatchingReferences(registeredServices, registration); + List<ReferenceInfo> affectedReferences = OsgiServiceUtil.getMatchingDynamicReferences(registeredServices, registration); for (ReferenceInfo referenceInfo : affectedReferences) { Reference reference = referenceInfo.getReference(); switch (reference.getCardinality()) { Modified: sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java?rev=1661030&r1=1661029&r2=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java (original) +++ sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java Thu Feb 19 22:54:45 2015 @@ -20,7 +20,9 @@ package org.apache.sling.testing.mock.os import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -40,6 +42,7 @@ import javax.xml.xpath.XPathFactory; import org.apache.commons.lang3.StringUtils; import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -52,6 +55,7 @@ import com.google.common.cache.CacheLoad import com.google.common.cache.LoadingCache; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableList; /** * Helper methods to parse OSGi metadata. @@ -82,13 +86,15 @@ final class OsgiMetadataUtil { private static final LoadingCache<Class, OsgiMetadata> METADATA_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<Class, OsgiMetadata>() { @Override public OsgiMetadata load(Class clazz) throws Exception { - Document metadataDocument = OsgiMetadataUtil.getMetadataDocument(clazz); - if (metadataDocument == null) { - return NULL_METADATA; - } - else { - return new OsgiMetadata(clazz, metadataDocument); + List<Document> metadataDocuments = OsgiMetadataUtil.getMetadataDocument(clazz); + if (metadataDocuments != null) { + for (Document metadataDocument : metadataDocuments) { + if (matchesService(clazz, metadataDocument)) { + return new OsgiMetadata(clazz, metadataDocument); + } + } } + return NULL_METADATA; } }); @@ -114,7 +120,11 @@ final class OsgiMetadataUtil { }; public static String getMetadataPath(Class clazz) { - return "/OSGI-INF/" + StringUtils.substringBefore(clazz.getName(), "$") + ".xml"; + return "OSGI-INF/" + StringUtils.substringBefore(clazz.getName(), "$") + ".xml"; + } + + public static String getOldMetadataMultiPath() { + return "OSGI-INF/serviceComponents.xml"; } /** @@ -138,32 +148,63 @@ final class OsgiMetadataUtil { } } - private static Document getMetadataDocument(Class clazz) { + private static List<Document> getMetadataDocument(Class clazz) { String metadataPath = getMetadataPath(clazz); - InputStream metadataStream = clazz.getResourceAsStream(metadataPath); - if (metadataStream == null) { - log.debug("No OSGi metadata found at {}", metadataPath); - return null; + InputStream metadataStream = OsgiMetadataUtil.class.getClassLoader().getResourceAsStream(metadataPath); + if (metadataStream == null) { + String oldMetadataPath = getOldMetadataMultiPath(); + log.debug("No OSGi metadata found at {}, try to fallback to {}", metadataPath, oldMetadataPath); + + try { + Enumeration<URL> metadataUrls = OsgiMetadataUtil.class.getClassLoader().getResources(oldMetadataPath); + List<Document> docs = new ArrayList<Document>(); + while (metadataUrls.hasMoreElements()) { + URL metadataUrl = metadataUrls.nextElement(); + metadataStream = metadataUrl.openStream(); + docs.add(toXmlDocument(metadataStream, oldMetadataPath)); + } + if (docs.size() == 0) { + return null; + } + else { + return docs; + } + } + catch (IOException ex) { + throw new RuntimeException("Unable to read classpath resource: " + oldMetadataPath, ex); + } + } + else { + return ImmutableList.of(toXmlDocument(metadataStream, metadataPath)); } + } + + private static Document toXmlDocument(InputStream inputStream, String path) { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - return documentBuilder.parse(metadataStream); + return documentBuilder.parse(inputStream); } catch (ParserConfigurationException ex) { - throw new RuntimeException("Unable to read classpath resource: " + metadataPath, ex); + throw new RuntimeException("Unable to read classpath resource: " + path, ex); } catch (SAXException ex) { - throw new RuntimeException("Unable to read classpath resource: " + metadataPath, ex); + throw new RuntimeException("Unable to read classpath resource: " + path, ex); } catch (IOException ex) { - throw new RuntimeException("Unable to read classpath resource: " + metadataPath, ex); + throw new RuntimeException("Unable to read classpath resource: " + path, ex); } finally { try { - metadataStream.close(); + inputStream.close(); } catch (IOException ex) { // ignore } } } + private static boolean matchesService(Class clazz, Document metadata) { + String query = "/components/component[@name='" + clazz.getName() + "']"; + NodeList nodes = queryNodes(metadata, query); + return nodes != null && nodes.getLength() > 0; + } + private static Set<String> getServiceInterfaces(Class clazz, Document metadata) { Set<String> serviceInterfaces = new HashSet<String>(); String query = "/components/component[@name='" + clazz.getName() + "']/service/provide[@interface!='']"; @@ -317,6 +358,7 @@ final class OsgiMetadataUtil { private final String name; private final String interfaceType; private final ReferenceCardinality cardinality; + private final ReferencePolicy policy; private final String bind; private final String unbind; @@ -325,6 +367,7 @@ final class OsgiMetadataUtil { this.name = getAttributeValue(node, "name"); this.interfaceType = getAttributeValue(node, "interface"); this.cardinality = toCardinality(getAttributeValue(node, "cardinality")); + this.policy = toPolicy(getAttributeValue(node, "policy")); this.bind = getAttributeValue(node, "bind"); this.unbind = getAttributeValue(node, "unbind"); } @@ -333,15 +376,6 @@ final class OsgiMetadataUtil { return clazz; } - private ReferenceCardinality toCardinality(String value) { - for (ReferenceCardinality item : ReferenceCardinality.values()) { - if (StringUtils.equals(item.getCardinalityString(), value)) { - return item; - } - } - return ReferenceCardinality.MANDATORY_UNARY; - } - public String getName() { return this.name; } @@ -353,6 +387,10 @@ final class OsgiMetadataUtil { public ReferenceCardinality getCardinality() { return this.cardinality; } + + public ReferencePolicy getPolicy() { + return policy; + } public String getBind() { return this.bind; @@ -362,6 +400,24 @@ final class OsgiMetadataUtil { return this.unbind; } + private static ReferenceCardinality toCardinality(String value) { + for (ReferenceCardinality item : ReferenceCardinality.values()) { + if (StringUtils.equals(item.getCardinalityString(), value)) { + return item; + } + } + return ReferenceCardinality.MANDATORY_UNARY; + } + + private static ReferencePolicy toPolicy(String value) { + for (ReferencePolicy item : ReferencePolicy.values()) { + if (StringUtils.equalsIgnoreCase(item.name(), value)) { + return item; + } + } + return ReferencePolicy.STATIC; + } + } } Modified: sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java?rev=1661030&r1=1661029&r2=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java (original) +++ sling/trunk/testing/mocks/osgi-mock/src/main/java/org/apache/sling/testing/mock/osgi/OsgiServiceUtil.java Thu Feb 19 22:54:45 2015 @@ -30,6 +30,7 @@ import java.util.SortedSet; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.OsgiMetadata; import org.apache.sling.testing.mock.osgi.OsgiMetadataUtil.Reference; import org.osgi.framework.BundleContext; @@ -67,8 +68,14 @@ final class OsgiServiceUtil { } else { methodName = metadata.getDeactivateMethodName(); } + boolean fallbackDefaultName = false; if (StringUtils.isEmpty(methodName)) { - return false; + fallbackDefaultName = true; + if (activate) { + methodName = "activate"; + } else { + methodName = "deactivate"; + } } // try to find matching activate/deactivate method and execute it @@ -143,6 +150,9 @@ final class OsgiServiceUtil { return true; } + if (fallbackDefaultName) { + return false; + } throw new RuntimeException("No matching " + (activate ? "activation" : "deactivation") + " method with name '" + methodName + "' " + " found in class " + targetClass.getName()); } @@ -402,16 +412,18 @@ final class OsgiServiceUtil { * @param registration Service registration * @return List of references */ - public static List<ReferenceInfo> getMatchingReferences(SortedSet<MockServiceRegistration> registeredServices, + public static List<ReferenceInfo> getMatchingDynamicReferences(SortedSet<MockServiceRegistration> registeredServices, MockServiceRegistration registration) { List<ReferenceInfo> references = new ArrayList<ReferenceInfo>(); for (MockServiceRegistration existingRegistration : registeredServices) { OsgiMetadata metadata = OsgiMetadataUtil.getMetadata(existingRegistration.getService().getClass()); if (metadata != null) { for (Reference reference : metadata.getReferences()) { - for (String serviceInterface : registration.getClasses()) { - if (StringUtils.equals(serviceInterface, reference.getInterfaceType())) { - references.add(new ReferenceInfo(existingRegistration, reference)); + if (reference.getPolicy() == ReferencePolicy.DYNAMIC) { + for (String serviceInterface : registration.getClasses()) { + if (StringUtils.equals(serviceInterface, reference.getInterfaceType())) { + references.add(new ReferenceInfo(existingRegistration, reference)); + } } } } Modified: sling/trunk/testing/mocks/osgi-mock/src/test/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtilTest.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/test/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtilTest.java?rev=1661030&r1=1661029&r2=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/test/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtilTest.java (original) +++ sling/trunk/testing/mocks/osgi-mock/src/test/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtilTest.java Thu Feb 19 22:54:45 2015 @@ -19,6 +19,7 @@ package org.apache.sling.testing.mock.osgi; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.util.List; @@ -53,12 +54,7 @@ public class OsgiMetadataUtilTest { @Test public void testNoMetadata() { OsgiMetadata metadata = OsgiMetadataUtil.getMetadata(ServiceWithoutMetadata.class); - - Set<String> serviceInterfaces = metadata.getServiceInterfaces(); - assertEquals(0, serviceInterfaces.size()); - - Map<String, Object> props = metadata.getProperties(); - assertEquals(0, props.size()); + assertNull(metadata); } @Test Modified: sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml?rev=1661030&r1=1661029&r2=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml (original) +++ sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest.xml Thu Feb 19 22:54:45 2015 @@ -20,10 +20,10 @@ <scr:component name="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service3" activate="activate" deactivate="deactivate" modified="modified"> <implementation class="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service3"/> <property name="service.pid" value="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service3"/> - <reference name="reference1" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface1" cardinality="1..1" policy="static" bind="bindReference1" unbind="unbindReference1"/> - <reference name="reference1Optional" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface1Optional" cardinality="0..1" policy="static" bind="bindReference1Optional" unbind="unbindReference1Optional"/> - <reference name="reference2" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface2" cardinality="1..n" policy="static" bind="bindReference2" unbind="unbindReference2"/> - <reference name="reference3" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface3" cardinality="0..n" policy="static" bind="bindReference3" unbind="unbindReference3"/> + <reference name="reference1" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface1" cardinality="1..1" policy="dynamic" bind="bindReference1" unbind="unbindReference1"/> + <reference name="reference1Optional" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface1Optional" cardinality="0..1" policy="dynamic" bind="bindReference1Optional" unbind="unbindReference1Optional"/> + <reference name="reference2" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface2" cardinality="1..n" policy="dynamic" bind="bindReference2" unbind="unbindReference2"/> + <reference name="reference3" interface="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$ServiceInterface3" cardinality="0..n" policy="dynamic" bind="bindReference3" unbind="unbindReference3"/> </scr:component> <scr:component name="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service4"> <implementation class="org.apache.sling.testing.mock.osgi.OsgiServiceUtilTest$Service4"/> Copied: sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/serviceComponents.xml (from r1660969, sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest.xml) URL: http://svn.apache.org/viewvc/sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/serviceComponents.xml?p2=sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/serviceComponents.xml&p1=sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest.xml&r1=1660969&r2=1661030&rev=1661030&view=diff ============================================================================== --- sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest.xml (original) +++ sling/trunk/testing/mocks/osgi-mock/src/test/resources/OSGI-INF/serviceComponents.xml Thu Feb 19 22:54:45 2015 @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- This file follows the old SCR convention using a fixed name "serviceComponents.xml" --> <components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"> <scr:component name="org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest$ServiceWithMetadata" activate="activate"> <implementation class="org.apache.sling.testing.mock.osgi.OsgiMetadataUtilTest$ServiceWithMetadata"/>