This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.testing.osgi-mock-1.1.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-osgi-mock.git
commit 907c7dfd0c8a814dff013fd16c7fe9b67d46c6bb Author: Stefan Seifert <[email protected]> AuthorDate: Fri Nov 14 21:32:42 2014 +0000 SLING-4165 OSGi Mock: Fail-fast when calling methods requiring SCR metadata and this is not present git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/mocks/osgi-mock@1639791 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/sling/testing/mock/osgi/MapUtil.java | 52 +++++++++++++++ .../testing/mock/osgi/MockComponentContext.java | 4 +- .../apache/sling/testing/mock/osgi/MockOsgi.java | 31 ++------- .../testing/mock/osgi/MockServiceRegistration.java | 6 +- .../testing/mock/osgi/NoScrMetadataException.java | 31 +++++++++ .../sling/testing/mock/osgi/OsgiMetadataUtil.java | 74 ++++++++++------------ .../testing/mock/osgi/ReflectionServiceUtil.java | 23 ++++--- .../testing/mock/osgi/context/OsgiContextImpl.java | 9 +-- .../mock/osgi/ReflectionServiceUtilTest.java | 22 ++++++- .../mock/osgi/context/OsgiContextImplTest.java | 7 ++ 10 files changed, 177 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MapUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/MapUtil.java new file mode 100644 index 0000000..e819beb --- /dev/null +++ b/src/main/java/org/apache/sling/testing/mock/osgi/MapUtil.java @@ -0,0 +1,52 @@ +/* + * 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.sling.testing.mock.osgi; + +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +/** + * Map util methods. + */ +final class MapUtil { + + public static Dictionary<String, Object> toDictionary(Map<String, Object> map) { + if (map == null) { + return null; + } + return new Hashtable<String, Object>(map); + } + + public static Map<String, Object> toMap(Dictionary<String, Object> dictionary) { + if (dictionary == null) { + return null; + } + Map<String,Object> map = new HashMap<String, Object>(); + Enumeration<String> keys = dictionary.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + map.put(key, dictionary.get(key)); + } + return map; + } + +} diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockComponentContext.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockComponentContext.java index cf70b1d..40e57a9 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/MockComponentContext.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockComponentContext.java @@ -36,12 +36,12 @@ class MockComponentContext implements ComponentContext { private final Dictionary<String, Object> properties; public MockComponentContext(final MockBundleContext mockBundleContext) { - this(mockBundleContext, new Hashtable<String, Object>()); + this(mockBundleContext, null); } public MockComponentContext(final MockBundleContext mockBundleContext, final Dictionary<String, Object> properties) { this.bundleContext = mockBundleContext; - this.properties = properties; + this.properties = properties != null ? properties : new Hashtable<String, Object>(); } @Override diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java index 395f2c6..7a7474f 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockOsgi.java @@ -19,9 +19,6 @@ package org.apache.sling.testing.mock.osgi; import java.util.Dictionary; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Hashtable; import java.util.Map; import org.osgi.framework.BundleContext; @@ -75,7 +72,7 @@ public final class MockOsgi { * @return Mocked {@link ComponentContext} instance */ public static ComponentContext newComponentContext(Map<String, Object> properties) { - return newComponentContext(toDictionary(properties)); + return newComponentContext(MapUtil.toDictionary(properties)); } /** @@ -94,7 +91,7 @@ public final class MockOsgi { * @return Mocked {@link ComponentContext} instance */ public static ComponentContext newComponentContext(BundleContext bundleContext, Map<String, Object> properties) { - return newComponentContext(bundleContext, toDictionary(properties)); + return newComponentContext(bundleContext, MapUtil.toDictionary(properties)); } /** @@ -147,7 +144,7 @@ public final class MockOsgi { * @return true if activation method was called. False if it failed. */ public static boolean activate(Object target, Map<String, Object> properties) { - return activate(target, toDictionary(properties)); + return activate(target, MapUtil.toDictionary(properties)); } /** @@ -171,7 +168,7 @@ public final class MockOsgi { * @return true if activation method was called. False if it failed. */ public static boolean activate(Object target, BundleContext bundleContext, Map<String, Object> properties) { - return activate(target, bundleContext, toDictionary(properties)); + return activate(target, bundleContext, MapUtil.toDictionary(properties)); } /** @@ -202,7 +199,7 @@ public final class MockOsgi { * @return true if deactivation method was called. False if it failed. */ public static boolean deactivate(Object target, Map<String, Object> properties) { - return deactivate(target, toDictionary(properties)); + return deactivate(target, MapUtil.toDictionary(properties)); } /** @@ -225,7 +222,7 @@ public final class MockOsgi { * @return true if activation method was called. False if it failed. */ public static boolean deactivate(Object target, BundleContext bundleContext, Map<String, Object> properties) { - return deactivate(target, bundleContext, toDictionary(properties)); + return deactivate(target, bundleContext, MapUtil.toDictionary(properties)); } /** @@ -236,7 +233,7 @@ public final class MockOsgi { * @return true if modified method was called. False if it failed. */ public static boolean modified(Object target, BundleContext bundleContext, Dictionary<String, Object> properties) { - return modified(target, bundleContext, toMap(properties)); + return modified(target, bundleContext, MapUtil.toMap(properties)); } /** @@ -250,18 +247,4 @@ public final class MockOsgi { return ReflectionServiceUtil.modified(target, bundleContext, properties); } - static Dictionary<String, Object> toDictionary(Map<String, Object> map) { - return new Hashtable<String, Object>(map); - } - - static Map<String, Object> toMap(Dictionary<String, Object> dictionary) { - Map<String,Object> map = new HashMap<String, Object>(); - Enumeration<String> keys = dictionary.keys(); - while (keys.hasMoreElements()) { - String key = keys.nextElement(); - map.put(key, dictionary.get(key)); - } - return map; - } - } diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/MockServiceRegistration.java b/src/main/java/org/apache/sling/testing/mock/osgi/MockServiceRegistration.java index a3bdaf5..1dc564b 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/MockServiceRegistration.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/MockServiceRegistration.java @@ -44,12 +44,11 @@ class MockServiceRegistration implements ServiceRegistration { private Dictionary<String, Object> properties; private final ServiceReference serviceReference; - @SuppressWarnings("unchecked") public MockServiceRegistration(final Bundle bundle, final String[] clazzes, final Object service, final Dictionary<String, Object> properties) { this.clazzes = new HashSet<String>(ImmutableList.copyOf(clazzes)); this.service = service; - this.properties = properties != null ? properties : new Hashtable(); + this.properties = properties != null ? properties : new Hashtable<String,Object>(); this.properties.put(Constants.SERVICE_ID, ++serviceCounter); this.serviceReference = new MockServiceReference(bundle, this); readOsgiMetadata(); @@ -91,6 +90,9 @@ class MockServiceRegistration implements ServiceRegistration { private void readOsgiMetadata() { Class<?> serviceClass = service.getClass(); Document doc = OsgiMetadataUtil.getMetadata(serviceClass); + if (doc == null) { + return; + } // add service interfaces from OSGi metadata clazzes.addAll(OsgiMetadataUtil.getServiceInterfaces(serviceClass, doc)); diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/NoScrMetadataException.java b/src/main/java/org/apache/sling/testing/mock/osgi/NoScrMetadataException.java new file mode 100644 index 0000000..97b9260 --- /dev/null +++ b/src/main/java/org/apache/sling/testing/mock/osgi/NoScrMetadataException.java @@ -0,0 +1,31 @@ +/* + * 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.sling.testing.mock.osgi; + +/** + * Is thrown when a OSGi mock method required SCR metadata and this is not found in the classpath. + */ +public final class NoScrMetadataException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NoScrMetadataException(Class<?> type) { + super("No OSGi SCR metadata found in classpath at " + OsgiMetadataUtil.getMetadataPath(type)); + } + +} diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java index a1b7a0a..41fc4b5 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/OsgiMetadataUtil.java @@ -89,6 +89,10 @@ final class OsgiMetadataUtil { return NAMESPACES.keySet().iterator(); } }; + + public static String getMetadataPath(Class clazz) { + return "/OSGI-INF/" + StringUtils.substringBefore(clazz.getName(), "$") + ".xml"; + } /** * Try to read OSGI-metadata from /OSGI-INF and read all implemented @@ -97,7 +101,7 @@ final class OsgiMetadataUtil { * @return Metadata document or null */ public static Document getMetadata(Class clazz) { - String metadataPath = "/OSGI-INF/" + StringUtils.substringBefore(clazz.getName(), "$") + ".xml"; + String metadataPath = getMetadataPath(clazz); InputStream metadataStream = clazz.getResourceAsStream(metadataPath); if (metadataStream == null) { log.debug("No OSGi metadata found at {}", metadataPath); @@ -124,16 +128,14 @@ final class OsgiMetadataUtil { public static Set<String> getServiceInterfaces(Class clazz, Document metadata) { Set<String> serviceInterfaces = new HashSet<String>(); - if (metadata != null) { - String query = "/components/component[@name='" + clazz.getName() + "']/service/provide[@interface!='']"; - NodeList nodes = queryNodes(metadata, query); - if (nodes != null) { - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - String serviceInterface = getAttributeValue(node, "interface"); - if (StringUtils.isNotBlank(serviceInterface)) { - serviceInterfaces.add(serviceInterface); - } + String query = "/components/component[@name='" + clazz.getName() + "']/service/provide[@interface!='']"; + NodeList nodes = queryNodes(metadata, query); + if (nodes != null) { + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + String serviceInterface = getAttributeValue(node, "interface"); + if (StringUtils.isNotBlank(serviceInterface)) { + serviceInterfaces.add(serviceInterface); } } } @@ -142,20 +144,18 @@ final class OsgiMetadataUtil { public static Map<String, Object> getProperties(Class clazz, Document metadata) { Map<String, Object> props = new HashMap<String, Object>(); - if (metadata != null) { - String query = "/components/component[@name='" + clazz.getName() + "']/property[@name!='' and @value!='']"; - NodeList nodes = queryNodes(metadata, query); - if (nodes != null) { - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - String name = getAttributeValue(node, "name"); - String value = getAttributeValue(node, "value"); - String type = getAttributeValue(node, "type"); - if (StringUtils.equals("Integer", type)) { - props.put(name, Integer.parseInt(value)); - } else { - props.put(name, value); - } + String query = "/components/component[@name='" + clazz.getName() + "']/property[@name!='' and @value!='']"; + NodeList nodes = queryNodes(metadata, query); + if (nodes != null) { + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + String name = getAttributeValue(node, "name"); + String value = getAttributeValue(node, "value"); + String type = getAttributeValue(node, "type"); + if (StringUtils.equals("Integer", type)) { + props.put(name, Integer.parseInt(value)); + } else { + props.put(name, value); } } } @@ -164,14 +164,12 @@ final class OsgiMetadataUtil { public static List<Reference> getReferences(Class clazz, Document metadata) { List<Reference> references = new ArrayList<Reference>(); - if (metadata != null) { - String query = "/components/component[@name='" + clazz.getName() + "']/reference[@name!='']"; - NodeList nodes = queryNodes(metadata, query); - if (nodes != null) { - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - references.add(new Reference(node)); - } + String query = "/components/component[@name='" + clazz.getName() + "']/reference[@name!='']"; + NodeList nodes = queryNodes(metadata, query); + if (nodes != null) { + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + references.add(new Reference(node)); } } return references; @@ -190,12 +188,10 @@ final class OsgiMetadataUtil { } private static String getLifecycleMethodName(Class clazz, Document metadata, String methodName) { - if (metadata != null) { - String query = "/components/component[@name='" + clazz.getName() + "']"; - Node node = queryNode(metadata, query); - if (node != null) { - return getAttributeValue(node, methodName); - } + String query = "/components/component[@name='" + clazz.getName() + "']"; + Node node = queryNode(metadata, query); + if (node != null) { + return getAttributeValue(node, methodName); } return null; } diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtil.java b/src/main/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtil.java index 8269501..0ecc24f 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtil.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtil.java @@ -55,11 +55,15 @@ final class ReflectionServiceUtil { * @param componentContext Component context * @return true if activation/deactivation method was called. False if it failed. */ + @SuppressWarnings("unchecked") public static boolean activateDeactivate(Object target, ComponentContext componentContext, boolean activate) { Class<?> targetClass = target.getClass(); // get method name for activation/deactivation from osgi metadata Document metadata = OsgiMetadataUtil.getMetadata(targetClass); + if (metadata==null) { + throw new NoScrMetadataException(targetClass); + } String methodName; if (activate) { methodName = OsgiMetadataUtil.getActivateMethodName(targetClass, metadata); @@ -89,7 +93,7 @@ final class ReflectionServiceUtil { // 3. map method = getMethod(targetClass, methodName, new Class<?>[] { Map.class }); if (method != null) { - invokeMethod(target, method, new Object[] { componentContext.getProperties() }); + invokeMethod(target, method, new Object[] { MapUtil.toMap(componentContext.getProperties()) }); return true; } @@ -125,7 +129,7 @@ final class ReflectionServiceUtil { args[i] = componentContext.getBundleContext(); } else if (method.getParameterTypes()[i] == Map.class) { - args[i] = componentContext.getProperties(); + args[i] = MapUtil.toMap(componentContext.getProperties()); } else if (method.getParameterTypes()[i] == int.class || method.getParameterTypes()[i] == Integer.class) { args[i] = 0; @@ -157,6 +161,9 @@ final class ReflectionServiceUtil { // get method name for activation/deactivation from osgi metadata Document metadata = OsgiMetadataUtil.getMetadata(targetClass); + if (metadata==null) { + throw new NoScrMetadataException(targetClass); + } String methodName = OsgiMetadataUtil.getModifiedMethodName(targetClass, metadata); // try to find matching modified method and execute it @@ -257,7 +264,12 @@ final class ReflectionServiceUtil { // collect all declared reference annotations on class and field level Class<?> targetClass = target.getClass(); - List<Reference> references = getReferences(targetClass); + + Document metadata = OsgiMetadataUtil.getMetadata(targetClass); + if (metadata==null) { + throw new NoScrMetadataException(targetClass); + } + List<Reference> references = OsgiMetadataUtil.getReferences(targetClass, metadata); // try to inject services boolean allInjected = true; @@ -268,11 +280,6 @@ final class ReflectionServiceUtil { return allInjected; } - private static List<Reference> getReferences(Class clazz) { - Document metadata = OsgiMetadataUtil.getMetadata(clazz); - return OsgiMetadataUtil.getReferences(clazz, metadata); - } - private static boolean injectServiceReference(Reference reference, Object target, BundleContext bundleContext) { Class<?> targetClass = target.getClass(); diff --git a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java index d4cb74b..fb01663 100644 --- a/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java +++ b/src/main/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImpl.java @@ -32,8 +32,6 @@ import org.osgi.service.component.ComponentContext; import aQute.bnd.annotation.ConsumerType; -import com.google.common.collect.ImmutableMap; - /** * Defines OSGi context objects and helper methods. Should not be used directly * but via the {@link org.apache.sling.testing.mock.osgi.junit.OsgiContext} JUnit rule. @@ -108,8 +106,7 @@ public class OsgiContextImpl { if (properties != null) { serviceProperties = new Hashtable<String, Object>(properties); } - bundleContext().registerService(serviceClass != null ? serviceClass.getName() : null, service, - serviceProperties); + bundleContext().registerService(serviceClass != null ? serviceClass.getName() : null, service, serviceProperties); return service; } @@ -121,7 +118,7 @@ public class OsgiContextImpl { * @return Registered service instance */ public final <T> T registerInjectActivateService(final T service) { - return registerInjectActivateService(service, ImmutableMap.<String, Object> of()); + return registerInjectActivateService(service, null); } /** @@ -135,7 +132,7 @@ public class OsgiContextImpl { public final <T> T registerInjectActivateService(final T service, final Map<String, Object> properties) { MockOsgi.injectServices(service, bundleContext()); MockOsgi.activate(service, bundleContext(), properties); - registerService(null, service, null); + registerService(null, service, properties); return service; } diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtilTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtilTest.java index 37e5c49..b8b8cea 100644 --- a/src/test/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtilTest.java +++ b/src/test/java/org/apache/sling/testing/mock/osgi/ReflectionServiceUtilTest.java @@ -123,6 +123,26 @@ public class ReflectionServiceUtilTest { assertSame(service1, service4.getReference1()); } + @Test(expected=NoScrMetadataException.class) + public void testInjectServicesNoMetadata() { + MockOsgi.injectServices(new Object(), MockOsgi.newBundleContext()); + } + + @Test(expected=NoScrMetadataException.class) + public void testActivateNoMetadata() { + MockOsgi.activate(new Object()); + } + + @Test(expected=NoScrMetadataException.class) + public void testDeactivateNoMetadata() { + MockOsgi.deactivate(new Object()); + } + + @Test(expected=NoScrMetadataException.class) + public void testModifiedNoMetadata() { + MockOsgi.modified(new Object(), MockOsgi.newBundleContext(), ImmutableMap.<String,Object>of()); + } + public interface ServiceInterface1 { // no methods } @@ -173,7 +193,7 @@ public class ReflectionServiceUtilTest { @Activate private void activate(ComponentContext ctx) { this.componentContext = ctx; - this.config = MockOsgi.toMap(ctx.getProperties()); + this.config = MapUtil.toMap(ctx.getProperties()); } @Deactivate diff --git a/src/test/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImplTest.java b/src/test/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImplTest.java index 902624e..19c5a5b 100644 --- a/src/test/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImplTest.java +++ b/src/test/java/org/apache/sling/testing/mock/osgi/context/OsgiContextImplTest.java @@ -27,6 +27,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.apache.sling.testing.mock.osgi.NoScrMetadataException; +import org.apache.sling.testing.mock.osgi.ReflectionServiceUtilTest; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -90,6 +92,11 @@ public class OsgiContextImplTest { @Test public void testRegisterInjectActivate() { + context.registerInjectActivateService(new ReflectionServiceUtilTest.Service3()); + } + + @Test(expected=NoScrMetadataException.class) + public void testRegisterInjectActivateInvalid() { context.registerInjectActivateService(new Object()); } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
