You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2018/04/27 10:01:19 UTC
[sling-org-apache-sling-feature-modelconverter] 01/40: Move feature
model to whiteboard git
This is an automated email from the ASF dual-hosted git repository.
davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-modelconverter.git
commit 8ca0ccbf5a2efac4a09df847d86c3d4e4c55317a
Author: Carsten Ziegeler <cz...@adobe.com>
AuthorDate: Fri Nov 3 15:06:50 2017 +0100
Move feature model to whiteboard git
---
pom.xml | 127 ++++
.../sling/feature/modelconverter/impl/Main.java | 729 +++++++++++++++++++++
2 files changed, 856 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..64dbec0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+ <!--
+ 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.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>32</version>
+ <relativePath />
+ </parent>
+
+ <artifactId>org.apache.sling.feature.modelconverter</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+
+ <name>Apache Sling Feature Model Converter</name>
+ <description>
+ A feature describes an OSGi system
+ </description>
+
+ <properties>
+ <sling.java.version>8</sling.java.version>
+ </properties>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/support/feature-modelconverter</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/support/feature-modelconverter</developerConnection>
+ <url>http://svn.apache.org/viewvc/sling/trunk/tooling/support/feature-modelconverter</url>
+ </scm>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-dependencies</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>unpack-dependencies</goal>
+ </goals>
+ <configuration>
+ <excludes>META-INF/**</excludes>
+ <outputDirectory>${project.build.directory}/classes</outputDirectory>
+ <overWriteReleases>false</overWriteReleases>
+ <overWriteSnapshots>true</overWriteSnapshots>
+ <includeArtifactIds>commons-cli,org.apache.sling.feature,org.apache.sling.feature.support,org.apache.sling.commons.johnzon,org.apache.sling.provisioning.model,slf4j-api,slf4j-simple,osgi.core</includeArtifactIds>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.apache.sling.feature.modelconverter.impl.Main</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.feature</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.feature.support</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.provisioning.model</artifactId>
+ <version>1.8.2</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.johnzon</artifactId>
+ <version>1.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java b/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
new file mode 100644
index 0000000..f030367
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/modelconverter/impl/Main.java
@@ -0,0 +1,729 @@
+/*
+ * 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.modelconverter.impl;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.sling.feature.Application;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Bundles;
+import org.apache.sling.feature.Configurations;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.Extensions;
+import org.apache.sling.feature.KeyValueMap;
+import org.apache.sling.feature.support.ArtifactHandler;
+import org.apache.sling.feature.support.ArtifactManager;
+import org.apache.sling.feature.support.ArtifactManagerConfig;
+import org.apache.sling.feature.support.FeatureUtil;
+import org.apache.sling.feature.support.json.ApplicationJSONReader;
+import org.apache.sling.feature.support.json.ApplicationJSONWriter;
+import org.apache.sling.feature.support.json.FeatureJSONWriter;
+import org.apache.sling.provisioning.model.Artifact;
+import org.apache.sling.provisioning.model.ArtifactGroup;
+import org.apache.sling.provisioning.model.Configuration;
+import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.MergeUtility;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.ModelConstants;
+import org.apache.sling.provisioning.model.ModelUtility;
+import org.apache.sling.provisioning.model.ModelUtility.ResolverOptions;
+import org.apache.sling.provisioning.model.ModelUtility.VariableResolver;
+import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Section;
+import org.apache.sling.provisioning.model.Traceable;
+import org.apache.sling.provisioning.model.io.ModelReader;
+import org.apache.sling.provisioning.model.io.ModelWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Main {
+
+ private static Logger LOGGER;
+
+ private static String runModes;
+
+ private static String output;
+
+ private static String input;
+
+ private static boolean createApp = false;
+
+ private static boolean includeModelInfo = false;
+
+ private static String repoUrls;
+
+ private static String propsFile;
+
+ /**
+ * Parse the command line parameters and update a configuration object.
+ * @param args Command line parameters
+ * @return Configuration object.
+ */
+ private static void parseArgs(final String[] args) {
+ final Option repoOption = Option.builder("u").hasArg().argName("Set repository url")
+ .desc("repository url").required().build();
+
+ final Option modelOption = new Option("f", true, "Set feature files/directories");
+ final Option propsOption = new Option("p", true, "sling.properties file");
+ final Option runModeOption = new Option("r", true, "Set run modes (comma separated)");
+ final Option createAppOption = new Option("a", false, "If enabled, create application json");
+ createAppOption.setArgs(0);
+ final Option includeModelOption = new Option("i", false, "Include model filename as metadata for artifacts");
+ includeModelOption.setArgs(0);
+
+ final Option outputOption = Option.builder("o").hasArg().argName("Set output file")
+ .desc("output file").build();
+
+ final Options options = new Options();
+ options.addOption(repoOption);
+ options.addOption(modelOption);
+ options.addOption(createAppOption);
+ options.addOption(outputOption);
+ options.addOption(includeModelOption);
+ options.addOption(propsOption);
+ options.addOption(runModeOption);
+
+ final CommandLineParser parser = new DefaultParser();
+ try {
+ final CommandLine cl = parser.parse(options, args);
+
+ if ( cl.hasOption(repoOption.getOpt()) ) {
+ repoUrls = cl.getOptionValue(repoOption.getOpt());
+ }
+ if ( cl.hasOption(modelOption.getOpt()) ) {
+ input = cl.getOptionValue(modelOption.getOpt());
+ }
+ if ( cl.hasOption(createAppOption.getOpt()) ) {
+ createApp = true;
+ }
+ if ( cl.hasOption(includeModelOption.getOpt()) ) {
+ includeModelInfo = true;
+ }
+ if ( cl.hasOption(runModeOption.getOpt()) ) {
+ runModes = cl.getOptionValue(runModeOption.getOpt());
+ }
+ if ( cl.hasOption(outputOption.getOpt()) ) {
+ output = cl.getOptionValue(outputOption.getOpt());
+ }
+ if ( cl.hasOption(propsOption.getOpt()) ) {
+ propsFile = cl.getOptionValue(propsOption.getOpt());
+ }
+ } catch ( final ParseException pe) {
+ LOGGER.error("Unable to parse command line: {}", pe.getMessage(), pe);
+ System.exit(1);
+ }
+ if ( input == null ) {
+ LOGGER.error("Required argument missing: model file or directory");
+ System.exit(1);
+ }
+ }
+
+ private static ArtifactManager getArtifactManager() {
+ final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
+ if ( repoUrls != null ) {
+ amConfig.setRepositoryUrls(repoUrls.split(","));
+ }
+ try {
+ return ArtifactManager.getArtifactManager(amConfig);
+ } catch ( IOException ioe) {
+ LOGGER.error("Unable to create artifact manager " + ioe.getMessage(), ioe);
+ System.exit(1);
+ }
+ // we never reach this, but have to keep the compiler happy
+ return null;
+ }
+
+ public static void main(final String[] args) {
+ // setup logging
+ System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
+ System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
+ System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "true");
+ System.setProperty("org.slf4j.simpleLogger.showLogName", "false");
+
+ LOGGER = LoggerFactory.getLogger("modelconverter");
+
+ LOGGER.info("Apache Sling Provisiong Model to Feature Application Converter");
+ LOGGER.info("");
+
+ parseArgs(args);
+
+ final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
+ if ( repoUrls != null ) {
+ amConfig.setRepositoryUrls(repoUrls.split(","));
+ }
+ final ArtifactManager am = getArtifactManager();
+
+ final File f = new File(input);
+ final List<File> files = new ArrayList<>();
+ if ( f.isDirectory() ) {
+ for(final File file : f.listFiles()) {
+ if ( file.isFile() && !file.getName().startsWith(".") ) {
+ files.add(file);
+ }
+ }
+ if ( files.isEmpty() ) {
+ LOGGER.error("No files found in {}", f);
+ System.exit(1);
+ }
+ Collections.sort(files);
+ } else {
+ files.add(f);
+ }
+ boolean isJson = false;
+ boolean isTxt = false;
+ for(final File t : files) {
+ if ( t.getName().endsWith(".json") ) {
+ if ( isTxt ) {
+ LOGGER.error("Input files are a mixture of JSON and txt");
+ System.exit(1);
+ }
+ isJson = true;
+ } else {
+ if ( isJson ) {
+ LOGGER.error("Input files are a mixture of JSON and txt");
+ System.exit(1);
+ }
+ isTxt = true;
+ }
+ }
+
+ if ( isTxt ) {
+ if ( output == null ) {
+ output = createApp ? "application.json" : "feature.json";
+ }
+ final Model model = createModel(files, runModes);
+
+ if ( createApp ) {
+ final Application app = buildApplication(model);
+
+ writeApplication(app, output);
+ } else {
+ final List<org.apache.sling.feature.Feature> features = buildFeatures(model);
+ int index = 1;
+ for(final org.apache.sling.feature.Feature feature : features) {
+ writeFeature(feature, output, features.size() > 1 ? index : 0);
+ index++;
+ }
+ }
+ } else {
+ if ( output == null ) {
+ output = createApp ? "application.txt" : "feature.txt";
+ }
+ try {
+ if ( createApp ) {
+ // each file is an application
+ int index = 1;
+ for(final File appFile : files ) {
+ try ( final FileReader r = new FileReader(appFile) ) {
+ final Application app = ApplicationJSONReader.read(r);
+ convert(app, files.size() > 1 ? index : 0);
+ }
+ index++;
+ }
+ } else {
+ final Application app = FeatureUtil.assembleApplication(null, am, files.stream()
+ .map(File::getAbsolutePath)
+ .toArray(String[]::new));
+ convert(app, 0);
+ }
+ } catch ( final IOException ioe) {
+ LOGGER.error("Unable to read feature/application files " + ioe.getMessage(), ioe);
+ System.exit(1);
+ }
+ }
+ }
+
+ private static List<org.apache.sling.feature.Feature> buildFeatures(final Model model) {
+ final List<org.apache.sling.feature.Feature> features = new ArrayList<>();
+
+ for(final Feature feature : model.getFeatures() ) {
+ final String idString;
+ if ( feature.getName() != null ) {
+ if ( feature.getVersion() != null ) {
+ idString = "generated/" + feature.getName() + "/" + feature.getVersion();
+ } else {
+ idString = "generated/" + feature.getName() + "/1.0.0";
+ }
+ } else {
+ idString = "generated/feature/1.0.0";
+ }
+ final org.apache.sling.feature.Feature f = new org.apache.sling.feature.Feature(ArtifactId.fromMvnId(idString));
+ features.add(f);
+
+ buildFromFeature(feature, f.getBundles(), f.getConfigurations(), f.getExtensions(), f.getFrameworkProperties());
+ }
+
+ return features;
+ }
+
+ private static Application buildApplication(final Model model) {
+ final Application app = new Application();
+
+ for(final Feature feature : model.getFeatures() ) {
+ buildFromFeature(feature, app.getBundles(), app.getConfigurations(), app.getExtensions(), app.getFrameworkProperties());
+ }
+
+ // hard coded dependency to launchpad api
+ app.getBundles().add(1, new org.apache.sling.feature.Artifact(ArtifactId.fromMvnId("org.apache.sling/org.apache.sling.launchpad.api/1.2.0")));
+ // sling.properties (TODO)
+ if ( propsFile == null ) {
+ app.getFrameworkProperties().put("org.osgi.framework.bootdelegation", "sun.*,com.sun.*");
+ } else {
+
+ }
+ // felix framework hard coded for now
+ app.setFramework(FeatureUtil.getFelixFrameworkId(null));
+ return app;
+ }
+
+ private static void buildFromFeature(final Feature feature,
+ final Bundles bundles,
+ final Configurations configurations,
+ final Extensions extensions,
+ final KeyValueMap properties) {
+ Extension cpExtension = extensions.getByName(Extension.NAME_CONTENT_PACKAGES);
+ for(final RunMode runMode : feature.getRunModes() ) {
+ if ( !ModelConstants.FEATURE_LAUNCHPAD.equals(feature.getName()) ) {
+ for(final ArtifactGroup group : runMode.getArtifactGroups()) {
+ for(final Artifact artifact : group) {
+ final ArtifactId id = ArtifactId.fromMvnUrl(artifact.toMvnUrl());
+ final org.apache.sling.feature.Artifact newArtifact = new org.apache.sling.feature.Artifact(id);
+
+ for(final Map.Entry<String, String> entry : artifact.getMetadata().entrySet()) {
+ newArtifact.getMetadata().put(entry.getKey(), entry.getValue());
+ }
+
+ if ( newArtifact.getId().getType().equals("zip") ) {
+ if ( cpExtension == null ) {
+ cpExtension = new Extension(ExtensionType.ARTIFACTS, Extension.NAME_CONTENT_PACKAGES, true);
+ extensions.add(cpExtension);
+ }
+ cpExtension.getArtifacts().add(newArtifact);
+ } else {
+ int startLevel = group.getStartLevel();
+ if ( ModelConstants.FEATURE_BOOT.equals(feature.getName()) ) {
+ startLevel = 1;
+ } else if ( startLevel == 0 ) {
+ startLevel = 20;
+ }
+ bundles.add(startLevel, newArtifact);
+ }
+ }
+ }
+ }
+
+ for(final Configuration cfg : runMode.getConfigurations()) {
+ final org.apache.sling.feature.Configuration newCfg;
+ if ( cfg.getFactoryPid() != null ) {
+ newCfg = new org.apache.sling.feature.Configuration(cfg.getFactoryPid(), cfg.getPid());
+ } else {
+ newCfg = new org.apache.sling.feature.Configuration(cfg.getPid());
+ }
+ final Enumeration<String> keys = cfg.getProperties().keys();
+ while ( keys.hasMoreElements() ) {
+ final String key = keys.nextElement();
+ newCfg.getProperties().put(key, cfg.getProperties().get(key));
+ }
+ configurations.add(newCfg);
+ }
+
+ for(final Map.Entry<String, String> prop : runMode.getSettings()) {
+ properties.put(prop.getKey(), prop.getValue());
+ }
+ }
+ Extension repoExtension = extensions.getByName(Extension.NAME_REPOINIT);
+ for(final Section sect : feature.getAdditionalSections("repoinit")) {
+ final String text = sect.getContents();
+ if ( repoExtension == null ) {
+ repoExtension = new Extension(ExtensionType.TEXT, Extension.NAME_REPOINIT, true);
+ extensions.add(repoExtension);
+ repoExtension.setText(text);
+ } else {
+ repoExtension.setText(repoExtension.getText() + "\n\n" + text);
+ }
+ }
+ }
+
+ private static void writeApplication(final Application app, final String out) {
+ LOGGER.info("Writing application...");
+ final File file = new File(out);
+ try ( final FileWriter writer = new FileWriter(file)) {
+ ApplicationJSONWriter.write(writer, app);
+ } catch ( final IOException ioe) {
+ LOGGER.error("Unable to write application to {} : {}", out, ioe.getMessage(), ioe);
+ System.exit(1);
+ }
+ }
+
+ private static void writeFeature(final org.apache.sling.feature.Feature f, String out, final int index) {
+ LOGGER.info("Writing feature...");
+ if ( index > 0 ) {
+ final int lastDot = out.lastIndexOf('.');
+ if ( lastDot == -1 ) {
+ out = out + "_" + String.valueOf(index);
+ } else {
+ out = out.substring(0, lastDot) + "_" + String.valueOf(index) + out.substring(lastDot);
+ }
+ }
+ final File file = new File(out);
+ try ( final FileWriter writer = new FileWriter(file)) {
+ FeatureJSONWriter.write(writer, f);
+ } catch ( final IOException ioe) {
+ LOGGER.error("Unable to write feature to {} : {}", out, ioe.getMessage(), ioe);
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Read the models and prepare the model
+ * @param files The model files
+ */
+ private static Model createModel(final List<File> files,
+ final String runModes) {
+ LOGGER.info("Assembling model...");
+ Model model = null;
+ for(final File initFile : files) {
+ try {
+ model = processModel(model, initFile);
+ } catch ( final IOException iae) {
+ LOGGER.error("Unable to read provisioning model {} : {}", initFile, iae.getMessage(), iae);
+ System.exit(1);
+ }
+ }
+
+ final Model effectiveModel = ModelUtility.getEffectiveModel(model, new ResolverOptions().variableResolver(new VariableResolver() {
+
+ @Override
+ public String resolve(Feature feature, String name) {
+ if ( "sling.home".equals(name) ) {
+ return "${sling.home}";
+ }
+ return feature.getVariables().get(name);
+ }
+ }));
+ final Map<Traceable, String> errors = ModelUtility.validate(effectiveModel);
+ if ( errors != null ) {
+ LOGGER.error("Invalid assembled provisioning model.");
+ for(final Map.Entry<Traceable, String> entry : errors.entrySet()) {
+ LOGGER.error("- {} : {}", entry.getKey().getLocation(), entry.getValue());
+ }
+ System.exit(1);
+ }
+ final Set<String> modes = calculateRunModes(effectiveModel, runModes);
+
+ removeInactiveFeaturesAndRunModes(effectiveModel, modes);
+
+ return effectiveModel;
+ }
+
+ /**
+ * Process the given model and merge it into the provided model
+ * @param model The already read model
+ * @param modelFile The model file
+ * @return The merged model
+ * @throws IOException If reading fails
+ */
+ private static Model processModel(Model model,
+ final File modelFile) throws IOException {
+ LOGGER.info("- reading model {}", modelFile);
+
+ final Model nextModel = readProvisioningModel(modelFile);
+ // resolve references to other models
+ final ResolverOptions options = new ResolverOptions().variableResolver(new VariableResolver() {
+
+ @Override
+ public String resolve(final Feature feature, final String name) {
+ return name;
+ }
+ });
+
+
+ final Model effectiveModel = ModelUtility.getEffectiveModel(nextModel, options);
+ for(final Feature feature : effectiveModel.getFeatures()) {
+ for(final RunMode runMode : feature.getRunModes()) {
+ for(final ArtifactGroup group : runMode.getArtifactGroups()) {
+ final List<org.apache.sling.provisioning.model.Artifact> removeList = new ArrayList<>();
+ for(final org.apache.sling.provisioning.model.Artifact a : group) {
+ if ( "slingstart".equals(a.getType())
+ || "slingfeature".equals(a.getType())) {
+
+ final ArtifactManagerConfig cfg = new ArtifactManagerConfig();
+ final ArtifactManager mgr = ArtifactManager.getArtifactManager(cfg);
+
+ final ArtifactId correctedId = new ArtifactId(a.getGroupId(),
+ a.getArtifactId(),
+ a.getVersion(),
+ "slingstart".equals(a.getType()) ? "slingfeature" : a.getClassifier(),
+ "txt");
+
+ final ArtifactHandler handler = mgr.getArtifactHandler(correctedId.toMvnUrl());
+ model = processModel(model, handler.getFile());
+
+ removeList.add(a);
+ } else {
+ final org.apache.sling.provisioning.model.Artifact realArtifact = nextModel.getFeature(feature.getName()).getRunMode(runMode.getNames()).getArtifactGroup(group.getStartLevel()).search(a);
+
+ if ( includeModelInfo ) {
+ realArtifact.getMetadata().put("model-filename", modelFile.getName());
+ }
+ if ( runMode.getNames() != null ) {
+ realArtifact.getMetadata().put("runmodes", String.join(",", runMode.getNames()));
+ }
+ }
+ }
+ for(final org.apache.sling.provisioning.model.Artifact r : removeList) {
+ nextModel.getFeature(feature.getName()).getRunMode(runMode.getNames()).getArtifactGroup(group.getStartLevel()).remove(r);
+ }
+ }
+ }
+ }
+
+ if ( model == null ) {
+ model = nextModel;
+ } else {
+ MergeUtility.merge(model, nextModel);
+ }
+ return model;
+ }
+
+ /**
+ * Read the provisioning model
+ */
+ private static Model readProvisioningModel(final File file)
+ throws IOException {
+ try (final FileReader is = new FileReader(file)) {
+ final Model m = ModelReader.read(is, file.getAbsolutePath());
+ return m;
+ }
+ }
+
+ private static void removeInactiveFeaturesAndRunModes(final Model m,
+ final Set<String> activeRunModes) {
+ final String[] requiredFeatures = new String[] {ModelConstants.FEATURE_LAUNCHPAD, ModelConstants.FEATURE_BOOT};
+ // first pass:
+ // - remove special features except boot required ones
+ // - remove special run modes and inactive run modes
+ // - remove special configurations (TODO)
+ final Iterator<Feature> i = m.getFeatures().iterator();
+ while ( i.hasNext() ) {
+ final Feature feature = i.next();
+ if ( feature.isSpecial() ) {
+ boolean remove = true;
+ if ( requiredFeatures != null ) {
+ for(final String name : requiredFeatures) {
+ if ( feature.getName().equals(name) ) {
+ remove = false;
+ break;
+ }
+ }
+ }
+ if ( remove ) {
+ i.remove();
+ continue;
+ }
+ }
+ feature.setComment(null);
+ final Iterator<RunMode> rmI = feature.getRunModes().iterator();
+ while ( rmI.hasNext() ) {
+ final RunMode rm = rmI.next();
+ if ( rm.isActive(activeRunModes) || rm.isRunMode(ModelConstants.RUN_MODE_STANDALONE) ) {
+ final Iterator<Configuration> cI = rm.getConfigurations().iterator();
+ while ( cI.hasNext() ) {
+ final Configuration config = cI.next();
+ if ( config.isSpecial() ) {
+ cI.remove();
+ continue;
+ }
+ config.setComment(null);
+ }
+ } else {
+ rmI.remove();
+ continue;
+ }
+ }
+ }
+
+ // second pass: aggregate the settings and add them to the first required feature
+ final Feature requiredFeature = m.getFeature(requiredFeatures[0]);
+ if ( requiredFeature != null ) {
+ for(final Feature f : m.getFeatures()) {
+ if ( f.getName().equals(requiredFeature.getName()) ) {
+ continue;
+ }
+ copyAndClearSettings(requiredFeature, f.getRunMode(new String[] {ModelConstants.RUN_MODE_STANDALONE}));
+ copyAndClearSettings(requiredFeature, f.getRunMode());
+ }
+ }
+ }
+
+ private static void copyAndClearSettings(final Feature requiredFeature, final RunMode rm) {
+ if ( rm != null && !rm.getSettings().isEmpty() ) {
+ final RunMode requiredRunMode = requiredFeature.getOrCreateRunMode(null);
+ final Set<String> keys = new HashSet<>();
+ for(final Map.Entry<String, String> entry : rm.getSettings()) {
+ requiredRunMode.getSettings().put(entry.getKey(), entry.getValue());
+ keys.add(entry.getKey());
+ }
+
+ for(final String key : keys) {
+ rm.getSettings().remove(key);
+ }
+ }
+ }
+
+ private static Set<String> calculateRunModes(final Model model, final String runModes) {
+ final Set<String> modesSet = new HashSet<>();
+
+ // check configuration property first
+ if (runModes != null && runModes.trim().length() > 0) {
+ final String[] modes = runModes.split(",");
+ for(int i=0; i < modes.length; i++) {
+ modesSet.add(modes[i].trim());
+ }
+ }
+
+ // handle configured options
+ final Feature feature = model.getFeature(ModelConstants.FEATURE_BOOT);
+ if ( feature != null ) {
+ handleOptions(modesSet, feature.getRunMode().getSettings().get("sling.run.mode.options"));
+ handleOptions(modesSet, feature.getRunMode().getSettings().get("sling.run.mode.install.options"));
+ }
+
+ return modesSet;
+ }
+
+ private static void handleOptions(final Set<String> modesSet, final String propOptions) {
+ if ( propOptions != null && propOptions.trim().length() > 0 ) {
+
+ final String[] options = propOptions.trim().split("\\|");
+ for(final String opt : options) {
+ String selected = null;
+ final String[] modes = opt.trim().split(",");
+ for(int i=0; i<modes.length; i++) {
+ modes[i] = modes[i].trim();
+ if ( selected != null ) {
+ modesSet.remove(modes[i]);
+ } else {
+ if ( modesSet.contains(modes[i]) ) {
+ selected = modes[i];
+ }
+ }
+ }
+ if ( selected == null ) {
+ selected = modes[0];
+ modesSet.add(modes[0]);
+ }
+ }
+ }
+ }
+
+ private static void convert(final Application app, final int index) {
+ final Feature f = new Feature("application");
+
+ // bundles
+ for(final Map.Entry<Integer, org.apache.sling.feature.Artifact> bundle : app.getBundles()) {
+ final ArtifactId id = bundle.getValue().getId();
+ final Artifact newBundle = new Artifact(id.getGroupId(), id.getArtifactId(), id.getVersion(), id.getClassifier(), id.getType());
+ for(final Map.Entry<String, String> prop : bundle.getValue().getMetadata()) {
+ newBundle.getMetadata().put(prop.getKey(), prop.getValue());
+ }
+ f.getOrCreateRunMode(null).getOrCreateArtifactGroup(bundle.getKey()).add(newBundle);
+ }
+
+ // configurations
+ for(final org.apache.sling.feature.Configuration cfg : app.getConfigurations()) {
+ final Configuration c;
+ if ( cfg.isFactoryConfiguration() ) {
+ c = new Configuration(cfg.getName(), cfg.getFactoryPid());
+ } else {
+ c = new Configuration(cfg.getPid(), null);
+ }
+ final Enumeration<String> keys = cfg.getProperties().keys();
+ while ( keys.hasMoreElements() ) {
+ final String key = keys.nextElement();
+ c.getProperties().put(key, cfg.getProperties().get(key));
+ }
+ f.getOrCreateRunMode(null).getConfigurations().add(c);
+ }
+
+ // framework properties
+ for(final Map.Entry<String, String> prop : app.getFrameworkProperties()) {
+ f.getOrCreateRunMode(null).getSettings().put(prop.getKey(), prop.getValue());
+ }
+
+ // extensions: content packages and repoinit
+ for(final Extension ext : app.getExtensions()) {
+ if ( Extension.NAME_CONTENT_PACKAGES.equals(ext.getName()) ) {
+ for(final org.apache.sling.feature.Artifact cp : ext.getArtifacts() ) {
+ final ArtifactId id = cp.getId();
+ final Artifact newCP = new Artifact(id.getGroupId(), id.getArtifactId(), id.getVersion(), id.getClassifier(), id.getType());
+ for(final Map.Entry<String, String> prop : cp.getMetadata()) {
+ newCP.getMetadata().put(prop.getKey(), prop.getValue());
+ }
+ f.getOrCreateRunMode(null).getOrCreateArtifactGroup(0).add(newCP);
+ }
+
+ } else if ( Extension.NAME_REPOINIT.equals(ext.getName()) ) {
+ final Section section = new Section("repoinit");
+ section.setContents(ext.getText());
+ f.getAdditionalSections().add(section);
+ } else if ( ext.isRequired() ) {
+ LOGGER.error("Unable to convert required extension {}", ext.getName());
+ System.exit(1);
+ }
+ }
+
+ LOGGER.info("Writing feature...");
+ String out = output;
+ if ( index > 0 ) {
+ final int lastDot = out.lastIndexOf('.');
+ if ( lastDot == -1 ) {
+ out = out + "_" + String.valueOf(index);
+ } else {
+ out = out.substring(0, lastDot) + "_" + String.valueOf(index) + out.substring(lastDot);
+ }
+ }
+ final File file = new File(out);
+ final Model m = new Model();
+ m.getFeatures().add(f);
+ try ( final FileWriter writer = new FileWriter(file)) {
+ ModelWriter.write(writer, m);
+ } catch ( final IOException ioe) {
+ LOGGER.error("Unable to write feature to {} : {}", out, ioe.getMessage(), ioe);
+ System.exit(1);
+ }
+ }
+}
--
To stop receiving notification emails like this one, please contact
davidb@apache.org.