This is an automated email from the ASF dual-hosted git repository. andysch pushed a commit to branch feature/create-fm-descriptor in repository https://gitbox.apache.org/repos/asf/sling-slingfeature-maven-plugin.git
commit f0870054dbf72d926c9f9288dc2e68b99f511a48 Author: Andreas Schaefer <[email protected]> AuthorDate: Tue Dec 3 12:30:45 2019 -0800 Created new Goal to create an FM Descriptor from its POM and install it in the local Maven repo for other modules to pick up with includeArtifact --- README.md | 37 +++ pom.xml | 6 + .../create-fm-descriptor-simple/invoker.properties | 17 ++ src/it/create-fm-descriptor-simple/pom.xml | 67 +++++ src/it/create-fm-descriptor-simple/verify.bsh | 74 ++++++ .../maven/mojos/CreateFeatureDescriptorMojo.java | 144 ++++++++++ .../mojos/manager/DefaultFeaturesManager.java | 294 +++++++++++++++++++++ .../maven/mojos/manager/FeaturesManager.java | 42 +++ .../feature/maven/mojos/manager/RunmodeMapper.java | 75 ++++++ .../mojos/manager/SimpleVariablesInterpolator.java | 52 ++++ .../maven/mojos/manager/VariablesInterpolator.java | 25 ++ 11 files changed, 833 insertions(+) diff --git a/README.md b/README.md index 1bfb2d2..bd46cbd 100644 --- a/README.md +++ b/README.md @@ -423,6 +423,43 @@ Beside the Feature Files this Mojo for now supports all the parameters of the current Feature Launcher (1.0.7-SNAPSHOT). For more info see the FeautreLaucherMojoTest.testFullLaunch() test method. +## Create Feature Model Descriptor from POM (create-fm-descriptor) + +A simple Maven module creates its artifact but in order to another module +to use that in the context of a Feature Model build the module's Feature +Module descriptor must be created during the build. +This can be done here: +``` +<!-- Generate and Install the Sling OSGi Feature Model file --> +<plugin> + <groupId>org.apache.sling</groupId> + <artifactId>slingfeature-maven-plugin</artifactId> + <version>${slingfeature-maven-plugin.version}</version> + <extensions>true</extensions> + <executions> + <execution> + <id>create-fm-descriptor</id> + <phase>package</phase> + <goals> + <goal>create-fm-descriptor</goal> + </goals> + <configuration> + <classifier>jcr-packageinit</classifier> + </configuration> + </execution> + </executions> +</plugin> +``` +The property **classifier** will be added only to the Feature Model +Descriptor file name when it is installed in your local Maven repository. +If one is provided then the user of that descriptor needs to specify it +in the **includeArtifact**. +The Feature Model Descriptor file name in the local Maven repository has +this pattern: +``` +<group id>:<artifact id>-<version>=[-<classifier>].slingosgifeature +``` + ## Features Diff (features-diff) This MOJO compares different versions of the same Feature Model, producing the prototype diff --git a/pom.xml b/pom.xml index b3f13fe..ceefa86 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ <maven.version>3.5.0</maven.version> <maven.scm.version>1.11.1</maven.scm.version> <maven.site.path>${project.artifactId}-archives/${project.artifactId}-LATEST</maven.site.path> + <maven-artifact-transfer.version>0.11.0</maven-artifact-transfer.version> </properties> <scm> @@ -310,6 +311,11 @@ <version>3.6.0</version> </dependency> <dependency> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-artifact-transfer</artifactId> + <version>${maven-artifact-transfer.version}</version> + </dependency> + <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-exec</artifactId> <version>1.3</version> diff --git a/src/it/create-fm-descriptor-simple/invoker.properties b/src/it/create-fm-descriptor-simple/invoker.properties new file mode 100644 index 0000000..209b6ef --- /dev/null +++ b/src/it/create-fm-descriptor-simple/invoker.properties @@ -0,0 +1,17 @@ +# 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. + +invoker.goals = clean install +invoker.debug = true diff --git a/src/it/create-fm-descriptor-simple/pom.xml b/src/it/create-fm-descriptor-simple/pom.xml new file mode 100644 index 0000000..5516676 --- /dev/null +++ b/src/it/create-fm-descriptor-simple/pom.xml @@ -0,0 +1,67 @@ +<?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> + <groupId>org.apache.sling</groupId> + <artifactId>slingfeature-maven-plugin-test-create-fm-descriptor-simple</artifactId> + <packaging>jar</packaging> + <version>1.0.0-SNAPSHOT</version> + + <name>Apache Sling Features Maven plugin test - create fm descriptor simple</name> + + <dependencies> + <dependency> + <groupId>org.codehaus.janino</groupId> + <artifactId>janino</artifactId> + <version>2.7.5</version> + <exclusions> + <exclusion> + <groupId>org.apache.ant</groupId> + <artifactId>ant-nodeps</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.testing</artifactId> + <version>2.1.2</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>@project.groupId@</groupId> + <artifactId>@project.artifactId@</artifactId> + <version>@project.version@</version> + <extensions>true</extensions> + <executions> + <execution> + <id>analyze</id> + <phase>package</phase> + <goals> + <goal>create-fm-descriptor</goal> + </goals> + <configuration> + <classifier>test</classifier> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/src/it/create-fm-descriptor-simple/verify.bsh b/src/it/create-fm-descriptor-simple/verify.bsh new file mode 100644 index 0000000..c5d8872 --- /dev/null +++ b/src/it/create-fm-descriptor-simple/verify.bsh @@ -0,0 +1,74 @@ +/* + * 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. + */ +import java.io.*; +import java.util.*; +import org.codehaus.plexus.util.*; + + boolean check() { + // Check folder and file existence + + String group = "org.apache.sling"; + String groupPath = group.replaceAll("\\.", "/"); + String artifact = "slingfeature-maven-plugin-test-create-fm-descriptor-simple"; + String version = "1.0.0-SNAPSHOT"; + File localMavenRepositoryInstallationFolder = new File( + localRepositoryPath, groupPath + "/" + artifact + "/" + version + ); + if(!localMavenRepositoryInstallationFolder.exists()) { + System.out.println("Installation Folder does not exist: " + localMavenRepositoryInstallationFolder); + return false; + } + + String classifier = "test"; + String extension = "slingosgifeature"; + File fmDescriptorFile = new File( + localMavenRepositoryInstallationFolder, artifact + "-" + version + "-" + classifier + "." + extension + ); + if(!fmDescriptorFile.exists()) { + System.out.println("FM Descriptor file does not exist: " + fmDescriptorFile); + return false; + } + + // Check FM Descriptor Content + String fmContent = FileUtils.fileRead(fmDescriptorFile); + System.out.println("FM Descriptor File Content: " + fmContent); + String dependentGroup = "org.codehaus.janino"; + String dependentArtifact = "janino"; + String dependentVersion = "2.7.5"; + String[] values = { + "\"id\":\"" + group + ":" + artifact + ":slingosgifeature:" + version + "\"", + "\"bundles\":[", + "\"id\":\"" + group + ":" + artifact + ":" + version + "\"", + "\"id\":\"" + dependentGroup + ":" + dependentArtifact + ":" + dependentVersion + "\"", + }; + for (String value : values) { + if (fmContent.indexOf(value) < 0) { + System.out.println("Did not find line: " + value + " -> FAILED!"); + return false; + } + } + + return true; + } + try { + return check(); + } + catch(Throwable t) { + t.printStackTrace(); + return false; + } + return true; diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/CreateFeatureDescriptorMojo.java b/src/main/java/org/apache/sling/feature/maven/mojos/CreateFeatureDescriptorMojo.java new file mode 100644 index 0000000..21537af --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/CreateFeatureDescriptorMojo.java @@ -0,0 +1,144 @@ +/* + * 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.maven.mojos; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.model.Dependency; +import org.apache.maven.shared.transfer.artifact.install.ArtifactInstaller; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.shared.transfer.artifact.install.ArtifactInstallerException; +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.maven.mojos.manager.DefaultFeaturesManager; +import org.apache.sling.feature.maven.mojos.manager.FeaturesManager; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Create a Feature Model Descriptor (slingosigfeature) based on the + * project's POM + */ +@Mojo( + name = "create-fm-descriptor", + requiresProject = true, + threadSafe = true +) +public class CreateFeatureDescriptorMojo extends AbstractIncludingFeatureMojo { + + public static final String CFG_VERBOSE = "verbose"; + public static final String CFG_CLASSIFIER = "classifier"; + + public static final String CFG_FM_OUTPUT_DIRECTORY = "featureModelsOutputDirectory"; + public static final String DEFAULT_FM_OUTPUT_DIRECTORY = "${project.build.directory}/fm"; + + /** + * Target directory for the Feature Model file + */ + @Parameter(property = CFG_FM_OUTPUT_DIRECTORY, defaultValue = DEFAULT_FM_OUTPUT_DIRECTORY) + private File fmOutput; + + /** + * Optional Classifier for this Feature Model Descriptor file name + */ + @Parameter(property = CFG_CLASSIFIER, required = false, defaultValue = "") + private String classifier; + + /** + * Framework Properties for the Launcher + */ + @Parameter(property = CFG_VERBOSE, required = false, defaultValue = "false") + private boolean verbose; + + @Component + protected ArtifactInstaller installer; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + checkPreconditions(); + // Create a Feature Model Descriptor with its bundle in it + FeaturesManager featuresManager = new DefaultFeaturesManager( + true, 20, fmOutput, null, null, null + ); + featuresManager.init(project.getGroupId(), project.getArtifactId(), project.getVersion()); + ArtifactId bundle = new ArtifactId(project.getGroupId(), project.getArtifactId(), project.getVersion(), "", "bundle"); + featuresManager.addArtifact("", bundle); + List<Dependency> dependencies = project.getDependencies(); + for(Dependency dependency: dependencies) { + if("compile".equals(dependency.getScope())) { + featuresManager.addArtifact("", new ArtifactId( + dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), dependency.getClassifier(), dependency.getType() + )); + } + } + getLog().info("Project Features: " + featuresManager); + try { + featuresManager.serialize(); + // Now install the file as .slingosigfeature into the local Maven Repo + installFMDescriptor(bundle); + } catch(Exception e) { + getLog().error("Failure to serialize Feature Manager", e); + } + } + private void installFMDescriptor(ArtifactId artifact) { + Collection<Artifact> artifacts = Collections.synchronizedCollection(new ArrayList<>()); + // Source FM Descriptor File Path + String fmDescriptorFilePath = artifact.getArtifactId() + ".json"; + File fmDescriptorFile = new File(fmOutput, fmDescriptorFilePath); + if(fmDescriptorFile.exists() && fmDescriptorFile.canRead()) { + // Need to create a new Artifact Handler for the different extension and an Artifact to not + // change the module artifact + DefaultArtifactHandler fmArtifactHandler = new DefaultArtifactHandler("slingosgifeature"); + //AS TODO: For now the classifier is not set (check if we need to add the artifact id) + DefaultArtifact fmArtifact = new DefaultArtifact( + artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), + null, "slingosgifeature", classifier, fmArtifactHandler + ); + fmArtifact.setFile(fmDescriptorFile); + artifacts.add(fmArtifact); + try { + installArtifact(mavenSession.getProjectBuildingRequest(), artifacts); + } catch (MojoFailureException | MojoExecutionException e) { + getLog().error("Failed to install FM Descriptor", e); + } + } else { + getLog().error("Could not find FM Descriptor File: " + fmDescriptorFile); + } + } + + private void installArtifact(ProjectBuildingRequest pbr, Collection<Artifact> artifacts ) + throws MojoFailureException, MojoExecutionException + { + try + { + installer.install(pbr, artifacts); + } + catch ( ArtifactInstallerException e ) + { + throw new MojoExecutionException( "ArtifactInstallerException", e ); + } + } +} diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/manager/DefaultFeaturesManager.java b/src/main/java/org/apache/sling/feature/maven/mojos/manager/DefaultFeaturesManager.java new file mode 100644 index 0000000..f283aaa --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/manager/DefaultFeaturesManager.java @@ -0,0 +1,294 @@ +/* + * 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.maven.mojos.manager; + +import org.apache.sling.feature.Artifact; +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.Artifacts; +import org.apache.sling.feature.Configuration; +import org.apache.sling.feature.Extension; +import org.apache.sling.feature.ExtensionType; +import org.apache.sling.feature.Extensions; +import org.apache.sling.feature.Feature; +import org.apache.sling.feature.io.json.FeatureJSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static java.util.Objects.requireNonNull; + +public class DefaultFeaturesManager implements FeaturesManager { + + public static final String ZIP_TYPE = "zip"; + + private static final String JAVA_IO_TMPDIR_PROPERTY = "java.io.tmpdir"; + + private static final String CONTENT_PACKAGES = "content-packages"; + + private static final String SLING_OSGI_FEATURE_TILE_TYPE = "slingosgifeature"; + + private static final String JSON_FILE_EXTENSION = ".json"; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final Map<String, Feature> runModes = new HashMap<>(); + + private final VariablesInterpolator interpolator = new SimpleVariablesInterpolator(); + + private final boolean mergeConfigurations; + + private final int bundlesStartOrder; + + private final File featureModelsOutputDirectory; + + private final String artifactIdOverride; + + private final String prefix; + + private final Map<String, String> properties; + + private final List<String> targetAPIRegions = new ArrayList<>(); + + private Feature targetFeature = null; + + public DefaultFeaturesManager() { + this(true, 20, new File(System.getProperty(JAVA_IO_TMPDIR_PROPERTY)), null, null, null); + } + + public DefaultFeaturesManager(boolean mergeConfigurations, + int bundlesStartOrder, + File featureModelsOutputDirectory, + String artifactIdOverride, + String prefix, + Map<String, String> properties) { + this.mergeConfigurations = mergeConfigurations; + this.bundlesStartOrder = bundlesStartOrder; + this.featureModelsOutputDirectory = featureModelsOutputDirectory; + this.artifactIdOverride = artifactIdOverride; + this.prefix = prefix; + this.properties = properties; + } + + public void init(String groupId, String artifactId, String version) { + targetFeature = new Feature(new ArtifactId(groupId, artifactId, version, null, SLING_OSGI_FEATURE_TILE_TYPE)); + + initAPIRegions(targetFeature); + + runModes.clear(); + } + + private void initAPIRegions(Feature feature) { + if (targetAPIRegions.size() > 0) { + Extension apiRegions = new Extension(ExtensionType.JSON, "api-regions", false); + StringBuilder jsonBuilder = new StringBuilder("["); + for (String apiRegion : targetAPIRegions) { + if (jsonBuilder.length() > 1) { + jsonBuilder.append(','); + } + jsonBuilder.append("{\"name\":\""); + jsonBuilder.append(apiRegion); + jsonBuilder.append("\",\"exports\":[]}"); + } + jsonBuilder.append("]"); + apiRegions.setJSON(jsonBuilder.toString()); + feature.getExtensions().add(apiRegions); + } + } + + public Feature getTargetFeature() { + return targetFeature; + } + + public Feature getRunMode(String runMode) { + if (getTargetFeature() == null) { + throw new IllegalStateException("Target Feature not initialized yet, please make sure convert() method was invoked first."); + } + + if (runMode == null) { + return getTargetFeature(); + } + + ArtifactId newId = appendRunmode(getTargetFeature().getId(), runMode); + + return runModes.computeIfAbsent(runMode, k -> { + Feature f = new Feature(newId); + initAPIRegions(f); + return f; + }); + } + + public void addArtifact(String runMode, ArtifactId id) { + addArtifact(runMode, id, null); + } + + public void addArtifact(String runMode, ArtifactId id, Integer startOrder) { + requireNonNull(id, "Artifact can not be attached to a feature without specifying a valid ArtifactId."); + + Artifact artifact = new Artifact(id); + + Feature targetFeature = getRunMode(runMode); + Artifacts artifacts; + + if (ZIP_TYPE.equals(id.getType()) ) { + Extensions extensions = targetFeature.getExtensions(); + Extension extension = extensions.getByName(CONTENT_PACKAGES); + + if (extension == null) { + extension = new Extension(ExtensionType.ARTIFACTS, CONTENT_PACKAGES, true); + extensions.add(extension); + } + + artifacts = extension.getArtifacts(); + } else { + int startOrderForBundle = startOrder != null ? startOrder.intValue() : bundlesStartOrder; + artifact.setStartOrder(startOrderForBundle); + artifacts = targetFeature.getBundles(); + } + + artifacts.add(artifact); + } + + private ArtifactId appendRunmode(ArtifactId id, String runMode) { + ArtifactId newId; + if (runMode == null) { + newId = id; + } else { + final String classifier; + if (id.getClassifier() != null && !id.getClassifier().isEmpty()) { + classifier = id.getClassifier() + '-' + runMode; + } else { + classifier = runMode; + } + + newId = new ArtifactId(id.getGroupId(), id.getArtifactId(), id.getVersion(), classifier, id.getType()); + } + return newId; + } + + public void addConfiguration(String runMode, String pid, Dictionary<String, Object> configurationProperties) { + Feature feature = getRunMode(runMode); + Configuration configuration = feature.getConfigurations().getConfiguration(pid); + + if (configuration == null) { + configuration = new Configuration(pid); + feature.getConfigurations().add(configuration); + } else if (!mergeConfigurations) { + throw new IllegalStateException("Configuration '" + + pid + + "' already defined in Feature Model '" + + feature.getId().toMvnId() + + "', set the 'mergeConfigurations' flag to 'true' if you want to merge multiple configurations with same PID"); + } + + Enumeration<String> keys = configurationProperties.keys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = configurationProperties.get(key); + + if (value != null && Collection.class.isInstance(value)) { + value = ((Collection<?>) value).toArray(); + } + + configuration.getProperties().put(key, value); + } + } + + public void serialize() throws Exception { + RunmodeMapper runmodeMapper = RunmodeMapper.open(featureModelsOutputDirectory); + + serialize(targetFeature, null, runmodeMapper); + + if (!runModes.isEmpty()) { + for (Entry<String, Feature> runmodeEntry : runModes.entrySet()) { + String runmode = runmodeEntry.getKey(); + serialize(runmodeEntry.getValue(), runmode, runmodeMapper); + } + } + + runmodeMapper.save(); + } + + private void serialize(Feature feature, String runMode, RunmodeMapper runmodeMapper) throws Exception { + StringBuilder fileNameBuilder = new StringBuilder() + .append((prefix != null) ? prefix : "") + .append(feature.getId().getArtifactId()); + + String classifier = feature.getId().getClassifier(); + if (classifier != null && !classifier.isEmpty()) { + fileNameBuilder.append('-').append(classifier); + } + + if (properties != null) { + properties.put("filename", fileNameBuilder.toString()); + } + + fileNameBuilder.append(JSON_FILE_EXTENSION); + + String fileName = fileNameBuilder.toString(); + + File targetFile = new File(featureModelsOutputDirectory, fileName); + if (!targetFile.getParentFile().exists()) { + targetFile.getParentFile().mkdirs(); + } + + if (artifactIdOverride != null && !artifactIdOverride.isEmpty()) { + String interpolatedIdOverride = interpolator.interpolate(artifactIdOverride, properties); + ArtifactId idOverrride = appendRunmode(ArtifactId.parse(interpolatedIdOverride), runMode); + feature = feature.copy(idOverrride); + } + + logger.info("Writing resulting Feature Model '{}' to file '{}'...", feature.getId(), targetFile); + + try (FileWriter targetWriter = new FileWriter(targetFile)) { + FeatureJSONWriter.write(targetWriter, feature); + + logger.info("'{}' Feature File successfully written!", targetFile); + + runmodeMapper.addOrUpdate(runMode, fileName); + } + } + + public synchronized DefaultFeaturesManager setAPIRegions(List<String> regions) { + targetAPIRegions.clear(); + targetAPIRegions.addAll(regions); + return this; + } + + @Override + public void addOrAppendRepoInitExtension(String text) { + + Extension repoInitExtension = getTargetFeature().getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); + + if (repoInitExtension == null) { + repoInitExtension = new Extension(ExtensionType.TEXT, Extension.EXTENSION_NAME_REPOINIT, true); + getTargetFeature().getExtensions().add(repoInitExtension); + repoInitExtension.setText(text); + } else { + repoInitExtension.setText(repoInitExtension.getText() + "\n " + text); + } + } +} diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/manager/FeaturesManager.java b/src/main/java/org/apache/sling/feature/maven/mojos/manager/FeaturesManager.java new file mode 100644 index 0000000..ac7d1a2 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/manager/FeaturesManager.java @@ -0,0 +1,42 @@ +/* + * 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.maven.mojos.manager; + +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.Feature; + +import java.util.Dictionary; + +public interface FeaturesManager { + + void init(String groupId, String artifactId, String version); + + Feature getTargetFeature(); + + Feature getRunMode(String runMode); + + void addArtifact(String runMode, ArtifactId id); + + void addArtifact(String runMode, ArtifactId id, Integer startOrder); + + void addConfiguration(String runMode, String pid, Dictionary<String, Object> configurationProperties); + + void serialize() throws Exception; + + void addOrAppendRepoInitExtension(String text); + +} diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/manager/RunmodeMapper.java b/src/main/java/org/apache/sling/feature/maven/mojos/manager/RunmodeMapper.java new file mode 100644 index 0000000..eb161e3 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/manager/RunmodeMapper.java @@ -0,0 +1,75 @@ +/* + * 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.maven.mojos.manager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +final class RunmodeMapper { + + private static final String FILENAME = "runmode.mapping"; + + public static RunmodeMapper open(File featureModelsOutputDirectory) throws IOException { + Properties properties = new Properties(); + + File runmodeMappingFile = new File(featureModelsOutputDirectory, FILENAME); + if (runmodeMappingFile.exists()) { + try (FileInputStream input = new FileInputStream(runmodeMappingFile)) { + properties.load(input); + } + } + + return new RunmodeMapper(runmodeMappingFile, properties); + } + + private static final String DEFAULT = "(default)"; + + private final File runmodeMappingFile; + + private final Properties properties; + + private RunmodeMapper(File runmodeMappingFile, Properties properties) { + this.runmodeMappingFile = runmodeMappingFile; + this.properties = properties; + } + + public void addOrUpdate(String runMode, String jsonFileName) { + if (runMode == null) { + runMode = DEFAULT; + } + + String value = properties.getProperty(runMode); + + if (value != null && !value.contains(jsonFileName)) { + value += ',' + jsonFileName; + } else { + value = jsonFileName; + } + + properties.setProperty(runMode, value); + } + + public void save() throws IOException { + try (FileOutputStream output = new FileOutputStream(runmodeMappingFile)) { + properties.store(output, "File edited by the Apache Sling Content Package to Sling Feature converter"); + } + } + +} diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/manager/SimpleVariablesInterpolator.java b/src/main/java/org/apache/sling/feature/maven/mojos/manager/SimpleVariablesInterpolator.java new file mode 100644 index 0000000..a9acc56 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/manager/SimpleVariablesInterpolator.java @@ -0,0 +1,52 @@ +/* + * 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.maven.mojos.manager; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +public final class SimpleVariablesInterpolator implements VariablesInterpolator { + + private final Pattern replacementPattern = Pattern.compile("\\$\\{\\{(.+?)\\}\\}"); + + public String interpolate(String format, Map<String, String> properties) { + requireNonNull(format, "Input string format must be not null"); + + if (properties == null || properties.isEmpty()) { + return format; + } + + Matcher matcher = replacementPattern.matcher(format); + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + String variable = matcher.group(1); + String resolved = properties.get(variable); + + if (resolved == null) { + resolved = String.format("\\$\\{\\{%s\\}\\}", variable); + } + + matcher.appendReplacement(result, resolved); + } + matcher.appendTail(result); + return result.toString(); + } + +} diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/manager/VariablesInterpolator.java b/src/main/java/org/apache/sling/feature/maven/mojos/manager/VariablesInterpolator.java new file mode 100644 index 0000000..0759967 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/maven/mojos/manager/VariablesInterpolator.java @@ -0,0 +1,25 @@ +/* + * 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.maven.mojos.manager; + +import java.util.Map; + +public interface VariablesInterpolator { + + String interpolate(String format, Map<String, String> properties); + +}
