blob: a00aa95781bdf3f3e4bdef7eac3183bdc75f6290 [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.maven.slingstart;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.json.stream.JsonGenerator;
import org.apache.felix.configurator.impl.json.JSMin;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.builder.FeatureProvider;
import org.apache.sling.feature.io.json.FeatureJSONReader;
import org.apache.sling.feature.io.json.FeatureJSONWriter;
import org.apache.sling.feature.modelconverter.FeatureToProvisioning;
import org.apache.sling.maven.slingstart.ModelPreprocessor.Environment;
import org.apache.sling.maven.slingstart.ModelPreprocessor.ProjectInfo;
import aQute.bnd.version.MavenVersion;
public class FeatureModelConverter {
static final String BUILD_DIR = "provisioning/converted";
public static Feature getFeature(ArtifactId id, MavenSession session, MavenProject project, ArtifactHandlerManager manager, ArtifactResolver resolver) {
try {
File file = ModelUtils.getArtifact(project, session, manager, resolver, id.getGroupId(), id.getArtifactId(), id.getVersion(), id.getType(), id.getClassifier()).getFile();
try (Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
return FeatureJSONReader.read(reader, file.toURI().toURL().toString());
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static void convert(Environment env) throws MavenExecutionException {
for (ProjectInfo pi : env.modelProjects.values()) {
convert(env, pi, pi.defaultProvisioningModelName);
}
}
public static List<File> getFeatureFiles(final File baseDir, final String config) {
final List<File> files = new ArrayList<>();
for (final String cfg : config.split(",")) {
final File featuresDir = new File(baseDir, cfg.trim().replace('/', File.separatorChar));
final File[] children = featuresDir.listFiles();
if (children != null) {
for (final File f : children) {
if (f.isFile() && f.getName().endsWith(".json")) {
files.add(f);
}
}
}
}
if (files.isEmpty()) {
return null;
}
return files;
}
public static void convert(Environment env, ProjectInfo info, String defaultProvName)
throws MavenExecutionException {
final String config = ModelPreprocessor.nodeValue(info.plugin, "featuresDirectory", "src/main/features");
final List<File> files = getFeatureFiles(info.project.getBasedir(), config);
if (files == null) {
return;
}
try {
convert(files, info.project, defaultProvName,
id -> getFeature(id, env.session, info.project, env.artifactHandlerManager, env.resolver));
} catch (RuntimeException ex) {
throw new MavenExecutionException(ex.getMessage(), ex);
}
}
static final String PROVISIONING_MODEL_NAME_VARIABLE = "provisioning.model.name";
static void convert(List<File> files, MavenProject project, String defaultProvName, FeatureProvider fp)
throws MavenExecutionException {
File processedFeaturesDir = new File(project.getBuild().getDirectory(), "features/processed");
processedFeaturesDir.mkdirs();
List<File> substedFiles = new ArrayList<>();
for (File f : files) {
File outFile = new File(processedFeaturesDir, f.getName());
if (!outFile.exists() || outFile.lastModified() <= f.lastModified()) {
try {
final String suggestedClassifier;
if (!f.getName().equals("feature.json")) {
final int lastDot = f.getName().lastIndexOf('.');
suggestedClassifier = f.getName().substring(0, lastDot);
} else {
suggestedClassifier = null;
}
String json = readFeatureFile(project, f, suggestedClassifier);
// check for prov model name
if (defaultProvName != null) {
try (final Reader reader = new StringReader(json)) {
final Feature feature = FeatureJSONReader.read(reader, f.getAbsolutePath());
if (feature.getVariables().get(PROVISIONING_MODEL_NAME_VARIABLE) == null) {
feature.getVariables().put(PROVISIONING_MODEL_NAME_VARIABLE, defaultProvName);
try (final Writer writer = new StringWriter()) {
FeatureJSONWriter.write(writer, feature);
writer.flush();
json = writer.toString();
}
}
}
}
try (final Writer fileWriter = new FileWriter(outFile)) {
fileWriter.write(json);
}
} catch (IOException e) {
throw new MavenExecutionException("Problem processing feature file " + f.getAbsolutePath(), e);
}
}
substedFiles.add(outFile);
}
File targetDir = new File(project.getBuild().getDirectory(), BUILD_DIR);
targetDir.mkdirs();
try {
for (File f : substedFiles) {
File genFile = new File(targetDir, f.getName() + ".txt");
FeatureToProvisioning.convert(f, genFile, fp, substedFiles.toArray(new File[] {}));
}
} catch (Exception e) {
throw new MavenExecutionException("Cannot convert feature files to provisioning model", e);
}
}
/**
* Read the json file, minify it, add id if missing and replace variables
*
* @param file The json file
* @return The read and minified JSON
*/
public static String readFeatureFile(final MavenProject project, final File file,
final String suggestedClassifier) {
final StringBuilder sb = new StringBuilder();
try (final Reader reader = new FileReader(file)) {
final char[] buf = new char[4096];
int l = 0;
while ((l = reader.read(buf)) > 0) {
sb.append(buf, 0, l);
}
} catch (final IOException io) {
throw new RuntimeException("Unable to read feature " + file.getAbsolutePath(), io);
}
final String readJson = sb.toString();
// minify JSON (remove comments)
String json;
try (final Writer out = new StringWriter(); final Reader in = new StringReader(readJson)) {
final JSMin min = new JSMin(in, out);
min.jsmin();
json = out.toString();
} catch (final IOException e) {
throw new RuntimeException("Unable to read feature file " + file.getAbsolutePath(), e);
}
// check if "id" is set
try (final JsonReader reader = Json.createReader(new StringReader(json))) {
final JsonObject obj = reader.readObject();
if (!obj.containsKey("id")) {
final StringBuilder isb = new StringBuilder();
isb.append(project.getGroupId());
isb.append(':');
isb.append(project.getArtifactId());
isb.append(':');
isb.append("slingosgifeature");
if (suggestedClassifier != null) {
isb.append(':');
isb.append(suggestedClassifier);
}
isb.append(':');
isb.append(project.getVersion());
final StringWriter writer = new StringWriter();
try (final JsonGenerator generator = Json.createGenerator(writer)) {
generator.writeStartObject();
generator.write("id", isb.toString());
for (final Map.Entry<String, JsonValue> entry : obj.entrySet()) {
generator.write(entry.getKey(), entry.getValue());
}
generator.writeEnd();
}
json = writer.toString();
}
}
// replace variables
return Substitution.replaceMavenVars(project, json);
}
/**
* Remove leading zeros for a version part
*/
private static String cleanVersionString(final String version) {
final StringBuilder sb = new StringBuilder();
boolean afterDot = false;
for(int i=0;i<version.length(); i++) {
final char c = version.charAt(i);
if ( c == '.' ) {
if (afterDot == true ) {
sb.append('0');
}
afterDot = true;
sb.append(c);
} else if ( afterDot && c == '0' ) {
// skip
} else if ( afterDot && c == '-' ) {
sb.append('0');
sb.append(c);
afterDot = false;
} else {
afterDot = false;
sb.append(c);
}
}
return sb.toString();
}
public static String getOSGiVersion(final String version) {
final DefaultArtifactVersion dav = new DefaultArtifactVersion(cleanVersionString(version));
final StringBuilder sb = new StringBuilder();
sb.append(dav.getMajorVersion());
sb.append('.');
sb.append(dav.getMinorVersion());
sb.append('.');
sb.append(dav.getIncrementalVersion());
if ( dav.getQualifier() != null ) {
sb.append('.');
sb.append(dav.getQualifier());
}
final MavenVersion mavenVersion = new MavenVersion(sb.toString());
return mavenVersion.getOSGiVersion().toString();
}
}