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

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

There is a newer version: 2.0.10
Show 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.TreeMap;
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.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.builder.ArtifactProvider;
import org.apache.sling.feature.scanner.impl.BundleDescriptorImpl;
import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
import org.apache.sling.feature.scanner.spi.ExtensionScanner;
import org.apache.sling.feature.scanner.spi.FrameworkScanner;

/**
 * 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, startLevel); 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, bundle.getStartOrder()); 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 StringBuilder sb = new StringBuilder(); sb.append(framework.toMvnId()); if (props != null) { final Map sortedMap = new TreeMap(props); for (final Map.Entry entry : sortedMap.entrySet()) { sb.append(":").append(entry.getKey()).append("=").append(entry.getValue()); } } 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 desc; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy