blob: af3386049e37cbecfd2186d6dac6310779ca2067 [file] [log] [blame]
/*
* 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.launcher.impl;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.StreamSupport;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.ExtensionType;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.FeatureConstants;
import org.apache.sling.feature.builder.BuilderContext;
import org.apache.sling.feature.builder.FeatureBuilder;
import org.apache.sling.feature.builder.MergeHandler;
import org.apache.sling.feature.builder.PostProcessHandler;
import org.apache.sling.feature.io.file.ArtifactHandler;
import org.apache.sling.feature.io.file.ArtifactManager;
import org.apache.sling.feature.io.json.FeatureJSONReader;
import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
import org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler;
public class FeatureProcessor {
/**
* Initialize the launcher
* Read the features and prepare the application
* @param config The current configuration
* @param artifactManager The artifact manager
* @return The merged feature representing the application
* @throws IOException when an IO exception occurs during application creation
*/
public static Feature createApplication(final LauncherConfig config,
final ArtifactManager artifactManager) throws IOException
{
final BuilderContext builderContext = new BuilderContext(id -> {
try {
final ArtifactHandler handler = artifactManager.getArtifactHandler(id.toMvnUrl());
try (final FileReader r = new FileReader(handler.getFile())) {
final Feature f = FeatureJSONReader.read(r, handler.getUrl());
return f;
}
} catch (IOException e) {
// ignore
return null;
}
});
builderContext.setArtifactProvider(id -> {
try {
final ArtifactHandler handler = artifactManager.getArtifactHandler(id.toMvnUrl());
return handler.getFile();
} catch (final IOException e) {
// ignore
return null;
}
});
builderContext.addVariablesOverwrites(config.getVariables());
builderContext.addFrameworkPropertiesOverwrites(config.getInstallation().getFrameworkProperties());
builderContext.addMergeExtensions(StreamSupport.stream(Spliterators.spliteratorUnknownSize(
ServiceLoader.load(MergeHandler.class).iterator(), Spliterator.ORDERED), false)
.toArray(MergeHandler[]::new));
builderContext.addPostProcessExtensions(StreamSupport.stream(Spliterators.spliteratorUnknownSize(
ServiceLoader.load(PostProcessHandler.class).iterator(), Spliterator.ORDERED), false)
.toArray(PostProcessHandler[]::new));
List<Feature> features = new ArrayList<>();
for (final String initFile : config.getFeatureFiles()) {
Main.LOG().debug("Reading feature file {}", initFile);
final ArtifactHandler featureArtifact = artifactManager.getArtifactHandler(initFile);
try (final FileReader r = new FileReader(featureArtifact.getFile())) {
final Feature f = FeatureJSONReader.read(r, featureArtifact.getUrl());
features.add(f);
} catch (Exception ex) {
throw new IOException("Error reading feature: " + initFile, ex);
}
}
Collections.sort(features);
// TODO make feature id configurable
final Feature app = FeatureBuilder.assemble(ArtifactId.fromMvnId("group:assembled:1.0.0"), builderContext, features.toArray(new Feature[0]));
final Artifact a = new Artifact(ArtifactId.parse("org.apache.sling/org.apache.sling.launchpad.api/1.2.0"));
a.getMetadata().put(org.apache.sling.feature.Artifact.KEY_START_ORDER, "1");
app.getBundles().add(a);
// TODO: this sucks
for (Artifact bundle : app.getBundles()) {
if ( bundle.getStartOrder() == 0) {
final int so = bundle.getMetadata().get("start-level") != null ? Integer.parseInt(bundle.getMetadata().get("start-level")) : 1;
bundle.setStartOrder(so);
}
}
FeatureBuilder.resolveVariables(app, config.getVariables());
return app;
}
/**
* Prepare the launcher
* - add all bundles to the bundle map of the installation object
* - add all other artifacts to the install directory (only if startup mode is INSTALL)
* - process configurations
* @param ctx The launcher prepare context
* @param config The launcher configuration
* @param app The merged feature to launch
* @throws Exception when something goes wrong
*/
public static void prepareLauncher(final LauncherPrepareContext ctx, final LauncherConfig config,
final Feature app) throws Exception {
for(final Map.Entry<Integer, List<Artifact>> entry : app.getBundles().getBundlesByStartOrder().entrySet()) {
for(final Artifact a : entry.getValue()) {
final File artifactFile = ctx.getArtifactFile(a.getId());
config.getInstallation().addBundle(entry.getKey(), artifactFile);
}
}
for (final Configuration cfg : app.getConfigurations()) {
if (Configuration.isFactoryConfiguration(cfg.getPid())) {
config.getInstallation().addConfiguration(Configuration.getName(cfg.getPid()),
Configuration.getFactoryPid(cfg.getPid()), cfg.getConfigurationProperties());
} else {
config.getInstallation().addConfiguration(cfg.getPid(), null, cfg.getConfigurationProperties());
}
}
for (final Map.Entry<String, String> prop : app.getFrameworkProperties().entrySet()) {
if ( !config.getInstallation().getFrameworkProperties().containsKey(prop.getKey()) ) {
config.getInstallation().getFrameworkProperties().put(prop.getKey(), prop.getValue());
}
}
extensions: for(final Extension ext : app.getExtensions()) {
for (ExtensionHandler handler : ServiceLoader.load(ExtensionHandler.class, FeatureProcessor.class.getClassLoader()))
{
if (handler.handle(ext, ctx, config.getInstallation())) {
continue extensions;
}
}
if ( ext.isRequired() ) {
throw new Exception("Unknown required extension " + ext.getName());
}
}
}
/**
* Prepare the cache
* - add all bundles
* - add all other artifacts (only if startup mode is INSTALL)
* @param artifactManager The artifact manager
* @param app the feature with the artifacts
* @return An Artifact to File mapping.
* @throws Exception when something goes wrong.
*/
public static Map<Artifact, File> calculateArtifacts(final ArtifactManager artifactManager,
final Feature app) throws Exception
{
Map<Artifact, File> result = new HashMap<>();
for (final Map.Entry<Integer, List<Artifact>> entry : app.getBundles().getBundlesByStartOrder().entrySet())
{
for (final Artifact a : entry.getValue())
{
final ArtifactHandler handler = artifactManager.getArtifactHandler(":" + a.getId().toMvnPath());
final File artifactFile = handler.getFile();
result.put(a, artifactFile);
}
}
for (final Extension ext : app.getExtensions())
{
if (ext.getType() == ExtensionType.ARTIFACTS && ext.getName().equals(FeatureConstants.EXTENSION_NAME_CONTENT_PACKAGES))
{
for (final Artifact a : ext.getArtifacts())
{
final ArtifactHandler handler = artifactManager.getArtifactHandler(":" + a.getId().toMvnPath());
result.put(a, handler.getFile());
}
}
}
return result;
}
}