You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:21:52 UTC
[sling-org-apache-sling-testing-osgi-mock] 02/09: SLING-6586
Loading SCR metadata fails for components with name != class name
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-2.2.4
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-osgi-mock.git
commit 869e9ee7b1be38d25535b4cd048b7a827a681084
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Wed Mar 1 17:27:15 2017 +0000
SLING-6586 Loading SCR metadata fails for components with name != class name
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/mocks/osgi-mock@1785001 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 33 ++++-
.../testing/mock/osgi/NoScrMetadataException.java | 2 +-
.../sling/testing/mock/osgi/OsgiMetadataUtil.java | 161 ++++++++++++---------
3 files changed, 128 insertions(+), 68 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7e2acfa..30395c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,7 +78,38 @@
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
-
+
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <!-- Do not use version 0.9.10, it created threading issues when running junit tests in parallel (see SLING-5002) -->
+ <version>0.9.9</version>
+ <scope>compile</scope>
+ <!-- exclude all optional dependencies -->
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>dom4j</groupId>
+ <artifactId>dom4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
<!-- Artifact is shaded and inlined, only some classes included (see below) -->
<dependency>
<groupId>org.apache.sling</groupId>
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
index 97b9260..0e9114d 100644
--- a/src/main/java/org/apache/sling/testing/mock/osgi/NoScrMetadataException.java
+++ b/src/main/java/org/apache/sling/testing/mock/osgi/NoScrMetadataException.java
@@ -25,7 +25,7 @@ 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));
+ super("No OSGi SCR metadata found for class " + OsgiMetadataUtil.cleanupClassName(type.getName()));
}
}
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 3b6a10d..110f1fb 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
@@ -20,9 +20,7 @@ package org.apache.sling.testing.mock.osgi;
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;
@@ -30,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.regex.Pattern;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
@@ -37,6 +36,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
@@ -45,6 +45,8 @@ import org.apache.felix.framework.FilterImpl;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
+import org.reflections.Reflections;
+import org.reflections.scanners.ResourcesScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -57,7 +59,6 @@ import com.google.common.cache.CacheLoader;
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.
@@ -65,11 +66,16 @@ import com.google.common.collect.ImmutableList;
final class OsgiMetadataUtil {
private static final Logger log = LoggerFactory.getLogger(OsgiMetadataUtil.class);
+
+ private static final String METADATA_PATH = "OSGI-INF";
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY;
static {
DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
DOCUMENT_BUILDER_FACTORY.setNamespaceAware(true);
+
+ // suppress log entries from Reflections library
+ Reflections.log = null;
}
private static final XPathFactory XPATH_FACTORY = XPathFactory.newInstance();
@@ -80,30 +86,7 @@ final class OsgiMetadataUtil {
}
private static final OsgiMetadata NULL_METADATA = new OsgiMetadata();
-
- /*
- * The OSGI metadata XML files do not change during the unit test runs because static part of classpath.
- * So we can cache the parsing step if we need them multiple times.
- */
- private static final LoadingCache<Class, OsgiMetadata> METADATA_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<Class, OsgiMetadata>() {
- @Override
- public OsgiMetadata load(Class clazz) throws Exception {
- 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;
- }
- });
-
- private OsgiMetadataUtil() {
- // static methods only
- }
-
+
private static final NamespaceContext NAMESPACE_CONTEXT = new NamespaceContext() {
@Override
public String getNamespaceURI(String prefix) {
@@ -121,14 +104,26 @@ final class OsgiMetadataUtil {
}
};
- public static String getMetadataPath(Class clazz) {
- return "OSGI-INF/" + StringUtils.substringBefore(clazz.getName(), "$") + ".xml";
- }
+ /*
+ * The OSGI metadata XML files do not change during the unit test runs because static part of classpath.
+ * So we can cache the parsing step if we need them multiple times.
+ */
+ private static final Map<String,Document> METADATA_DOCUMENT_CACHE = initMetadataDocumentCache();
+ 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 = METADATA_DOCUMENT_CACHE.get(cleanupClassName(clazz.getName()));
+ if (metadataDocument != null) {
+ return new OsgiMetadata(clazz, metadataDocument);
+ }
+ return NULL_METADATA;
+ }
+ });
- public static String getOldMetadataMultiPath() {
- return "OSGI-INF/serviceComponents.xml";
+ private OsgiMetadataUtil() {
+ // static methods only
}
-
+
/**
* Try to read OSGI-metadata from /OSGI-INF and read all implemented interfaces and service properties.
* The metadata is cached after initial read, so it's no problem to call this method multiple time for the same class.
@@ -149,37 +144,68 @@ final class OsgiMetadataUtil {
throw new RuntimeException("Error loading OSGi metadata from loader cache.", ex);
}
}
+
+ /**
+ * Reads all SCR metadata XML documents located at OSGI-INF/ and caches them with quick access by implementation class.
+ * @return Cache map
+ */
+ private static Map<String,Document> initMetadataDocumentCache() {
+ Map<String,Document> cacheMap = new HashMap<>();
+
+ XPath xpath = XPATH_FACTORY.newXPath();
+ xpath.setNamespaceContext(NAMESPACE_CONTEXT);
+ XPathExpression xpathExpression;
+ try {
+ xpathExpression = xpath.compile("//*[implementation/@class]");
+ }
+ catch (XPathExpressionException ex) {
+ throw new RuntimeException("Compiling XPath expression failed.", ex);
+ }
- private static List<Document> getMetadataDocument(Class clazz) {
- String metadataPath = getMetadataPath(clazz);
- 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);
+ Reflections reflections = new Reflections(METADATA_PATH, new ResourcesScanner());
+ Set<String> paths = reflections.getResources(Pattern.compile("^.*\\.xml$"));
+ for (String path : paths) {
+ parseMetadataDocuments(cacheMap, path, xpathExpression);
+ }
+
+ return cacheMap;
+ }
+
+ private static void parseMetadataDocuments(Map<String,Document> cacheMap, String resourcePath, XPathExpression xpathExpression) {
+ try (InputStream fileStream = OsgiMetadataUtil.class.getClassLoader().getResourceAsStream(resourcePath)) {
+ if (fileStream != null) {
+ Document metadata = toXmlDocument(fileStream, resourcePath);
+ NodeList nodes = (NodeList)xpathExpression.evaluate(metadata, XPathConstants.NODESET);
+ if (nodes != null) {
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ String implementationClass = getImplementationClassName(node);
+ if (implementationClass != null) {
+ cacheMap.put(implementationClass, metadata);
+ }
+ }
+ }
}
}
- else {
- return ImmutableList.of(toXmlDocument(metadataStream, metadataPath));
+ catch (Throwable ex) {
+ log.warn("Error reading SCR metadata XML document from " + resourcePath, ex);
}
}
+
+ private static String getImplementationClassName(Node componentNode) {
+ NodeList childNodes = componentNode.getChildNodes();
+ for (int j = 0; j < childNodes.getLength(); j++) {
+ Node childNode = childNodes.item(j);
+ if (childNode.getNodeName().equals("implementation")) {
+ String implementationClass = getAttributeValue(childNode, "class");
+ if (!StringUtils.isBlank(implementationClass)) {
+ return implementationClass;
+ }
+ break;
+ }
+ }
+ return null;
+ }
private static Document toXmlDocument(InputStream inputStream, String path) {
try {
@@ -206,16 +232,19 @@ final class OsgiMetadataUtil {
* @return XPath query fragment to find matching XML node in SCR metadata
*/
private static String getComponentXPathQuery(Class clazz) {
- String className = StringUtils.substringBefore(clazz.getName(), "$$Enhancer");
+ String className = cleanupClassName(clazz.getName());
return "//*[implementation/@class='" + className + "' or @name='" + className + "']";
}
-
- private static boolean matchesService(Class clazz, Document metadata) {
- String query = getComponentXPathQuery(clazz);
- NodeList nodes = queryNodes(metadata, query);
- return nodes != null && nodes.getLength() > 0;
+
+ /**
+ * Remove extensions from class names added e.g. by mockito.
+ * @param className Class name
+ * @return Cleaned up class name
+ */
+ public static final String cleanupClassName(String className) {
+ return StringUtils.substringBefore(className, "$$Enhancer");
}
-
+
private static Set<String> getServiceInterfaces(Class clazz, Document metadata) {
Set<String> serviceInterfaces = new HashSet<String>();
String query = getComponentXPathQuery(clazz) + "/service/provide[@interface!='']";
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.