blob: 4605275eb738f4cc6fb7ac53593f2663db0abe8a [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.projectsupport;
import static org.apache.sling.maven.projectsupport.BundleListUtils.interpolateProperties;
import static org.apache.sling.maven.projectsupport.BundleListUtils.readBundleList;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.shared.filtering.MavenFileFilter;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.filtering.PropertyUtils;
import org.apache.sling.maven.projectsupport.BundleListUtils.ArtifactDefinitionsCallback;
import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
public abstract class AbstractUsingBundleListMojo extends AbstractBundleListMojo {
/**
* JAR Packaging type.
*/
protected static final String JAR = "jar";
/**
* WAR Packaging type.
*/
protected static final String WAR = "war";
protected static final String CONFIG_PATH_PREFIX = "resources/config";
protected static final String BUNDLE_PATH_PREFIX = "resources/bundles";
protected static boolean shouldCopy(File source, File dest) {
if (!dest.exists()) {
return true;
}
return source.lastModified() > dest.lastModified();
}
/**
* The definition of the defaultBundleList artifact.
*
* @parameter
*/
protected ArtifactDefinition defaultBundleList;
/**
* Any additional bundles to include in the project's bundles directory.
*
* @parameter
*/
private ArtifactDefinition[] additionalBundles;
private BundleList initializedBundleList;
/**
* Bundles which should be removed from the project's bundles directory.
*
* @parameter
*/
private ArtifactDefinition[] bundleExclusions;
/**
* If true, include the default bundles.
*
* @parameter expression="${includeDefaultBundles}" default-value="true"
*/
private boolean includeDefaultBundles;
/**
* @parameter
*/
private File[] rewriteRuleFiles;
/**
* The list of tokens to include when copying configs
* from partial bundle lists.
*
* @parameter default-value="**"
*/
private String[] configIncludes;
/**
* The list of tokens to exclude when copying the configs
* from partial bundle lists.
*
* @parameter
*/
private String[] configExcludes;
/**
* The list of names to exclude when copying properties
* from partial bundle lists.
*
* @parameter
*/
private String[] propertiesExcludes;
/**
* @component
*/
protected MavenFileFilter mavenFileFilter;
/**
* The zip unarchiver.
*
* @component role="org.codehaus.plexus.archiver.UnArchiver" roleHint="zip"
*/
private ZipUnArchiver zipUnarchiver;
private Properties slingProperties;
private Properties slingWebappProperties;
private Properties slingStandaloneProperties;
private String slingBootstrapCommand;
private String slingWebappBootstrapCommand;
private String slingStandaloneBootstrapCommand;
/**
* @parameter default-value="${project.build.directory}/tmpBundleListconfig"
*/
private File tmpOutputDir;
/**
* @parameter default-value="${project.build.directory}/tmpConfigDir"
*/
private File tempConfigDir;
private File overlayConfigDir;
public final void execute() throws MojoFailureException, MojoExecutionException {
try {
initBundleList();
extractConfigurations();
} catch (MojoExecutionException e) {
throw e;
} catch (Exception e) {
throw new MojoExecutionException("Unable to load dependency information from properties file.", e);
}
executeWithArtifacts();
}
@Override
protected File getConfigDirectory() {
if ( this.overlayConfigDir != null ) {
return this.overlayConfigDir;
}
return super.getConfigDirectory();
}
/**
* Execute the logic of the plugin after the default artifacts have been
* initialized.
*/
protected abstract void executeWithArtifacts() throws MojoExecutionException, MojoFailureException;
protected BundleList getInitializedBundleList() {
return initializedBundleList;
}
/**
* Hook methods for subclasses to initialize any additional artifact
* definitions.
*
* @param dependencies the dependency properties loaded from the JAR file
*/
protected void initArtifactDefinitions(Properties dependencies) {
}
/**
* Hook methods for subclasses to initialize the bundle list.
*/
protected void initBundleList(BundleList bundleList) {
}
/**
* Initialize the artifact definitions using defaults inside the plugin JAR.
*
* @throws IOException if the default properties can't be read
* @throws XmlPullParserException
* @throws MojoExecutionException
*/
private final void initArtifactDefinitions() throws IOException {
BundleListUtils.initArtifactDefinitions(getClass().getClassLoader(), new ArtifactDefinitionsCallback() {
public void initArtifactDefinitions(Properties dependencies) {
if (defaultBundleList == null) {
defaultBundleList = new ArtifactDefinition();
}
defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList"));
AbstractUsingBundleListMojo.this.initArtifactDefinitions(dependencies);
}
});
}
private final void initBundleList() throws IOException, XmlPullParserException, MojoExecutionException {
initArtifactDefinitions();
if (BundleListUtils.isCurrentArtifact(project, defaultBundleList)) {
initializedBundleList = readBundleList(bundleListFile);
} else {
initializedBundleList = new BundleList();
if (includeDefaultBundles) {
Artifact defBndListArtifact = getArtifact(defaultBundleList.getGroupId(),
defaultBundleList.getArtifactId(), defaultBundleList.getVersion(), defaultBundleList.getType(),
defaultBundleList.getClassifier());
getLog().info("Using bundle list file from " + defBndListArtifact.getFile().getAbsolutePath());
initializedBundleList = readBundleList(defBndListArtifact.getFile());
}
if (bundleListFile.exists()) {
initializedBundleList.merge(readBundleList(bundleListFile));
}
}
// add additional bundles
if (additionalBundles != null) {
for (ArtifactDefinition def : additionalBundles) {
initializedBundleList.add(def.toBundleList());
}
}
interpolateProperties(initializedBundleList, project, mavenSession);
// check for partial bundle lists
final Set<Artifact> dependencies = project.getDependencyArtifacts();
for (Artifact artifact : dependencies) {
if (PARTIAL.equals(artifact.getType())) {
getLog().info(
String.format("Merging partial bundle list %s:%s:%s", artifact.getGroupId(),
artifact.getArtifactId(), artifact.getVersion()));
initializedBundleList.merge(readBundleList(artifact.getFile()));
}
}
// handle exclusions
if (bundleExclusions != null) {
for (ArtifactDefinition def : bundleExclusions) {
initializedBundleList.remove(def.toBundleList(), false);
}
}
initBundleList(initializedBundleList);
interpolateProperties(initializedBundleList, project, mavenSession);
rewriteBundleList(initializedBundleList);
}
private final void extractConfigurations() throws MojoExecutionException, IOException {
final Set<Artifact> dependencies = project.getDependencyArtifacts();
for (Artifact artifact : dependencies) {
if (PARTIAL.equals(artifact.getType())) {
extractConfiguration(artifact);
}
}
// copy own config files
if ( this.overlayConfigDir != null && super.getConfigDirectory().exists() ) {
copyDirectory(super.getConfigDirectory(), this.overlayConfigDir, null, FileUtils.getDefaultExcludes());
}
}
private void extractConfiguration(final Artifact artifact) throws MojoExecutionException, IOException {
// check for configuration artifact
Artifact cfgArtifact = null;
try {
cfgArtifact = getArtifact(artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion(),
AttachPartialBundleListMojo.CONFIG_TYPE,
AttachPartialBundleListMojo.CONFIG_CLASSIFIER);
} catch (final MojoExecutionException ignore) {
// we just ignore this
}
if ( cfgArtifact != null ) {
getLog().info(
String.format("Merging settings from partial bundle list %s:%s:%s", cfgArtifact.getGroupId(),
cfgArtifact.getArtifactId(), cfgArtifact.getVersion()));
// extract
zipUnarchiver.setSourceFile(cfgArtifact.getFile());
try {
this.tmpOutputDir.mkdirs();
zipUnarchiver.setDestDirectory(this.tmpOutputDir);
zipUnarchiver.extract();
final File slingDir = new File(this.tmpOutputDir, "sling");
this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_PROPS), 0);
this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_PROPS), 1);
this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_PROPS), 2);
this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_BOOTSTRAP), 0);
this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_BOOTSTRAP), 1);
this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_BOOTSTRAP), 2);
// and now configurations
final File configDir = new File(this.tmpOutputDir, "config");
if ( configDir.exists() ) {
if ( this.overlayConfigDir == null ) {
this.tempConfigDir.mkdirs();
this.overlayConfigDir = this.tempConfigDir;
}
final String[] defaultExcludes = FileUtils.getDefaultExcludes();
String[] excludes;
if ( this.configExcludes != null ) {
excludes = new String[defaultExcludes.length + this.configExcludes.length];
System.arraycopy(defaultExcludes, 0, excludes, 0, defaultExcludes.length);
System.arraycopy(this.configExcludes, 0, excludes, defaultExcludes.length, this.configExcludes.length);
} else {
excludes = defaultExcludes;
}
String[] includes = null;
if ( this.configIncludes != null ) {
includes = this.configIncludes;
}
copyDirectory(configDir, this.overlayConfigDir,
includes, excludes);
}
} catch (final ArchiverException ae) {
throw new MojoExecutionException("Unable to extract configuration archive.",ae);
} finally {
// and delete at the end
FileUtils.deleteDirectory(this.tmpOutputDir);
}
}
}
private void rewriteBundleList(BundleList bundleList) throws MojoExecutionException {
if (rewriteRuleFiles != null) {
KnowledgeBase knowledgeBase = createKnowledgeBase(rewriteRuleFiles);
StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession();
try {
session.setGlobal("mavenSession", mavenSession);
session.setGlobal("mavenProject", project);
session.insert(bundleList);
session.fireAllRules();
} finally {
session.dispose();
}
}
}
private KnowledgeBase createKnowledgeBase(File[] files) throws MojoExecutionException {
KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();
builder.add(ResourceFactory.newClassPathResource("drools-globals.drl", getClass()), ResourceType.DRL);
for (File file : files) {
getLog().info("Parsing rule file " + file.getAbsolutePath());
builder.add(ResourceFactory.newFileResource(file), ResourceType.DRL);
}
if (builder.hasErrors()) {
getLog().error("Rule errors:");
for (KnowledgeBuilderError error : builder.getErrors()) {
getLog().error(error.toString());
}
throw new MojoExecutionException("Unable to create rules. See log for details.");
}
KnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase();
base.addKnowledgePackages(builder.getKnowledgePackages());
return base;
}
private void copyProperties(final Properties source, final Properties dest) {
final Enumeration<Object> keys = source.keys();
while ( keys.hasMoreElements() ) {
final Object key = keys.nextElement();
dest.put(key, source.get(key));
}
}
private void readSlingProperties(final File propsFile, final int mode) throws MojoExecutionException {
if (propsFile.exists()) {
File tmp = null;
try {
tmp = File.createTempFile("sling", "props");
mavenFileFilter.copyFile(propsFile, tmp, true, project, Collections.EMPTY_LIST, true,
System.getProperty("file.encoding"), mavenSession);
final Properties loadedProps = PropertyUtils.loadPropertyFile(tmp, null);
if ( mode == 0 ) {
if ( this.slingProperties == null ) {
this.slingProperties = loadedProps;
} else {
this.copyProperties(loadedProps, this.slingProperties);
}
filterProperties(this.slingProperties);
} else if ( mode == 1 ) {
if ( this.slingWebappProperties == null ) {
this.slingWebappProperties = loadedProps;
} else {
this.copyProperties(loadedProps, this.slingWebappProperties);
}
filterProperties(this.slingWebappProperties);
} else {
if ( this.slingStandaloneProperties == null ) {
this.slingStandaloneProperties = loadedProps;
} else {
this.copyProperties(loadedProps, this.slingStandaloneProperties);
}
filterProperties(this.slingStandaloneProperties);
}
} catch (IOException e) {
throw new MojoExecutionException("Unable to create filtered properties file", e);
} catch (MavenFilteringException e) {
throw new MojoExecutionException("Unable to create filtered properties file", e);
} finally {
if (tmp != null) {
tmp.delete();
}
}
}
}
/**
* Filter properties by removing excluded properties
*/
private void filterProperties(final Properties props) {
if ( this.propertiesExcludes != null ) {
for(final String name : this.propertiesExcludes) {
props.remove(name.trim());
}
}
}
protected Properties getSlingProperties(final boolean standalone) throws MojoExecutionException {
readSlingProperties(this.commonSlingProps, 0);
final Properties additionalProps = (standalone ? this.slingStandaloneProperties : this.slingWebappProperties);
if ( this.slingProperties == null) {
return additionalProps;
}
if ( additionalProps != null ) {
final Properties combinedProps = new Properties();
this.copyProperties(this.slingProperties, combinedProps);
this.copyProperties(additionalProps, combinedProps);
return combinedProps;
}
return this.slingProperties;
}
/**
* Try to read the bootstrap command file
* The filter is copied to a tmp location to apply filtering.
* @throws MojoExecutionException
*/
private void readSlingBootstrap(final File bootstrapFile, final int mode) throws MojoExecutionException {
if (bootstrapFile.exists()) {
File tmp = null;
Reader reader = null;
try {
tmp = File.createTempFile("sling", "bootstrap");
mavenFileFilter.copyFile(bootstrapFile, tmp, true, project, Collections.EMPTY_LIST, true,
System.getProperty("file.encoding"), mavenSession);
reader = new FileReader(tmp);
final StringBuilder sb = new StringBuilder();
if ( mode == 0 ) {
if ( this.slingBootstrapCommand != null ) {
sb.append(this.slingBootstrapCommand);
}
} else if ( mode == 1 ) {
if ( this.slingWebappBootstrapCommand != null ) {
sb.append(this.slingWebappBootstrapCommand);
}
} else {
if ( this.slingStandaloneBootstrapCommand != null ) {
sb.append(this.slingStandaloneBootstrapCommand);
}
}
final char[] buffer = new char[2048];
int l;
while ( (l = reader.read(buffer, 0, buffer.length) ) != -1 ) {
sb.append(buffer, 0, l);
}
sb.append('\n');
if ( mode == 0 ) {
this.slingBootstrapCommand = sb.toString();
} else if ( mode == 1 ) {
this.slingWebappBootstrapCommand = sb.toString();
} else {
this.slingStandaloneBootstrapCommand = sb.toString();
}
} catch (final IOException e) {
throw new MojoExecutionException("Unable to create filtered bootstrap file", e);
} catch (final MavenFilteringException e) {
throw new MojoExecutionException("Unable to create filtered bootstrap file", e);
} finally {
if (tmp != null) {
tmp.delete();
}
if ( reader != null ) {
try {
reader.close();
} catch (final IOException ignore) {}
}
}
}
}
/**
* Try to read the bootstrap command file and return its content
* The filter is copied to a tmp location to apply filtering.
* @return The contents are <code>null</code>
* @throws MojoExecutionException
*/
protected String getSlingBootstrap(final boolean standalone) throws MojoExecutionException {
this.readSlingBootstrap(this.commonSlingBootstrap, 0);
final String addCmds = (standalone ? this.slingStandaloneBootstrapCommand : this.slingWebappBootstrapCommand);
if ( this.slingBootstrapCommand == null ) {
return addCmds;
}
if ( addCmds != null ) {
final StringBuilder builder = new StringBuilder(this.slingBootstrapCommand);
builder.append('\n');
builder.append(addCmds);
return builder.toString();
}
return this.slingBootstrapCommand;
}
}