All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sling.feature.scanner.Scanner Maven / Gradle / Ivy

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