blob: 5851a89c1126285f7818cfb358899cb742864ad2 [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.jackrabbit.filevault.maven.packaging;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.jar.JarFile;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
import org.apache.jackrabbit.vault.util.Constants;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
/**
* Common ancestor of all mojos dealing with package metadata.
*/
public abstract class AbstractMetadataPackageMojo extends AbstractMojo {
private static final String PROPERTIES_EMBEDDEDFILESMAP_KEY = "embeddedfiles.map";
protected String getProjectRelativeFilePath(File file) {
return getRelativePath(project.getBasedir().toPath(), file.toPath());
}
protected static String getRelativePath(Path base, Path file) {
if (file.startsWith(base)) {
return "'" + base.relativize(file).toString() + "'";
} else {
return "'" + file.toString() + "'";
}
}
protected static File getFirstExistingDirectory(File[] directories) {
for (File dir: directories) {
if (dir.exists() && dir.isDirectory()) {
return dir;
}
}
return null;
}
/**
* The Maven project.
*/
@Parameter(property = "project", readonly = true, required = true)
protected MavenProject project;
/**
* The directory that contains the META-INF/vault. Multiple directories can be specified as a comma separated list,
* which will act as a search path and cause the plugin to look for the first existing directory.
* <p>
* This directory is added as fileset to the package archiver before the the {@link #workDirectory}. This means that
* files specified in this directory have precedence over the one present in the {@link #workDirectory}. For example,
* if this directory contains a {@code properties.xml} it will not be overwritten by the generated one. A special
* case is the {@code filter.xml} which will be merged with inline filters if present.
*/
@Parameter(property = "vault.metaInfVaultDirectory", required = true, defaultValue = "${project.basedir}/META-INF/vault,"
+ "${project.basedir}/src/main/META-INF/vault," + "${project.basedir}/src/main/content/META-INF/vault,"
+ "${project.basedir}/src/content/META-INF/vault")
File[] metaInfVaultDirectory;
/**
* The directory containing the metadata to be packaged up into the content package.
* Basically containing all files/folders being generated by goal "generate-metadata".
*/
@Parameter(
defaultValue = "${project.build.directory}/vault-work",
required = true)
File workDirectory;
/**
* Optional classifier to add to the generated package. If given, the artifact will be attached
* as a supplemental artifact having this classifier.
* Also all generated metadata will be pushed to {@code <workDirectory>-<classifier>} and will preferably
* be looked up from there. In addition the embedded file names will be exchanged leveraging a classifier specific property.
*/
@Parameter(property = "vault.classifier")
protected String classifier = "";
/**
* Adds a path prefix to all resources. Useful for shallower source trees.
* This does not apply to files in {@link #workDirectory} nor {@link #metaInfVaultDirectory}
* but e.g. is relevant for the default filter and for the jcr_root of the package.
* Must start with "/" if not empty. As separator only forward slashes are allowed.
* The trailing slash is automatically appended if not there.
*/
@Parameter(property = "vault.prefix", defaultValue = "")
String prefix = "";
/**
* Timestamp for reproducible output archive entries, either formatted as ISO 8601
* <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
* <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>).
*
* @since 1.1.0
*/
@Parameter( defaultValue = "${project.build.outputTimestamp}" )
protected String outputTimestamp;
public void setPrefix(String prefix) {
if (prefix.startsWith("/")) {
throw new IllegalArgumentException("Parameter 'prefix' must start with a slash!");
}
if (prefix.length() > 0 && !prefix.endsWith("/")) {
prefix += "/";
}
this.prefix = prefix;
}
/**
* Sets the map of embedded files as project properties as a helper to pass data between the goals.
* It will always use classifier specific project properties.
* @param embeddedFiles map of embedded files (key=destination file name, value = source file)
*/
@SuppressWarnings("unchecked")
void setEmbeddedFilesMap(Map<String, File> embeddedFiles) {
getPluginContext().put(PROPERTIES_EMBEDDEDFILESMAP_KEY + classifier, embeddedFiles);
}
/**
* Reads the map of embedded files from the project properties. This is a helper to pass data between the goals.
* It will preferably use the embedded files from the classifier specific project properties.
* @return the map of embedded files (key=destination file name, value = source file)
*/
Map<String, File> getEmbeddedFilesMap() {
Map<String, File> map = getEmbeddedFilesMap(PROPERTIES_EMBEDDEDFILESMAP_KEY + classifier);
if (map.isEmpty()) {
getLog().debug("Using regular embedded files map as classifier specific one does not exist!");
map = getEmbeddedFilesMap(PROPERTIES_EMBEDDEDFILESMAP_KEY);
}
return map;
}
@SuppressWarnings("unchecked")
private Map<String, File> getEmbeddedFilesMap(String key) {
Object value = getPluginContext().get(key);
if (value == null) {
return Collections.emptyMap();
} else {
if (value instanceof Map<?,?>) {
return (Map<String, File>) value;
} else {
throw new IllegalStateException("The Maven property " + key + " is not containing a Map but rather " + value.getClass());
}
}
}
File getWorkDirectory(boolean isForWriting) {
if (StringUtils.isNotBlank(classifier)) {
File classifierWorkDirectory = new File(workDirectory.toString() + "-" + classifier);
if (!isForWriting) {
// fall back to regular work directory if work dir for classifier does not exist
if (!classifierWorkDirectory.exists()) {
getLog().warn("Using regular workDirectory " + workDirectory + " as classifier specific workDirectory does not exist at " + classifierWorkDirectory);
return workDirectory;
}
}
return classifierWorkDirectory;
} else {
return workDirectory;
}
}
/**
*
* @return the META-INF/vault directory below the (classifier-specific) {@link #workDirectory}
*/
File getGeneratedVaultDir(boolean isForWriting) {
return new File(getWorkDirectory(isForWriting), Constants.META_DIR);
}
File getGeneratedManifestFile(boolean isForWriting) {
return new File(getWorkDirectory(isForWriting), JarFile.MANIFEST_NAME);
}
File getGeneratedFilterFile(boolean isForWriting) {
return new File(getGeneratedVaultDir(isForWriting), Constants.FILTER_XML);
}
/**
*
* @return the first matching directory from the list given in {@link #metaInfVaultDirectory}
*/
protected File getMetaInfVaultSourceDirectory() {
return getMetaInfVaultSourceDirectory(metaInfVaultDirectory, getLog());
}
protected static File getMetaInfVaultSourceDirectory(File[] metaInfVaultDirectory, Log log) {
File metaInfDirectory = getFirstExistingDirectory(metaInfVaultDirectory);
if (metaInfDirectory != null) {
log.info("Using META-INF/vault from " + metaInfDirectory.getPath());
}
return metaInfDirectory;
}
Filters loadGeneratedFilterFile() throws IOException, ConfigurationException {
// load filters for further processing
Filters filters = new Filters();
filters.load(getGeneratedFilterFile(false));
return filters;
}
}