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

org.apache.sling.feature.analyser.Analyser 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.analyser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.ExecutionEnvironmentExtension;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.analyser.extensions.AnalyserMetaDataExtension;
import org.apache.sling.feature.analyser.task.AnalyserTask;
import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
import org.apache.sling.feature.builder.FeatureProvider;
import org.apache.sling.feature.scanner.BundleDescriptor;
import org.apache.sling.feature.scanner.FeatureDescriptor;
import org.apache.sling.feature.scanner.Scanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.sling.feature.analyser.task.AnalyzerTaskProvider.getTasks;
import static org.apache.sling.feature.analyser.task.AnalyzerTaskProvider.getTasksByClassName;
import static org.apache.sling.feature.analyser.task.AnalyzerTaskProvider.getTasksByIds;

public class Analyser {
    /**
     * Configurration key for configuration that applies to all tasks.
     */
    static final String ALL_TASKS_KEY = "all";

    private final AnalyserTask[] tasks;

    private final Scanner scanner;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Map> configurations;

    private boolean outputTaskDetails = true;

    /**
     * Create new analyser with a provided scanner and the tasks to run
     *
     * @param scanner The scanner
     * @param tasks   The tasks to run
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(final Scanner scanner, final AnalyserTask... tasks) throws IOException {
        this(scanner, Collections.emptyMap(), tasks);
    }

    /**
     * Create a new analyser with a provided scanner, tasks and configurations
     *
     * @param scanner        The scanner
     * @param configurations The configurations for the tasks
     * @param tasks          The tasks
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(
            final Scanner scanner, final Map> configurations, final AnalyserTask... tasks)
            throws IOException {
        this.tasks = tasks;
        this.configurations = configurations;
        this.scanner = scanner;
    }

    /**
     * Create a new analyser with the provided scanner and task class names
     *
     * @param scanner        The scanner
     * @param taskClassNames The task class names
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(final Scanner scanner, final String... taskClassNames) throws IOException {
        this(scanner, Collections.emptyMap(), taskClassNames);
    }

    /**
     * Create a new analyser with a provided scanner, task class names and
     * configurations
     *
     * @param scanner        The scanner
     * @param configurations The configurations for the tasks
     * @param taskClassNames The task class names
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(
            final Scanner scanner,
            final Map> configurations,
            final String... taskClassNames)
            throws IOException {
        this(scanner, configurations, getTasksByClassName(taskClassNames));
        if (this.tasks.length != taskClassNames.length) {
            throw new IOException("Couldn't find all tasks " + Arrays.toString(taskClassNames));
        }
    }

    /**
     * Create a new analyser with a provided scanner and includes/excludes for the
     * task ids
     *
     * @param scanner  The scanner
     * @param includes The includes for the task ids - can be {@code null}
     * @param excludes The excludes for the task ids - can be {@code null}
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(final Scanner scanner, final Set includes, final Set excludes) throws IOException {
        this(scanner, Collections.emptyMap(), includes, excludes);
    }

    /**
     * Create a new analyser with a provided scanner and includes/excludes for the
     * task ids and configuration
     *
     * @param scanner        The scanner
     * @param configurations The configurations for the tasks
     * @param includes       The includes for the task ids - can be {@code null}
     * @param excludes       The excludes for the task ids - can be {@code null}
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(
            final Scanner scanner,
            final Map> configurations,
            final Set includes,
            final Set excludes)
            throws IOException {
        this(scanner, configurations, getTasksByIds(includes, excludes));
    }

    /**
     * Create a new analyser with the provided scanner and use all available tasks
     *
     * @param scanner The scanner
     * @throws IOException If setting up the analyser fails
     */
    public Analyser(final Scanner scanner) throws IOException {
        this(scanner, getTasks());
    }

    /**
     * Enable/disable output of task details. By default the details are outputted.
     * @param outputTaskDetails flag for enabling/disabling output of task details
     * @since 1.6.0
     */
    public void setOutputTaskDetails(final boolean outputTaskDetails) {
        this.outputTaskDetails = outputTaskDetails;
    }

    /**
     * Analyse the feature
     *
     * @param feature The feature to analyse
     * @return The analyser result
     * @throws Exception If analysing fails
     */
    public AnalyserResult analyse(final Feature feature) throws Exception {
        return this.analyse(feature, null);
    }

    /**
     * Analyse the feature using the provided framework artifact
     *
     * @param feature The feature to analyse
     * @param fwk     The OSGi framework artifact
     * @return The analyser result
     * @throws Exception If analysing fails
     */
    public AnalyserResult analyse(final Feature feature, final ArtifactId fwk) throws Exception {
        return analyse(feature, fwk, null);
    }

    /**
     * Analyse the feature using the provided framework artifact
     *
     * @param feature         The feature to analyse
     * @param fwk             The OSGi framework artifact
     * @param featureProvider Optional provider to resolve features (if required)
     * @return The analyser result
     * @throws Exception If analysing fails
     */
    public AnalyserResult analyse(final Feature feature, final ArtifactId fwk, final FeatureProvider featureProvider)
            throws Exception {
        logger.info("Starting analyzing feature '{}'...", feature.getId());

        final long start = System.currentTimeMillis();
        final FeatureDescriptor featureDesc = scanner.scan(feature);
        BundleDescriptor bd = null;
        ArtifactId framework = fwk;
        if (framework == null) {
            final ExecutionEnvironmentExtension ext =
                    ExecutionEnvironmentExtension.getExecutionEnvironmentExtension(feature);
            if (ext != null && ext.getFramework() != null) {
                framework = ext.getFramework().getId();
            }
        }
        if (framework != null) {
            bd = scanner.scan(framework, feature.getFrameworkProperties());
        }
        if (this.outputTaskDetails) {
            logger.info("- Scanned feature in {}ms", System.currentTimeMillis() - start);
        }
        final BundleDescriptor fwkDesc = bd;

        final List globalWarnings = new ArrayList<>();
        final List artifactWarnings = new ArrayList<>();
        final List extensionWarnings = new ArrayList<>();
        final List configurationWarnings = new ArrayList<>();

        final List globalErrors = new ArrayList<>();
        final List artifactErrors = new ArrayList<>();
        final List extensionErrors = new ArrayList<>();
        final List configurationErrors = new ArrayList<>();

        AnalyserMetaDataExtension analyserMetaDataExtension =
                AnalyserMetaDataExtension.getAnalyserMetaDataExtension(feature);

        // execute analyser tasks
        for (final AnalyserTask task : tasks) {
            if (this.outputTaskDetails) {
                logger.info("- Executing {} [{}]...", task.getName(), task.getId());
            }
            final long startTask = System.currentTimeMillis();
            final Map taskConfiguration = getConfiguration(task.getId());

            final boolean strict = Boolean.valueOf(taskConfiguration.getOrDefault("strict", "false"));

            task.execute(new AnalyserTaskContext() {
                private final FeatureProvider cachingFeatureProvider = featureProvider != null
                        ? new FeatureProvider() {
                            private final ConcurrentHashMap cache = new ConcurrentHashMap<>();

                            @Override
                            public Feature provide(ArtifactId artifactId) {
                                return cache.computeIfAbsent(artifactId, key -> featureProvider.provide(artifactId));
                            }
                        }
                        : null;

                @Override
                public Feature getFeature() {
                    return feature;
                }

                @Override
                public FeatureDescriptor getFeatureDescriptor() {
                    return featureDesc;
                }

                @Override
                public FeatureProvider getFeatureProvider() {
                    return cachingFeatureProvider;
                }

                @Override
                public BundleDescriptor getFrameworkDescriptor() {
                    return fwkDesc;
                }

                @Override
                public Map getConfiguration() {
                    return taskConfiguration;
                }

                @Override
                public void reportWarning(final String message) {
                    if (strict) {
                        reportError(message);
                    }
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportWarning(feature.getId())) {
                        globalWarnings.add(new AnalyserResult.GlobalReport(message, task.getId()));
                    }
                }

                @Override
                public void reportArtifactWarning(ArtifactId artifactId, String message) {
                    if (strict) {
                        reportArtifactError(artifactId, message);
                    }
                    if (analyserMetaDataExtension == null
                            || (analyserMetaDataExtension.reportWarning(artifactId)
                                    && analyserMetaDataExtension.reportWarning(feature.getId()))) {
                        artifactWarnings.add(new AnalyserResult.ArtifactReport(artifactId, message, task.getId()));
                    }
                }

                @Override
                public void reportArtifactError(ArtifactId artifactId, String message) {
                    if (analyserMetaDataExtension == null
                            || (analyserMetaDataExtension.reportError(artifactId)
                                    && analyserMetaDataExtension.reportError(feature.getId()))) {
                        artifactErrors.add(new AnalyserResult.ArtifactReport(artifactId, message, task.getId()));
                    }
                }

                @Override
                public void reportExtensionWarning(String extension, String message) {
                    if (strict) {
                        reportExtensionError(extension, message);
                    }
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportWarning(feature.getId())) {
                        extensionWarnings.add(new AnalyserResult.ExtensionReport(extension, message, task.getId()));
                    }
                }

                @Override
                public void reportExtensionError(String extension, String message) {
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportError(feature.getId())) {
                        extensionErrors.add(new AnalyserResult.ExtensionReport(extension, message, task.getId()));
                    }
                }

                @Override
                public void reportError(final String message) {
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportError(feature.getId())) {
                        globalErrors.add(new AnalyserResult.GlobalReport(message, task.getId()));
                    }
                }

                @Override
                public void reportConfigurationError(Configuration cfg, String message) {
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportWarning(feature.getId())) {
                        configurationErrors.add(new AnalyserResult.ConfigurationReport(cfg, message, task.getId()));
                    }
                }

                @Override
                public void reportConfigurationWarning(Configuration cfg, String message) {
                    if (strict) {
                        reportConfigurationError(cfg, message);
                    }
                    if (analyserMetaDataExtension == null || analyserMetaDataExtension.reportWarning(feature.getId())) {
                        configurationWarnings.add(new AnalyserResult.ConfigurationReport(cfg, message, task.getId()));
                    }
                }
            });
            if (this.outputTaskDetails) {
                logger.info(
                        "- Executed {} [{}] in {}ms",
                        task.getName(),
                        task.getId(),
                        System.currentTimeMillis() - startTask);
            }
        }

        final int allWarnings = globalWarnings.size()
                + artifactWarnings.size()
                + extensionWarnings.size()
                + configurationWarnings.size();
        final int allErrors =
                globalErrors.size() + artifactErrors.size() + extensionErrors.size() + configurationErrors.size();
        logger.info(
                "Finished analyzing feature '{}' in {}ms : {} warnings, {} errors.",
                feature.getId(),
                System.currentTimeMillis() - start,
                allWarnings,
                allErrors);

        return new AnalyserResult() {
            @Override
            public List getGlobalWarnings() {
                return globalWarnings;
            }

            @Override
            public List getArtifactWarnings() {
                return artifactWarnings;
            }

            @Override
            public List getExtensionWarnings() {
                return extensionWarnings;
            }

            @Override
            public List getGlobalErrors() {
                return globalErrors;
            }

            @Override
            public List getArtifactErrors() {
                return artifactErrors;
            }

            @Override
            public List getExtensionErrors() {
                return extensionErrors;
            }

            @Override
            public FeatureDescriptor getFeatureDescriptor() {
                return featureDesc;
            }

            @Override
            public BundleDescriptor getFrameworkDescriptor() {
                return fwkDesc;
            }

            @Override
            public List getConfigurationErrors() {
                return configurationErrors;
            }

            @Override
            public List getConfigurationWarnings() {
                return configurationWarnings;
            }
        };
    }

    Map getConfiguration(final String id) {
        final Map result = new HashMap<>();

        Map globalCfg = this.configurations.get(ALL_TASKS_KEY);
        if (globalCfg != null) result.putAll(globalCfg);

        Map specificCfg = this.configurations.get(id);
        if (specificCfg != null) result.putAll(specificCfg);

        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy