You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2019/06/07 11:10:14 UTC
[sling-org-apache-sling-feature-analyser] branch master updated:
SLING-8474 : Improve scanner by caching scanned results
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git
The following commit(s) were added to refs/heads/master by this push:
new 66f2eb9 SLING-8474 : Improve scanner by caching scanned results
66f2eb9 is described below
commit 66f2eb9a6d88eec8b5062f47477b5bd640ff6a53
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Jun 7 13:08:01 2019 +0200
SLING-8474 : Improve scanner by caching scanned results
---
.../sling/feature/scanner/ArtifactDescriptor.java | 11 +-
.../sling/feature/scanner/BundleDescriptor.java | 7 +-
.../sling/feature/scanner/ContainerDescriptor.java | 8 +-
.../apache/sling/feature/scanner/Descriptor.java | 13 ++-
.../sling/feature/scanner/FeatureDescriptor.java | 5 +
.../apache/sling/feature/scanner/PackageInfo.java | 11 +-
.../org/apache/sling/feature/scanner/Scanner.java | 118 ++++++++++++++-------
7 files changed, 125 insertions(+), 48 deletions(-)
diff --git a/src/main/java/org/apache/sling/feature/scanner/ArtifactDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/ArtifactDescriptor.java
index 85e149e..a9a75d6 100644
--- a/src/main/java/org/apache/sling/feature/scanner/ArtifactDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/ArtifactDescriptor.java
@@ -16,12 +16,17 @@
*/
package org.apache.sling.feature.scanner;
-import org.apache.sling.feature.Artifact;
-
import java.io.File;
+import org.apache.sling.feature.Artifact;
+
/**
- * Information about an artifact
+ * Information about an artifact.
+ *
+ * Note that this implementation is not synchronized. If multiple threads access
+ * a descriptor concurrently, and at least one of the threads modifies the
+ * descriptor structurally, it must be synchronized externally. However, once a
+ * descriptor is locked, it is safe to access it concurrently.
*/
public abstract class ArtifactDescriptor extends Descriptor {
protected ArtifactDescriptor(String name) {
diff --git a/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
index 006f158..caa59fb 100644
--- a/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/BundleDescriptor.java
@@ -21,7 +21,12 @@ import java.util.jar.Manifest;
import org.apache.sling.feature.scanner.impl.BundleDescriptorImpl;
/**
- * Information about a bundle
+ * Information about a bundle.
+ *
+ * Note that this implementation is not synchronized. If multiple threads access
+ * a descriptor concurrently, and at least one of the threads modifies the
+ * descriptor structurally, it must be synchronized externally. However, once a
+ * descriptor is locked, it is safe to access it concurrently.
*/
public abstract class BundleDescriptor extends ArtifactDescriptor implements Comparable<BundleDescriptor> {
protected BundleDescriptor(String name) {
diff --git a/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
index 33a17a7..7440117 100644
--- a/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
@@ -21,8 +21,12 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Information about a container (feature/application).
- * This is the aggregated information.
+ * Information about a container (feature). This is the aggregated information.
+ *
+ * Note that this implementation is not synchronized. If multiple threads access
+ * a descriptor concurrently, and at least one of the threads modifies the
+ * descriptor structurally, it must be synchronized externally. However, once a
+ * descriptor is locked, it is safe to access it concurrently.
*/
public abstract class ContainerDescriptor extends Descriptor {
diff --git a/src/main/java/org/apache/sling/feature/scanner/Descriptor.java b/src/main/java/org/apache/sling/feature/scanner/Descriptor.java
index 59dbb3d..3235e6b 100644
--- a/src/main/java/org/apache/sling/feature/scanner/Descriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/Descriptor.java
@@ -16,15 +16,20 @@
*/
package org.apache.sling.feature.scanner;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
/**
- * A descriptor holds information about requirements and capabilities
+ * A descriptor holds information about requirements and capabilities.
+ *
+ * Note that this implementation is not synchronized. If multiple threads access
+ * a descriptor concurrently, and at least one of the threads modifies the
+ * descriptor structurally, it must be synchronized externally. However, once a
+ * descriptor is locked, it is safe to access it concurrently.
*/
public abstract class Descriptor {
private final String name;
diff --git a/src/main/java/org/apache/sling/feature/scanner/FeatureDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/FeatureDescriptor.java
index 9fe1c9d..bc0332e 100644
--- a/src/main/java/org/apache/sling/feature/scanner/FeatureDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/FeatureDescriptor.java
@@ -20,6 +20,11 @@ import org.apache.sling.feature.Feature;
/**
* Information about a feature.
+ *
+ * Note that this implementation is not synchronized. If multiple threads access
+ * a descriptor concurrently, and at least one of the threads modifies the
+ * descriptor structurally, it must be synchronized externally. However, once a
+ * descriptor is locked, it is safe to access it concurrently.
*/
public abstract class FeatureDescriptor extends ContainerDescriptor {
private final Feature feature;
diff --git a/src/main/java/org/apache/sling/feature/scanner/PackageInfo.java b/src/main/java/org/apache/sling/feature/scanner/PackageInfo.java
index 9979aab..369d68f 100644
--- a/src/main/java/org/apache/sling/feature/scanner/PackageInfo.java
+++ b/src/main/java/org/apache/sling/feature/scanner/PackageInfo.java
@@ -17,11 +17,18 @@
package org.apache.sling.feature.scanner;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
+/**
+ * A package info object contains information about a package, its name, its
+ * version and the uses constraints.
+ *
+ * A package info object is immutable.
+ */
public class PackageInfo implements Comparable<PackageInfo> {
private final boolean optional;
@@ -30,14 +37,14 @@ public class PackageInfo implements Comparable<PackageInfo> {
private final Set<String> uses;
public PackageInfo(final String name, final String version, final boolean optional) {
- this(name, version, optional, Collections.emptySet());
+ this(name, version, optional, null);
}
public PackageInfo(final String name, final String version, final boolean optional, Set<String> uses) {
this.name = name;
this.version = version;
this.optional = optional;
- this.uses = uses;
+ this.uses = uses == null ? Collections.emptySet() : Collections.unmodifiableSet(new HashSet<>(uses));
}
public String getName() {
diff --git a/src/main/java/org/apache/sling/feature/scanner/Scanner.java b/src/main/java/org/apache/sling/feature/scanner/Scanner.java
index b28b7ca..a2d7920 100644
--- a/src/main/java/org/apache/sling/feature/scanner/Scanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/Scanner.java
@@ -16,11 +16,19 @@
*/
package org.apache.sling.feature.scanner;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Bundles;
import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Extensions;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.builder.ArtifactProvider;
import org.apache.sling.feature.scanner.impl.BundleDescriptorImpl;
@@ -28,22 +36,20 @@ import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
import org.apache.sling.feature.scanner.spi.ExtensionScanner;
import org.apache.sling.feature.scanner.spi.FrameworkScanner;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-
/**
- * The scanner is a service that scans items and provides descriptions for these.
- * The following items can be scanned individually
+ * The scanner is a service that scans items and provides descriptions for
+ * these. The following items can be scanned individually
* <ul>
- * <li>A bundle artifact
- * <li>An extension (requires {@link ExtensionScanner}s)
- * <li>A feature (requires {@link ExtensionScanner}s)
- * <li>A framework (requires {@link FrameworkScanner}s)
+ * <li>A bundle artifact
+ * <li>A feature (requires {@link ExtensionScanner}s)
+ * <li>A framework (requires {@link FrameworkScanner}s)
* </ul>
+ *
+ * The scanner uses an internal cache for the scanned results, subsequent scan
+ * calls with the same input will be directly served from the cache. The cache
+ * is an in memory cache and its lifetime is bound to the lifetime of the used
+ * scanner instance.
+ *
*/
public class Scanner {
@@ -53,6 +59,9 @@ public class Scanner {
private final List<FrameworkScanner> frameworkScanners;
+ /** The in memory cache for the scanned descriptors. */
+ private final Map<String, Object> cache = new ConcurrentHashMap<>();
+
/**
* Create a new scanner
*
@@ -105,18 +114,25 @@ public class Scanner {
* @throws IOException If something goes wrong or the provided artifact is not a bundle.
*/
public BundleDescriptor scan(final Artifact bundle, final int startLevel) throws IOException {
- final File file = artifactProvider.provide(bundle.getId());
- if ( file == null ) {
- throw new IOException("Unable to find file for " + bundle.getId());
- }
+ final String key = bundle.getId().toMvnId().concat(":").concat(String.valueOf(startLevel));
+ BundleDescriptor desc = (BundleDescriptor) this.cache.get(key);
+ if (desc == null) {
+ final File file = artifactProvider.provide(bundle.getId());
+ if (file == null) {
+ throw new IOException("Unable to find file for " + bundle.getId());
+ }
- return new BundleDescriptorImpl(bundle, file, startLevel);
+ desc = new BundleDescriptorImpl(bundle, file, startLevel);
+ this.cache.put(key, desc);
+ }
+ return desc;
}
/**
- * Get all bundle descriptors for a feature / application
+ * Get all bundle descriptors
+ *
* @param bundles The bundles
- * @param desc The descriptor
+ * @param desc The container descriptor
* @throws IOException If something goes wrong or no suitable scanner is found.
*/
private void getBundleInfos(final Bundles bundles, final ContainerDescriptor desc)
@@ -129,9 +145,16 @@ public class Scanner {
}
}
- private void scan(final Feature f, final Extensions extensions, final ContainerDescriptor desc)
+ /**
+ * Scan all extensions of a feature
+ *
+ * @param f The feature
+ * @param desc The container descriptor
+ * @throws IOException If something goes wrong or no suitable scanner is found.
+ */
+ private void scanExtensions(final Feature f, final ContainerDescriptor desc)
throws IOException {
- for(final Extension ext : extensions) {
+ for (final Extension ext : f.getExtensions()) {
ContainerDescriptor extDesc = null;
for(final ExtensionScanner scanner : this.extensionScanners) {
extDesc = scanner.scan(f, ext, this.artifactProvider);
@@ -153,6 +176,11 @@ public class Scanner {
}
}
+ /**
+ * Compact the container description
+ *
+ * @param desc The contaier description
+ */
private void compact(final ContainerDescriptor desc) {
// TBD remove all import packages / dynamic import packages which are resolved by this bundle set
// same with requirements
@@ -167,15 +195,21 @@ public class Scanner {
* @throws IOException If something goes wrong or a scanner is missing
*/
public FeatureDescriptor scan(final Feature feature) throws IOException {
- final FeatureDescriptorImpl desc = new FeatureDescriptorImpl(feature);
+ final String key = feature.getId().toMvnId();
+
+ FeatureDescriptorImpl desc = (FeatureDescriptorImpl) this.cache.get(key);
+ if (desc == null) {
+ desc = new FeatureDescriptorImpl(feature);
- getBundleInfos(feature.getBundles(), desc);
- scan(feature, feature.getExtensions(), desc);
+ getBundleInfos(feature.getBundles(), desc);
+ scanExtensions(feature, desc);
- compact(desc);
+ compact(desc);
- desc.lock();
+ desc.lock();
+ this.cache.put(key, desc);
+ }
return desc;
}
@@ -188,17 +222,29 @@ public class Scanner {
* @throws IOException If something goes wrong or a scanner is missing
*/
public BundleDescriptor scan(final ArtifactId framework, final Map<String,String> props) throws IOException {
- BundleDescriptor fwk = null;
- for(final FrameworkScanner scanner : this.frameworkScanners) {
- fwk = scanner.scan(framework, props, artifactProvider);
- if ( fwk != null ) {
- break;
+ final StringBuilder sb = new StringBuilder();
+ sb.append(framework.toMvnId());
+ if (props != null) {
+ final Map<String, String> sortedMap = new TreeMap<String, String>(props);
+ for (final Map.Entry<String, String> entry : sortedMap.entrySet()) {
+ sb.append(":").append(entry.getKey()).append("=").append(entry.getValue());
}
}
- if ( fwk == null ) {
- throw new IOException("No scanner found for framework " + framework.toMvnId());
+ final String key = sb.toString();
+ BundleDescriptor desc = (BundleDescriptor) this.cache.get(key);
+ if (desc == null) {
+ for (final FrameworkScanner scanner : this.frameworkScanners) {
+ desc = scanner.scan(framework, props, artifactProvider);
+ if (desc != null) {
+ break;
+ }
+ }
+ if (desc == null) {
+ throw new IOException("No scanner found for framework " + framework.toMvnId());
+ }
+ this.cache.put(key, desc);
}
- return fwk;
+ return desc;
}
}