
com.mediaworx.mojo.opencms.AbstractOpenCmsMojo Maven / Gradle / Ivy
package com.mediaworx.mojo.opencms;
/*-
* #%L
* OpenCms Maven Plugin
* %%
* Copyright (C) 2017 - 2018 Silpion IT-Solutions GmbH (https://www.silpion.de/)
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import com.mediaworx.opencms.moduleutils.manifestgenerator.OpenCmsModuleManifestGenerator;
import com.mediaworx.opencms.moduleutils.manifestgenerator.exceptions.OpenCmsMetaXmlFileWriteException;
import com.mediaworx.opencms.moduleutils.manifestgenerator.exceptions.OpenCmsMetaXmlParseException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
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.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
import org.apache.maven.shared.filtering.MavenProjectValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.InterpolatorFilterReader;
import org.codehaus.plexus.interpolation.MapBasedValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.StringSearchInterpolator;
import org.codehaus.plexus.util.FileUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
//import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
public abstract class AbstractOpenCmsMojo extends AbstractMojo {
/**
* Directory containing the resources to be included, defaults to src/main/vfs.
*/
@Parameter(defaultValue = "${project.basedir}/src/main/vfs")
protected String vfsRoot = "";
/**
* Where the module zip should be created.
*/
@Parameter(defaultValue = "${project.build.directory}")
protected String moduleDir = null;
/**
* The directory containing generated classes.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}")
protected File classesDirectory;
/**
* Whether to include classes in final module zip.
*/
@Parameter(defaultValue = "true")
protected Boolean addClasses;
/**
* This is the temporary folder where the module is assembled.
*/
@Parameter(defaultValue = "${project.build.directory}/opencms-module")
protected String targetDir = null;
/**
* The Name of the module, defaults to ${project.groupId}.${project.artifactId}.
*/
@Parameter(defaultValue = "${project.groupId}.${project.artifactId}")
protected String moduleName = "";
/**
* The Version of the module. Defaults to ${project.version} with non numeric characters being removed from the end,
* e.g. -SNAPSHOT.
*/
@Parameter(defaultValue = "${project.version}")
protected String moduleVersion = "";
/**
* The maven project.
*/
@Component
protected MavenProject project;
/**
* Directories in sourceResources to exclude (ant-style excludes). You may provide excludes in ant-style patterns. Files
* matching that pattern will not be included to the module (basically they will not be copied to ${targetDir}).
*/
@Parameter(defaultValue = "**/.svn/,**/.cvs/,**/.git/,**/Thumbs.db,**/.DS_STORE")
protected String excludes = "";
/**
* The manifest provided is filtered and copied to ${targetDir} for inclusion in the module.
* In addition to the standard Maven project properties and user properties, the following properties will be set up
* from default Maven project properties as follows:
*
* - ${moduleversion} set to ${project.version} with stripped off -SNAPSHOT suffix
* - ${modulename} set to ${project.groupId}.${project.artifactId}
* - ${modulenicename} set to ${project.name}
* - ${moduledescription} set to ${project.description}
* - ${opencmsversion} set to the version of the opencms-core dependency
*
* The defaults can be overridden through POM properties or (except for ${modulenicename} and
* ${opencmsversion}) in the plugin configuration section.
*/
@Parameter
protected String manifestFile = null;
/**
* The manifest stub provided in conjunction with the meta information gathered is used to generate a manifest file.
* The same filtering as with manifestFile applies.
*/
@Parameter
protected String manifestMetaDir = null;
/**
* Whether to generate and replace uuids and dates in manifest fragments.
* Works only when using manifestMetaDir.
*/
@Parameter(defaultValue = "false")
protected Boolean replaceMetaVariables;
/**
* Defines a set of additional resources that are included in the module.
* By default, files under vfsRoot and under ${project.build.outputDirectory} as well as
* additional runtime dependencies will be included.
* Currently only <directory> and <targetPath> are
* evaluated. The <targetPath> configured is relative to ${targetDir}.
*
A possible configuration might look like
* this:
*
* <srcResources>
* <resource>
* <directory>${project.basedir}/web/</directory>
* <targetPath></targetPath>
* </resource>
* </srcResources>
*
*/
@Parameter
protected List srcResources;
@Parameter(defaultValue = "true")
protected Boolean failOnMissingResource;
/**
* Whether to include project dependencies in the module.
* This will only add the files to the module zip,
* they have to be included in the manifest to be imported into OpenCms.
* You can also use the Maven dependency plugin to add dependencies
* to your vfs lib directory.
*/
@Parameter(defaultValue = "false")
protected Boolean addDependencies;
@Parameter(defaultValue = "true")
protected Boolean dependenciesWithVersion;
@Parameter(defaultValue = "false", property = "skipOpenCms")
protected Boolean skipExecution;
protected String opencmsVersion = "";
@Component
@Parameter(defaultValue = "${localRepository}", readonly = true, required = true)
protected ArtifactRepository localRepository;
@Component
protected MavenProjectBuilder projectBuilder;
@Component
protected MavenProjectHelper projectHelper;
@Component
protected ArtifactHandlerManager artifactHandlerManager;
/* a list of dynamically added resources, like: JARs */
protected Set attachedModuleResources = new TreeSet<>();
public String getModuleVersion() {
return moduleVersion;
}
public void buildModule() throws MojoFailureException, MojoExecutionException {
File target = new File(targetDir);
// only create the target directory when it doesn't exist
// no deletion
if (!target.exists() && !target.mkdirs()) {
throw new MojoFailureException("Couldn't create target directory " + target.getAbsolutePath());
}
getLog().info("Target is " + target);
// check wether vfsRoot exists
File vfsRootF = new File(vfsRoot);
if (!vfsRootF.exists() || !vfsRootF.isDirectory()) {
throw new MojoFailureException("Directory doesn't exist: " + vfsRootF.getAbsolutePath());
}
Boolean hasClassesDir = false;
Boolean isClassesUnderVfs = false;
if (classesDirectory.exists() && classesDirectory.isDirectory()) {
hasClassesDir = Boolean.TRUE.equals(addClasses);
isClassesUnderVfs = classesDirectory.getAbsolutePath().startsWith(vfsRootF.getAbsolutePath());
}
Iterator files;
try {
Resource vfs = new Resource();
vfs.setDirectory(vfsRoot);
List res = new ArrayList<>();
res.add(vfs);
if (hasClassesDir && !isClassesUnderVfs) {
Resource classes = new Resource();
classes.setDirectory(classesDirectory.getAbsolutePath());
classes.setTargetPath("/system/modules/" + moduleName + "/classes");
res.add(classes);
}
if (null != srcResources) {
for (Resource srcResource : srcResources) {
try {
FileUtils.getFiles(new File(srcResource.getDirectory()), null, excludes);
res.add(srcResource);
} catch (IllegalStateException e) {
getLog().warn("Resource doesn't exists: " + e.getMessage());
getLog().debug(e);
if (failOnMissingResource) {
throw e;
}
}
}
}
for (Resource resource : res) {
File source = new File(resource.getDirectory());
files = getFilesAnDDirectories(source, null, excludes).iterator();
String srcPath = source.getAbsolutePath();
String targetPath = FilenameUtils.normalize(targetDir + getTargetPath(resource));
while (files.hasNext()) {
File file = (File) files.next();
if (file.isFile()) {
attachFile(srcPath, targetPath, file);
} else if (file.isDirectory()) {
if (!file.equals(source)) {
attachFolder(srcPath, targetPath, file);
}
}
}
}
} catch (IOException ex) {
throw new MojoExecutionException("Could not copy file(s) to directory", ex);
}
}
private List getFilesAnDDirectories(File source, String includes, String excludes) throws IOException {
List directoryNames = FileUtils.getFileAndDirectoryNames(source, includes, excludes, false, true, false, true);
List fileNames = FileUtils.getFileAndDirectoryNames(source, includes, excludes, false, true, true, false);
List files = new ArrayList<>();
for (String filename : directoryNames) {
files.add(new File(source, filename));
}
for (String filename : fileNames) {
files.add(new File(source, filename));
}
return files;
}
private void attachFile(String srcPath, String targetPath, File file) throws IOException {
getLog().debug("Copying " + file.getAbsolutePath());
File destination = new File(targetPath, file.getAbsolutePath().substring(srcPath.length() + 1));
FileUtils.copyFile(file, destination);
String fileVfsPath = StringUtils.removeStart(FilenameUtils.normalize(destination.getAbsolutePath()), targetPath);
File metaFile = new File(manifestMetaDir, fileVfsPath + ".ocmsfile.xml");
// when no meta file exists for file, attache as module resource, so we add a file entry later
if (!metaFile.exists()) {
attachModuleResource(ModuleResource.ofFile(destination));
}
}
private void attachFolder(String srcPath, String targetPath, File file) throws IOException {
getLog().debug("Create Directory file: '" + file.getAbsolutePath() + "', srcPath: '" + srcPath + "', targetPath: '" + targetPath + "'");
File destination = new File(targetPath, file.getAbsolutePath().substring(srcPath.length() + 1));
if (!destination.exists()) {
destination.mkdir();
}
String fileVfsPath = StringUtils.removeStart(FilenameUtils.normalize(destination.getAbsolutePath()), targetPath);
File metaFile = new File(manifestMetaDir, fileVfsPath + ".ocmsfolder.xml");
// when no meta file exists for file, attache as module resource, so we add a file entry later
if (!metaFile.exists()) {
attachModuleResource(ModuleResource.ofFolder(destination));
}
}
protected void addDependencies() throws MojoExecutionException {
Set artifacts = getProjectArtifacts();
OUTER:
for (MavenArtifact artifact : artifacts) {
getLog().debug("Processing: " + artifact.getArtifactId());
if ("opencms-core".equals(artifact.getArtifactId())) {
opencmsVersion = artifact.getVersion();
}
for (String trail : artifact.getDependencyTrail()) {
getLog().debug(" Trail: " + trail);
if (trail.contains(":opencms-core:")) {
getLog().debug(" Skipping " + artifact.getArtifactId());
continue OUTER;
}
}
ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME);
if (!artifact.isOptional() && filter.include(artifact.artifact) && "jar".equals(artifact.getType())) {
copyLibraryToModule(artifact);
}
}
}
protected void copyLibraryToModule(MavenArtifact artifact) throws MojoExecutionException {
String libDirectory = getModuleLibDir();
String finalArtifactName = getArtifactName(artifact);
getLog().info("Adding " + finalArtifactName);
try {
File destination = new File(libDirectory, finalArtifactName);
FileUtils.copyFile(artifact.getFile(), destination);
attachModuleResource(new ModuleResource.Jar(destination));
} catch (IOException e) {
throw new MojoExecutionException("Could not copy artifact: " + finalArtifactName + " to " + libDirectory, e);
}
}
protected String getModuleLibDir() {
return targetDir + "/system/modules/" + moduleName + "/lib";
}
private String getArtifactName(MavenArtifact artifact) {
String finalName;
if (!dependenciesWithVersion) {
finalName = artifact.getFinalNameNoVersion();
} else {
finalName = artifact.getDefaultFinalName();
}
return finalName;
}
private void generateManifest(File metaDir) throws MojoExecutionException {
System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
System.setProperty("org.slf4j.simpleLogger.showDateTime", "false");
System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "true");
System.setProperty("org.slf4j.simpleLogger.showLogName", "false");
OpenCmsModuleManifestGenerator mg = new OpenCmsModuleManifestGenerator();
try {
mg.setReplaceMetaVariables(replaceMetaVariables);
mg.generateManifest(metaDir);
} catch (OpenCmsMetaXmlParseException | OpenCmsMetaXmlFileWriteException e) {
throw new MojoExecutionException("Error generating manifest", e);
}
}
private void fixTimestamp(File metaDir) throws MojoExecutionException {
List metaFiles;
try {
String vfsRootCanon = new File(vfsRoot).getCanonicalPath();
String metaDirCanon = metaDir.getCanonicalPath();
metaFiles = FileUtils.getFiles(metaDir, "**/*.ocmsfile.xml", excludes);
for (File metaFile : metaFiles) {
File realFile = new File(metaFile.getCanonicalPath().replace(metaDirCanon, vfsRootCanon).replaceFirst("\\.ocmsfile\\.xml$", ""));
if (realFile.isFile() && realFile.exists()) {
if (getLog().isDebugEnabled()) {
getLog().debug("Set " + metaFile.getCanonicalPath() + " from " + realFile.getCanonicalPath());
}
if (!metaFile.setLastModified(realFile.lastModified())) {
getLog().warn("Failed to set last modified on " + metaFile.getCanonicalPath());
}
} else {
getLog().warn("*** Missing file referenced in manifest ***");
getLog().warn(realFile.getAbsolutePath() + " for " + metaFile.getAbsolutePath() + " not found");
getLog().warn("*** Check your meta files!! ***");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
protected void addManifest() throws MojoExecutionException {
if ((manifestFile == null || manifestFile.isEmpty()) && (manifestMetaDir == null || manifestMetaDir.isEmpty())) {
throw new MojoExecutionException("Neither manifestFile nor manifestMetaDir given");
}
File src;
boolean isGenerated = false;
if (manifestMetaDir != null && !manifestMetaDir.isEmpty()) {
File metaDir = new File(manifestMetaDir);
if (!metaDir.exists() || !metaDir.isDirectory()) {
throw new MojoExecutionException("Could not find manifest meta directory: " + manifestMetaDir);
}
fixTimestamp(metaDir);
isGenerated = true;
generateManifest(metaDir);
src = new File(manifestMetaDir, "manifest.xml");
} else {
src = new File(manifestFile);
}
if (!src.exists() || !src.isFile()) {
throw new MojoExecutionException("Could not find " + (isGenerated ? "generated" : "" + " manifest: ") + src.getAbsolutePath());
}
try {
File manifestTarget = new File(targetDir, "manifest.xml");
FileUtils.copyFile(src, manifestTarget, "UTF-8", filters(true));
addAttachedResourcesToManifest(manifestTarget);
} catch (IOException ex) {
throw new MojoExecutionException("Could not copy manifest", ex);
}
}
private void addAttachedResourcesToManifest(File src) throws MojoExecutionException {
try {
ModuleManifest moduleManifest = new ModuleManifest(src).setLog(getLog());
for (ModuleResource moduleResource : attachedModuleResources) {
moduleManifest.addResource(moduleResource);
}
moduleManifest.write(new FileOutputStream(src));
} catch (OpenCmsMetaXmlParseException | IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
public File getClassesDirectory() {
return classesDirectory;
}
protected String getTargetPath(Resource r) {
try {
return (null == r.getTargetPath()) ? "" : "/" + createPropertyInterpolator().interpolate(r.getTargetPath());
} catch (InterpolationException e) {
throw new RuntimeException(e);
}
}
protected Set getProjectArtifacts() {
return wrap(Artifacts.of(project));
}
protected Set wrap(Iterable artifacts) {
Set r = new HashSet();
for (Artifact a : artifacts) {
r.add(wrap(a));
}
return r;
}
@SuppressWarnings("unchecked")
protected MavenArtifact wrap(Artifact a) {
return new MavenArtifact(a, projectBuilder, project.getRemoteArtifactRepositories(), localRepository);
}
@SuppressWarnings("unchecked")
private FileUtils.FilterWrapper[] filters(boolean filter) throws IOException {
if (!filter) {
return new FileUtils.FilterWrapper[0];
}
FileUtils.FilterWrapper wrapper = new FileUtils.FilterWrapper() {
public Reader getReader(Reader reader) {
Interpolator propertiesInterpolator = createPropertyInterpolator();
InterpolatorFilterReader interpolatorFilterReader = new InterpolatorFilterReader(reader, propertiesInterpolator);
interpolatorFilterReader.setInterpolateWithPrefixPattern(false);
return interpolatorFilterReader;
}
};
return new FileUtils.FilterWrapper[]{wrapper};
}
public void setModuleVersion(String version) {
// modify version number to be OpenCms compatible (containing only digits
this.moduleVersion = extractVersionNumber(version);
}
private String extractVersionNumber(String originalVersionNumber) {
// check if there are non-digit letters at the end
if (originalVersionNumber != null && originalVersionNumber.matches(".*\\D+$")) {
// remove anything after the last digit
originalVersionNumber = originalVersionNumber.replaceAll("\\D+$", "");
}
return originalVersionNumber;
}
protected Interpolator createPropertyInterpolator() {
String escapeString = "\\";
Map values = new HashMap<>();
values.put("moduleversion", project.getProperties().getProperty("moduleversion", moduleVersion));
values.put("modulename", project.getProperties().getProperty("modulename", moduleName));
values.put("modulenicename", project.getProperties().getProperty("modulenicename", project.getName()));
values.put("moduledescription", project.getProperties().getProperty("moduledescription", project.getDescription()));
values.put("opencmsversion", project.getProperties().getProperty("opencmsversion", opencmsVersion));
StringSearchInterpolator propertiesInterpolator = new StringSearchInterpolator();
propertiesInterpolator.addValueSource(new MavenProjectValueSource(project, true));
propertiesInterpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
propertiesInterpolator.addValueSource(new MapBasedValueSource(values));
propertiesInterpolator.setEscapeString(escapeString);
return propertiesInterpolator;
}
protected void attachModuleResource(ModuleResource resource) {
attachParentFolders(resource);
attachedModuleResources.add(resource);
}
private void attachParentFolders(ModuleResource resource) {
// the relativ path within VFS
Path vfsPath = Paths.get(resource.getVfsPath(targetDir));
if (!"folder".equals(resource.getType())) {
vfsPath = vfsPath.getParent();
}
// the sub-path for all parts
Path manifestSubPath = Paths.get(manifestMetaDir);
Path targetSubPath = Paths.get(targetDir);
for (Path pathPart : vfsPath) {
manifestSubPath = manifestSubPath.resolve(pathPart);
targetSubPath = targetSubPath.resolve(pathPart);
Path metaFile = Paths.get(manifestSubPath.toString() + ".ocmsfolder.xml");
if (!Files.exists(metaFile)) {
getLog().debug("Add parent folder: " + targetSubPath);
attachedModuleResources.add(ModuleResource.ofFolder(targetSubPath.toFile()));
}
}
}
public boolean isSkipExecution() {
return skipExecution != null && skipExecution;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy