org.apache.sling.feature.scanner.Scanner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.sling.feature.analyser Show documentation
Show all versions of org.apache.sling.feature.analyser Show documentation
A feature describes an OSGi system
The newest version!
/*
* 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.feature.scanner;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.sling.feature.analyzer.impl.felix.utils.manifest.Clause;
import org.apache.sling.feature.analyzer.impl.felix.utils.manifest.Parser;
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.Feature;
import org.apache.sling.feature.analyser.extensions.AnalyserMetaDataExtension;
import org.apache.sling.feature.analyser.extensions.AnalyserMetaDataExtension.SystemBundle;
import org.apache.sling.feature.builder.ArtifactProvider;
import org.apache.sling.feature.impl.felix.utils.resource.ResourceBuilder;
import org.apache.sling.feature.scanner.impl.BundleDescriptorImpl;
import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
import org.apache.sling.feature.scanner.impl.SystemBundleDescriptor;
import org.apache.sling.feature.scanner.spi.ExtensionScanner;
import org.apache.sling.feature.scanner.spi.FrameworkScanner;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.resource.Capability;
/**
* The scanner is a service that scans items and provides descriptions for
* these. The following items can be scanned individually
*
* - A bundle artifact
*
- A feature (requires {@link ExtensionScanner}s)
*
- A framework (requires {@link FrameworkScanner}s)
*
*
* 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 {
private final ArtifactProvider artifactProvider;
private final List extensionScanners;
private final List frameworkScanners;
/** The in memory cache for the scanned descriptors. */
private final Map cache = new ConcurrentHashMap<>();
/**
* Create a new scanner
*
* @param artifactProvider The artifact provider
* @param extensionScanners A list of extension scanners
* @param frameworkScanners A list of framework scanners
* @throws IOException If something goes wrong
*/
public Scanner(
final ArtifactProvider artifactProvider,
final List extensionScanners,
final List frameworkScanners)
throws IOException {
this.artifactProvider = artifactProvider;
this.extensionScanners = extensionScanners == null ? getServices(ExtensionScanner.class) : extensionScanners;
this.frameworkScanners = frameworkScanners == null ? getServices(FrameworkScanner.class) : frameworkScanners;
}
/**
* Create a new scanner and use the service loader to find the scanners
*
* @param artifactProvider The artifact provider
* @throws IOException If something goes wrong
*/
public Scanner(final ArtifactProvider artifactProvider) throws IOException {
this(artifactProvider, null, null);
}
/**
* Get services from the service loader
*
* @param clazz The service class
* @return The list of services might be empty.
*/
private static List getServices(final Class clazz) {
final ServiceLoader loader = ServiceLoader.load(clazz);
final List list = new ArrayList<>();
for (final T task : loader) {
list.add(task);
}
return list;
}
/**
* Scan a bundle
*
* @param bundle The bundle artifact
* @return The bundle descriptor
* @throws IOException If something goes wrong or the provided artifact is not a
* bundle.
*/
public BundleDescriptor scanBundle(final Artifact bundle) throws IOException {
return this.doScan(bundle, bundle.getStartOrder());
}
private BundleDescriptor doScan(final Artifact bundle, final int startLevel) throws IOException {
final String key = bundle.getId()
.toMvnId()
.concat(":")
.concat(String.valueOf(startLevel))
.concat(":")
.concat(Stream.of(bundle.getFeatureOrigins())
.map(ArtifactId::toMvnId)
.collect(Collectors.joining(",")));
BundleDescriptor desc = (BundleDescriptor) this.cache.get(key);
if (desc == null) {
final URL file = artifactProvider.provide(bundle.getId());
if (file == null) {
throw new IOException("Unable to find file for " + bundle.getId());
}
desc = new BundleDescriptorImpl(bundle, file);
this.cache.put(key, desc);
}
return desc;
}
/**
* Get all bundle descriptors
*
* @param bundles The bundles
* @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) throws IOException {
for (final Artifact bundle : bundles) {
final BundleDescriptor bundleDesc = scanBundle(bundle);
desc.getBundleDescriptors().add(bundleDesc);
}
}
/**
* 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 : f.getExtensions()) {
if (AnalyserMetaDataExtension.isAnalyserMetaDataExtension(ext)) {
continue;
}
ContainerDescriptor extDesc = null;
for (final ExtensionScanner scanner : this.extensionScanners) {
extDesc = scanner.scan(f, ext, this.artifactProvider);
if (extDesc != null) {
break;
}
}
if (extDesc != null) {
desc.getRequirements().addAll(extDesc.getRequirements());
desc.getCapabilities().addAll(extDesc.getCapabilities());
desc.getExportedPackages().addAll(extDesc.getExportedPackages());
desc.getImportedPackages().addAll(extDesc.getImportedPackages());
desc.getDynamicImportedPackages().addAll(extDesc.getDynamicImportedPackages());
desc.getArtifactDescriptors().addAll(extDesc.getArtifactDescriptors());
desc.getBundleDescriptors().addAll(extDesc.getBundleDescriptors());
}
}
}
/**
* 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
}
/**
* Scan a feature
*
* @param feature The feature
* @return The feature descriptor
* @throws IOException If something goes wrong or a scanner is missing
*/
public FeatureDescriptor scan(final Feature feature) throws IOException {
final String key = feature.getId().toMvnId();
FeatureDescriptorImpl desc = (FeatureDescriptorImpl) this.cache.get(key);
if (desc == null) {
desc = new FeatureDescriptorImpl(feature);
populateCache(feature);
getBundleInfos(feature.getBundles(), desc);
scanExtensions(feature, desc);
compact(desc);
desc.lock();
this.cache.put(key, desc);
}
return desc;
}
/**
* Populate the scanner cache from the feature extension (if available)
* @param feature The feature
* @throws IOException If extracting the data fails
*/
private void populateCache(Feature feature) throws IOException {
AnalyserMetaDataExtension extension = AnalyserMetaDataExtension.getAnalyserMetaDataExtension(feature);
if (extension != null) {
for (Artifact bundle : feature.getBundles()) {
ArtifactId id = bundle.getId();
final String key = id.toMvnId()
.concat(":")
.concat(String.valueOf(bundle.getStartOrder()))
.concat(":")
.concat(Stream.of(bundle.getFeatureOrigins())
.map(ArtifactId::toMvnId)
.collect(Collectors.joining(",")));
if (this.cache.get(key) == null) {
Map headers = extension.getManifest(id);
if (headers != null) {
Manifest manifest = new Manifest();
headers.forEach(manifest.getMainAttributes()::putValue);
BundleDescriptor desc = new BundleDescriptorImpl(bundle, artifactProvider, manifest);
this.cache.put(key, desc);
}
}
}
SystemBundle systemBundle = extension.getSystemBundle();
if (systemBundle != null) {
URL artifactUrl = artifactProvider.provide(systemBundle.getArtifactId());
if (artifactUrl == null) {
throw new IOException("Unable to find file for " + systemBundle.getArtifactId());
}
BundleDescriptor desc = new SystemBundleDescriptor(systemBundle.getArtifactId(), artifactUrl);
String capabilities = systemBundle.getManifest().get(Constants.PROVIDE_CAPABILITY);
if (capabilities != null) {
try {
List parsedCapabilities = ResourceBuilder.parseCapability(null, capabilities);
desc.getCapabilities().addAll(parsedCapabilities);
} catch (BundleException e) {
throw new IOException("Failed to parse capabilites for the system bundle", e);
}
}
String exports = systemBundle.getManifest().get(Constants.EXPORT_PACKAGE);
if (exports != null) {
Clause[] pcks = Parser.parseHeader(exports);
for (final Clause pck : pcks) {
String version = pck.getAttribute("version");
PackageInfo info = new PackageInfo(pck.getName(), version, false);
desc.getExportedPackages().add(info);
}
}
desc.lock();
String key = systemBundle.getScannerCacheKey();
this.cache.put(key, desc);
}
}
}
/**
* Scan a framework
*
* @param framework The framework
* @param props framework properties to launch the framework
* @return The framework descriptor
* @throws IOException If something goes wrong or a scanner is missing
*/
public BundleDescriptor scan(final ArtifactId framework, final Map props) throws IOException {
final String key = SystemBundleDescriptor.createCacheKey(framework, props);
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 desc;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy