blob: 1a68482f58b18451452af93536b2037be306f085 [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.applicationbuilder.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.List;
import java.util.stream.Stream;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.builder.BuilderContext;
import org.apache.sling.feature.builder.FeatureBuilder;
import org.apache.sling.feature.builder.FeatureProvider;
import org.apache.sling.feature.io.file.ArtifactHandler;
import org.apache.sling.feature.io.file.ArtifactManager;
import org.apache.sling.feature.io.file.ArtifactManagerConfig;
import org.apache.sling.feature.io.json.FeatureJSONReader;
import org.apache.sling.feature.io.json.FeatureJSONWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static Logger LOGGER;
private static String output;
private static String filesInput;
private static String dirsInput;
private static String repoUrls;
private static String propsFile;
private static File cacheDir;
/**
* 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) throws Exception {
final Option repoOption = Option.builder("u").hasArg().argName("Set repository url")
.desc("repository url").build();
final Option filesOption = new Option("f", true, "Set feature files (comma separated)");
final Option dirsOption = new Option("d", true, "Set feature file dirs (comma separated)");
final Option propsOption = new Option("p", true, "sling.properties file");
final Option outputOption = Option.builder("o").hasArg().argName("Set output file")
.desc("output file").build();
final Option cacheOption = new Option("c", true, "Set cache dir");
final Option debugOption = new Option("v", false, "Verbose");
debugOption.setArgs(0);
final Options options = new Options();
options.addOption(repoOption);
options.addOption(filesOption);
options.addOption(dirsOption);
options.addOption(outputOption);
options.addOption(propsOption);
options.addOption(cacheOption);
options.addOption(debugOption);
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(filesOption.getOpt()) ) {
filesInput = cl.getOptionValue(filesOption.getOpt());
}
if ( cl.hasOption(dirsOption.getOpt()) ) {
dirsInput = cl.getOptionValue(dirsOption.getOpt());
}
if ( cl.hasOption(outputOption.getOpt()) ) {
output = cl.getOptionValue(outputOption.getOpt());
}
if ( cl.hasOption(propsOption.getOpt()) ) {
propsFile = cl.getOptionValue(propsOption.getOpt());
}
if (cl.hasOption(cacheOption.getOpt())) {
cacheDir = new File(cl.getOptionValue(cacheOption.getOpt()));
}
if ( cl.hasOption(debugOption.getOpt()) ) {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");
}
} catch ( final ParseException pe) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("applicationbuilder", options);
throw new Exception("Unable to parse command line: {}", pe);
}
if ( filesInput == null && dirsInput == null) {
throw new Exception("Required argument missing: model files or directory");
}
}
private static ArtifactManager getArtifactManager() {
final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
if ( repoUrls != null ) {
amConfig.setRepositoryUrls(repoUrls.split(","));
}
if (cacheDir != null) {
amConfig.setCacheDirectory(cacheDir);
}
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");
Exception clException = null;
try {
parseArgs(args);
}
catch (Exception ex) {
clException = ex;
}
LOGGER = LoggerFactory.getLogger("applicationbuilder");
LOGGER.info("Apache Sling Feature Application Builder");
LOGGER.info("");
if (clException != null) {
LOGGER.error(clException.getMessage(), clException);
System.exit(1);
}
final ArtifactManager am = getArtifactManager();
final String[] files =
Stream.concat(
Stream.of(filesInput != null ? filesInput.split(",") : new String[0]),
Stream.of(dirsInput != null ? dirsInput.split(",") : new String[0])
.map(path -> new File(path))
.filter(File::isDirectory)
.flatMap(dir ->
Stream.of(dir.listFiles()))
.filter(file -> !file.getName().startsWith("."))
.map(File::getAbsolutePath))
.toArray(String[]::new);
if (files.length == 0) {
LOGGER.error("No feature files found.");
System.exit(1);
}
try {
writeApplication(buildApplication(assembleApplication(am, files)), output == null ? "application.json" : output);
} catch ( final IOException ioe) {
LOGGER.error("Unable to read feature/application files " + ioe.getMessage(), ioe);
System.exit(1);
} catch ( final Exception e) {
LOGGER.error("Problem generating application", e);
System.exit(1);
}
}
private static Feature assembleApplication(
final ArtifactManager artifactManager,
final String... featureFiles)
throws IOException {
if ( featureFiles == null || featureFiles.length == 0 ) {
throw new IOException("No features found.");
}
List<Feature> features = new ArrayList<>();
for (final String initFile : featureFiles)
{
try
{
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"), new BuilderContext(new FeatureProvider() {
@Override
public Feature provide(final ArtifactId 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 (final IOException e) {
// ignore
}
return null;
}
}), features.toArray(new Feature[0]));
return app;
}
private static Feature buildApplication(final Feature app) {
final org.apache.sling.feature.Artifact a = new org.apache.sling.feature.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);
// sling.properties (TODO)
if ( propsFile == null ) {
app.getFrameworkProperties().put("org.osgi.framework.bootdelegation", "sun.*,com.sun.*");
} else {
}
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);
}
}
return app;
}
private static void writeApplication(final Feature app, final String out) {
LOGGER.info("Writing application: " + out);
final File file = new File(out);
try ( final FileWriter writer = new FileWriter(file)) {
FeatureJSONWriter.write(writer, app);
} catch ( final IOException ioe) {
LOGGER.error("Unable to write application to {} : {}", out, ioe.getMessage(), ioe);
System.exit(1);
}
}
}