You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2017/09/15 06:24:39 UTC
svn commit: r1808412 [5/7] - in
/jackrabbit/commons/filevault-package-maven-plugin/trunk: ./ it/ it/src/
it/src/test/ it/src/test/java/ it/src/test/java/org/
it/src/test/java/org/apache/ it/src/test/java/org/apache/jackrabbit/
it/src/test/java/org/apac...
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ImportPackageBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ImportPackageBuilder.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ImportPackageBuilder.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/ImportPackageBuilder.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,484 @@
+/*
+ * 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.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import javax.annotation.Nonnull;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.StringUtils;
+
+import aQute.bnd.header.Attrs;
+import aQute.bnd.header.Parameters;
+import aQute.bnd.osgi.Analyzer;
+import aQute.bnd.osgi.Clazz;
+import aQute.bnd.osgi.Constants;
+import aQute.bnd.osgi.Descriptors;
+import aQute.bnd.osgi.FileResource;
+import aQute.bnd.osgi.Processor;
+import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
+import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
+
+/**
+ * The import package builder is used to analyze the classes and dependencies of the project and calculate the
+ * import-package statement for this package.
+ */
+public class ImportPackageBuilder {
+
+ /**
+ * class file directory
+ */
+ private File classFileDirectory;
+
+ /**
+ * list of class files. initialized during {@link #analyze()}
+ */
+ private List<File> classFiles;
+
+ /**
+ * list of artifacts relevant for analysis
+ */
+ private List<Artifact> artifacts;
+
+ /**
+ * BND analyzer
+ */
+ private Analyzer analyzer;
+
+ /**
+ * The scan result from the fast-classpath analyzer
+ */
+ private ScanResult scanResult;
+
+ /**
+ * artifact-id -> bundle info mapping
+ */
+ private Map<String, BundleInfo> bundles = new HashMap<String, BundleInfo>();
+
+ /**
+ * map of all exported packaged by the bundles.
+ */
+ private Map<String, PackageInfo> exported = new HashMap<String, PackageInfo>();
+
+ /**
+ * map of all classes
+ */
+ private Map<String, ClassInfo> classes = new HashMap<String, ClassInfo>();
+
+ /**
+ * the calculated import parameters.
+ */
+ private Map<String, Attrs> importParameters = Collections.emptyMap();
+
+ /**
+ * Sets the class files directory
+ * @param classes the directory
+ * @return this.
+ */
+ @Nonnull
+ public ImportPackageBuilder withClassFileDirectory(File classes) {
+ classFileDirectory = classes;
+ return this;
+ }
+
+ /**
+ * defines the project from which the artifacts should be loaded.
+ * @param project the maven project
+ * @return this
+ */
+ @Nonnull
+ public ImportPackageBuilder withDependenciesFromProject(@Nonnull MavenProject project) {
+ artifacts = new ArrayList<Artifact>();
+ for (Artifact a : project.getDependencyArtifacts()) {
+ if (!Artifact.SCOPE_PROVIDED.equals(a.getScope()) && !Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
+ continue;
+ }
+ if (!"jar".equals(a.getType())) {
+ continue;
+ }
+ artifacts.add(a);
+ }
+ return this;
+ }
+
+ /**
+ * analyzes the imports
+ * @return this
+ * @throws IOException if an error occurrs.
+ */
+ @Nonnull
+ public ImportPackageBuilder analyze() throws IOException {
+ initClassFiles();
+ initAnalyzer();
+ scanClassPath();
+ scanBundles();
+ scanClasses();
+ calculateImportParameters();
+ return this;
+ }
+
+ /**
+ * returns the import parameter header. only available after {@link #analyze()}
+ * @return the parameters
+ */
+ @Nonnull
+ public Map<String, Attrs> getImportParameters() {
+ return importParameters;
+ }
+
+ /**
+ * generates a package report
+ * @return the report
+ */
+ @Nonnull
+ public String createExportPackageReport() {
+ TreeSet<String> unusedBundles = new TreeSet<String>(bundles.keySet());
+ StringBuilder report = new StringBuilder("Export package report:\n\n");
+ List<String> packages = new ArrayList<String>(exported.keySet());
+ Collections.sort(packages);
+ int pad = 18;
+ for (String packageName : packages) {
+ pad = Math.max(pad, packageName.length());
+ }
+ pad += 2;
+
+ report
+ .append(StringUtils.rightPad("Exported packages", pad))
+ .append(StringUtils.rightPad("Uses", 5))
+ .append(StringUtils.rightPad("Version", 10))
+ .append("Dependency\n");
+ report.append(StringUtils.repeat("-", pad + 30)).append("\n");
+ for (String packageName : packages) {
+ PackageInfo info = exported.get(packageName);
+
+ report.append(StringUtils.rightPad(packageName, pad));
+ report.append(StringUtils.rightPad(String.valueOf(info.usedBy.size()), 5));
+ boolean first = true;
+ for (BundleInfo bInfo : info.bundles.values()) {
+ if (first) {
+ String version = bInfo.packageVersions.get(packageName);
+ if (StringUtils.isEmpty(version)) {
+ version = "0.0.0";
+ }
+ report.append(StringUtils.rightPad(version, 10));
+ report.append(bInfo.getId());
+ first = false;
+ }
+ if (!info.usedBy.isEmpty()) {
+ unusedBundles.remove(bInfo.getId());
+ }
+ }
+ if (first) {
+ report.append(StringUtils.rightPad("n/a", 10));
+ }
+ report.append("\n");
+ }
+
+ report.append("\n").append(unusedBundles.size()).append(" unused bundles\n");
+ report.append("------------------------------\n");
+ for (String bundleId : unusedBundles) {
+ report.append(bundleId).append("\n");
+ }
+
+ report.append("\nAuto generated import-package: \n");
+ report.append("------------------------------\n");
+ for (Map.Entry<String, Attrs> e: importParameters.entrySet()) {
+ report.append(e.getKey());
+ try {
+ Processor.printClause(e.getValue(), report);
+ } catch (IOException e1) {
+ throw new IllegalStateException("Internal error while generating report", e1);
+ }
+ report.append("\n");
+ }
+
+ return report.toString();
+ }
+
+ /**
+ * internally scans all the class files.
+ */
+ private void initClassFiles() {
+ if (!classFileDirectory.exists()) {
+ classFiles = Collections.emptyList();
+ return;
+ }
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.setBasedir(classFileDirectory);
+ scanner.setIncludes(new String[]{"**/*.class"});
+ scanner.scan();
+ String[] paths = scanner.getIncludedFiles();
+ classFiles = new ArrayList<File>(paths.length);
+ for (String path : paths) {
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = new File(classFileDirectory, path);
+ }
+ classFiles.add(file);
+ }
+ }
+
+ /**
+ * Returns the classloader for the analysis. this should exclude the classes of the maven runtime.
+ *
+ * @throws IOException If an error occurs
+ */
+ private ClassLoader getClassLoader() throws IOException,
+ DependencyResolutionRequiredException {
+ List<URL> classPath = new ArrayList<URL>();
+
+ // add output directory to classpath
+ classPath.add(classFileDirectory.toURI().toURL());
+
+ // add artifacts from project
+ for (Artifact a: artifacts) {
+ classPath.add(a.getFile().toURI().toURL());
+ }
+
+ // use our parent as parent, in order to exclude the maven runtime.
+ return new URLClassLoader(classPath.toArray(new URL[classPath.size()]), this.getClass().getClassLoader().getParent());
+ }
+
+ /**
+ * scans the classpath
+ * @throws IOException if an error occurrs.
+ */
+ private void scanClassPath() throws IOException {
+ try {
+ scanResult = new FastClasspathScanner()
+ .overrideClassLoaders(getClassLoader())
+ .scan();
+
+ } catch (Exception e) {
+ throw new IOException("Failed to scan the classpath", e);
+ }
+ }
+
+ /**
+ * initializes the bnd analyzer
+ */
+ private void initAnalyzer() {
+ analyzer = new Analyzer();
+ }
+
+ /**
+ * scans all the bundles and initializes their export packages.
+ * @throws IOException if an error occurrs
+ */
+ private void scanBundles() throws IOException {
+ for (Artifact a : artifacts) {
+ BundleInfo info = new BundleInfo(a);
+ bundles.put(info.getId(), info);
+
+ // update the reverse map
+ for (String pkgName : info.packageVersions.keySet()) {
+ PackageInfo pkg = exported.get(pkgName);
+ if (pkg == null) {
+ pkg = new PackageInfo(pkgName);
+ exported.put(pkgName, pkg);
+ }
+ pkg.bundles.put(info.getId(), info);
+ }
+ }
+ }
+
+ /**
+ * Registers the package reference from the given class
+ * @param info the class info that references the package
+ * @param pkgName the package that is referenced
+ */
+ private void registerPackageReference(ClassInfo info, String pkgName) {
+ PackageInfo pkgInfo = exported.get(pkgName);
+ if (pkgInfo == null) {
+ pkgInfo = new PackageInfo(pkgName);
+ exported.put(pkgName, pkgInfo);
+ }
+ info.resolved.put(pkgName, pkgInfo);
+ pkgInfo.usedBy.add(info.getName());
+ }
+
+ /**
+ * scans the classes and resolves them against the bundles.
+ * @throws IOException if an error occurrs.
+ */
+ private void scanClasses() throws IOException {
+ for (File file : classFiles) {
+ try {
+ Clazz clazz = new Clazz(analyzer, file.getPath(), new FileResource(file));
+ clazz.parseClassFile();
+ ClassInfo info = new ClassInfo(clazz);
+ classes.put(info.getName(), info);
+
+ String myPackage = getPackageName(info.getName());
+ for (Descriptors.PackageRef ref : clazz.getReferred()) {
+ String importPkgName = ref.getFQN();
+ if (!importPkgName.equals(myPackage)) {
+ registerPackageReference(info, importPkgName);
+ }
+ }
+
+ // checking for super classes
+ io.github.lukehutch.fastclasspathscanner.scanner.ClassInfo clzInfo = scanResult.getClassNameToClassInfo().get(clazz.getFQN());
+ if (clzInfo != null) {
+ for (String name: clzInfo.getNamesOfImplementedInterfaces()) {
+ registerPackageReference(info, getPackageName(name));
+ }
+ for (String name: clzInfo.getNamesOfSuperclasses()) {
+ registerPackageReference(info, getPackageName(name));
+ }
+ }
+ } catch (Exception e) {
+ throw new IOException("Error while parsing class: " + file.getPath(), e);
+ }
+ }
+ }
+
+ /**
+ * Returns the package name for the given class name
+ * @param className the class name
+ * @return the package name
+ */
+ private static String getPackageName(String className) {
+ return StringUtils.chomp(className, ".");
+ }
+
+ /**
+ * Calculates returns the import parameter header.
+ */
+ private void calculateImportParameters() {
+ importParameters = new TreeMap<String, Attrs>();
+ for (PackageInfo info : exported.values()) {
+ if (!classFiles.isEmpty() && info.usedBy.isEmpty()) {
+ // skip if not used.
+ continue;
+ }
+ if (info.bundles.isEmpty()) {
+ // skip if no bundle
+ continue;
+ }
+ // get first version
+ BundleInfo bInfo = info.bundles.values().iterator().next();
+ String version = bInfo.packageVersions.get(info.getName());
+ Attrs options = new Attrs();
+ if (!StringUtils.isEmpty(version)) {
+ options.put(Constants.VERSION_ATTRIBUTE, new aQute.bnd.version.VersionRange("@" + version).toString());
+ }
+ importParameters.put(info.getName(), options);
+ }
+ }
+
+
+ private static class PackageInfo {
+
+ private final String name;
+
+ private final Map<String, BundleInfo> bundles = new HashMap<String, BundleInfo>();
+
+ private final Set<String> usedBy = new HashSet<String>();
+
+ private PackageInfo(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ private static class BundleInfo {
+
+ private final String id;
+
+ private final Map<String, String> packageVersions = new HashMap<String, String>();
+
+ private BundleInfo(Artifact artifact) throws IOException {
+ id = artifact.getId();
+ JarFile jar = new JarFile(artifact.getFile());
+ Manifest manifest = jar.getManifest();
+ String exportPackages = manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE);
+ if (exportPackages != null) {
+ for (Map.Entry<String, Attrs> entry : new Parameters(exportPackages).entrySet()) {
+ Attrs options = entry.getValue();
+ String version = options.getVersion();
+ packageVersions.put(entry.getKey(), version == null ? "" : version);
+ }
+ } else {
+ // scan the jar and associate the version
+ Enumeration<JarEntry> entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry e = entries.nextElement();
+ if (e.isDirectory()) {
+ continue;
+ }
+ String path = e.getName();
+ if (path.endsWith(".class")) {
+ // skip internal / impl
+ if (path.contains("/impl/") || path.contains("/internal/")) {
+ continue;
+ }
+ path = StringUtils.chomp(path, "/");
+ if (path.charAt(0) == '/') {
+ path = path.substring(1);
+ }
+ String packageName = path.replaceAll("/", ".");
+ packageVersions.put(packageName, "");
+ }
+ }
+ }
+ jar.close();
+ }
+
+ public String getId() {
+ return id;
+ }
+ }
+
+ private static class ClassInfo {
+
+ private final Clazz clazz;
+
+ private final Map<String, PackageInfo> resolved = new HashMap<String, PackageInfo>();
+
+ private ClassInfo(Clazz clazz) {
+ this.clazz = clazz;
+ }
+
+ public String getName() {
+ return clazz.getFQN();
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/IncludeExcludeList.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/IncludeExcludeList.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/IncludeExcludeList.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/IncludeExcludeList.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,40 @@
+/*
+ * 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.PrintWriter;
+import java.util.ArrayList;
+
+public class IncludeExcludeList extends ArrayList<String> {
+
+ public boolean add(String entry) {
+ return entry != null && super.add(entry);
+ }
+
+ public void write(PrintWriter pw, String tag) {
+ for (String include: this) {
+ pw.print(Filter.INDENT);
+ pw.print(Filter.INDENT);
+ pw.print('<');
+ pw.print(tag);
+ pw.print(" pattern=\"");
+ pw.print(include);
+ pw.print("\" />");
+ pw.println();
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageDependency.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageDependency.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageDependency.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageDependency.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,265 @@
+/*
+ * 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.util.List;
+import java.util.ArrayList;
+
+/**
+ * Implements a package dependency reference.
+ * @since 2.0
+ */
+public class PackageDependency {
+
+ /**
+ * An empty dependency array
+ */
+ public static final PackageDependency[] EMPTY = new PackageDependency[0];
+
+ /**
+ * group id of the dependency
+ */
+ private final String groupId;
+
+ /**
+ * name of the dependency
+ */
+ private final String name;
+
+ /**
+ * Version range of the dependency
+ */
+ private final VersionRange range;
+
+ /**
+ * internal string representation
+ */
+ private final String str;
+
+ /**
+ * Creates a new dependency reference.
+ * @param groupId group id
+ * @param name name
+ * @param range version range
+ */
+ public PackageDependency(String groupId, String name, VersionRange range) {
+ if (groupId.startsWith(PackageId.ETC_PACKAGES_PREFIX)) {
+ groupId = groupId.substring(PackageId.ETC_PACKAGES_PREFIX.length());
+ }
+ this.groupId = groupId;
+ this.name = name;
+ this.range = range == null ? VersionRange.INFINITE : range;
+ StringBuilder b = new StringBuilder();
+ if (groupId.length() > 0 || !VersionRange.INFINITE.equals(this.range)) {
+ b.append(groupId);
+ b.append(":");
+ }
+ b.append(name);
+ if (!VersionRange.INFINITE.equals(this.range)) {
+ b.append(":");
+ b.append(range);
+ }
+ this.str = b.toString();
+ }
+
+ /**
+ * Creates a new dependency to the specified package id
+ * @param id package id.
+ */
+ public PackageDependency(PackageId id) {
+ this(id.getGroup(), id.getName(), new VersionRange(id.getVersion()));
+ }
+
+ /**
+ * Returns the group id of the dependency
+ * @return the gorup id
+ */
+ public String getGroupId() {
+ return groupId;
+ }
+
+ /**
+ * Returns the name of the dependency
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the version range
+ * @return the version range
+ */
+ public VersionRange getRange() {
+ return range;
+ }
+
+ /**
+ * Returns the installation path of this dependency
+ * @return the installation path;
+ */
+ public String getPath() {
+ StringBuilder b = new StringBuilder();
+ if (groupId.length() > 0) {
+ if (groupId.charAt(0) != '/') {
+ b.append(PackageId.ETC_PACKAGES_PREFIX);
+ }
+ b.append(groupId);
+ b.append("/");
+ }
+ b.append(name);
+ return b.toString();
+ }
+
+ /**
+ * Checks if the given package id matches this dependency specification.
+ * @param id the package id
+ * @return <code>true</code> if matches
+ */
+ public boolean matches(PackageId id) {
+ return groupId.equals(id.getGroup())
+ && name.equals(id.getName())
+ && range.isInRange(id.getVersion());
+ }
+
+ /**
+ * Returns a dependency from a string. if the given id is null or an
+ * empty string, <code>null</code> is returned.
+ * @param str the string
+ * @return the dependency
+ */
+ public static PackageDependency fromString(String str) {
+ if (str == null || str.length() == 0) {
+ return null;
+ }
+ String[] segs = str.split(":");
+ String name;
+ String groupId = "";
+ String range = null;
+ if (segs.length == 1) {
+ name = segs[0];
+ // be backward compatible, respect group in name
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0) {
+ groupId = name.substring(0, idx);
+ name = name.substring(idx + 1);
+ }
+ } else if (segs.length == 2) {
+ groupId = segs[0];
+ name = segs[1];
+ boolean isVersion = true;
+ if (name.length() > 0) {
+ char c = name.charAt(0);
+ isVersion = Character.isDigit(c) || c == '[' || c == ')';
+ }
+ // be backward compatible, respect group in name
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0 && groupId.length() == 0) {
+ groupId = name.substring(0, idx);
+ name = name.substring(idx + 1);
+ } else if ((idx = groupId.lastIndexOf('/')) >=0 && isVersion) {
+ groupId = segs[0].substring(0, idx);
+ name = segs[0].substring(idx + 1);
+ range = segs[1];
+ }
+ } else {
+ groupId = segs[0];
+ name = segs[1];
+ range = segs[2];
+ }
+ return new PackageDependency(groupId, name, range == null ? null : VersionRange.fromString(range));
+ }
+
+ /**
+ * Parses a string serialization of dependency references generated by
+ * {@link #toString(PackageDependency...)})}
+ *
+ * @param str serialized string
+ * @return array of dependency references
+ */
+ public static PackageDependency[] parse(String str) {
+ List<PackageDependency> deps = new ArrayList<PackageDependency>();
+ boolean inRange = false;
+ int start = 0;
+ boolean wasSeg = false;
+ for (int i=0; i<str.length(); i++) {
+ char c = str.charAt(i);
+ if (c == ',') {
+ if (!inRange) {
+ deps.add(PackageDependency.fromString(str.substring(start, i)));
+ start = i + 1;
+ }
+ } else if (c == '[' || c == '(') {
+ if (wasSeg) {
+ inRange = true;
+ }
+ } else if (c == ']' || c == ')') {
+ inRange = false;
+ }
+ wasSeg = c == ':';
+ }
+ if (start < str.length()) {
+ deps.add(PackageDependency.fromString(str.substring(start)));
+ }
+ return deps.toArray(new PackageDependency[deps.size()]);
+ }
+
+ /**
+ * Returns dependencies from the given strings.
+ * @param str the strings
+ * @return the dependencies
+ */
+ public static PackageDependency[] fromString(String ... str) {
+ PackageDependency[] ret = new PackageDependency[str.length];
+ for (int i=0; i<str.length;i++) {
+ ret[i] = PackageDependency.fromString(str[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Returns a string representation from given dependencies.
+ * @param deps the dependencies
+ * @return the strings
+ */
+ public static String toString(PackageDependency ... deps) {
+ String delim = "";
+ StringBuilder b = new StringBuilder();
+ for (PackageDependency dep: deps) {
+ b.append(delim).append(dep);
+ delim=",";
+ }
+ return b.toString();
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj ||
+ obj instanceof PackageDependency && str.equals(obj.toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return str.hashCode();
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageId.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageId.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageId.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/PackageId.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,413 @@
+/*
+ * 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;
+
+/**
+ * <code>PackageId</code> provides the basic metrics for identifying a package.
+ * A package id consists of a group id, a name and a version.
+ * the group is a relative path, eg: "day/cq5/hotfix", the name and the version
+ * can be of any format.
+ */
+public class PackageId implements Comparable<PackageId> {
+
+ public static final String ETC_PACKAGES = "/etc/packages";
+
+ public static final String ETC_PACKAGES_PREFIX = "/etc/packages/";
+
+ public static final PackageId[] EMPTY = new PackageId[0];
+
+ private final String group;
+
+ private final String name;
+
+ private final Version version;
+
+ private final String str;
+
+ private final boolean fromPath;
+
+ /**
+ * Creates a new package id
+ * @param path path of the package
+ */
+ public PackageId(String path) {
+ fromPath = true;
+ path = path.trim();
+ int idx = path.lastIndexOf('.');
+ if (idx > 0) {
+ String ext = path.substring(idx);
+ if (ext.equalsIgnoreCase(".zip") || ext.equalsIgnoreCase(".jar")) {
+ path = path.substring(0, idx);
+ }
+ }
+ idx = path.lastIndexOf('/');
+ String name;
+ if (idx < 0) {
+ name = path;
+ this.group = "";
+ } else {
+ name = path.substring(idx + 1);
+ String grp = path.substring(0, idx);
+ if (grp.equals(ETC_PACKAGES)) {
+ grp = "";
+ } else if (grp.startsWith(ETC_PACKAGES_PREFIX)) {
+ grp = grp.substring(ETC_PACKAGES_PREFIX.length());
+ } else if (grp.startsWith("/")) {
+ grp = grp.substring(1);
+ }
+ this.group = grp;
+ }
+ // check if name contains a version
+ String[] segs = name.split("-");
+ int i=segs.length-1;
+ while (i>0) {
+ try {
+ // accept numbers < 1000 (hotfix case)
+ if (Integer.parseInt(segs[i]) >= 1000) {
+ break;
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ // check if starts with a letter'
+ if (Character.isJavaIdentifierStart(segs[i].charAt(0))) {
+ // then need a digit
+ if (segs[i].length() == 1 || !Character.isDigit(segs[i].charAt(1)) && !segs[i].equals("SNAPSHOT")) {
+ break;
+ }
+ }
+ i--;
+ }
+ if (i == segs.length-1) {
+ this.name = name;
+ version = Version.EMPTY;
+ } else {
+ StringBuilder str = new StringBuilder();
+ for (int j = 0; j<= i; j++) {
+ if (j > 0) {
+ str.append('-');
+ }
+ str.append(segs[j]);
+ }
+ this.name = str.toString();
+ str.setLength(0);
+ for (int j = i+1; j<segs.length; j++) {
+ if (j > i+1) {
+ str.append('-');
+ }
+ str.append(segs[j]);
+ }
+ this.version = Version.create(str.toString());
+ }
+ this.str = getString(group, this.name, version);
+
+ }
+ /**
+ * Creates a new package id
+ * @param path path of the package
+ * @param version version of the package
+ */
+ public PackageId(String path, String version) {
+ this(path, Version.create(version));
+ }
+
+ /**
+ * Creates a new package id
+ * @param path path of the package
+ * @param version version of the package
+ */
+ public PackageId(String path, Version version) {
+ fromPath = true;
+ path = path.trim();
+ int idx = path.lastIndexOf('.');
+ if (idx > 0) {
+ String ext = path.substring(idx);
+ if (ext.equalsIgnoreCase(".zip") || ext.equalsIgnoreCase(".jar")) {
+ path = path.substring(0, idx);
+ }
+ }
+ if (version != null && path.endsWith('-'+version.toString())) {
+ path = path.substring(0, path.length() - version.toString().length() - 1);
+ }
+ idx = path.lastIndexOf('/');
+ if (idx < 0) {
+ this.name = path;
+ this.group = "";
+ } else {
+ this.name = path.substring(idx + 1);
+ String grp = path.substring(0, idx);
+ if (grp.equals(ETC_PACKAGES)) {
+ grp = "";
+ } else if (grp.startsWith(ETC_PACKAGES_PREFIX)) {
+ grp = grp.substring(ETC_PACKAGES_PREFIX.length());
+ } else if (grp.startsWith("/")) {
+ grp = grp.substring(1);
+ }
+ this.group = grp;
+ }
+ // sanitize version
+ if (version == null || version.toString().length() == 0) {
+ version = Version.EMPTY;
+ }
+ this.version = version;
+ this.str = getString(group, name, version);
+ }
+
+ /**
+ * Creates a new package id
+ * @param group group id
+ * @param name name
+ * @param version version
+ */
+ public PackageId(String group, String name, String version) {
+ this(group, name, Version.create(version));
+ }
+
+ /**
+ * Creates a new package id
+ * @param group group id
+ * @param name name
+ * @param version version
+ */
+ public PackageId(String group, String name, Version version) {
+ fromPath = false;
+ // validate group
+ if (group.equals(ETC_PACKAGES)) {
+ group = "";
+ } else if (group.startsWith(ETC_PACKAGES_PREFIX)) {
+ group = group.substring(ETC_PACKAGES_PREFIX.length());
+ } else if (group.startsWith("/")) {
+ group = group.substring(1);
+ }
+ this.group = group;
+ this.name = name;
+ this.version = version == null ? Version.EMPTY : version;
+ this.str = getString(this.group, name, this.version);
+ }
+
+ /**
+ * Returns a package id from a id string. if the given id is null or an
+ * empty string, <code>null</code> is returned.
+ * @param str the string
+ * @return the package id
+ */
+ public static PackageId fromString(String str) {
+ if (str == null || str.length() == 0) {
+ return null;
+ }
+ String[] segs = str.split(":");
+ if (segs.length == 1) {
+ return new PackageId("", segs[0], "");
+ } else if (segs.length == 2) {
+ return new PackageId(segs[0], segs[1], "");
+ } else {
+ return new PackageId(segs[0], segs[1], segs[2]);
+ }
+ }
+
+ /**
+ * Returns an array of package id from strings
+ * @param str the strings
+ * @return the array of package ids
+ */
+ public static PackageId[] fromString(String ... str) {
+ PackageId[] ret = new PackageId[str.length];
+ for (int i=0; i<str.length; i++) {
+ ret[i] = PackageId.fromString(str[i]);
+ }
+ return ret;
+ }
+
+ /**
+ * Creates a comma separated list of id strings.
+ * @param packs the ids
+ * @return the string
+ */
+ public static String toString(PackageId ... packs) {
+ String delim = "";
+ StringBuilder b = new StringBuilder();
+ for (PackageId pack: packs) {
+ b.append(delim).append(pack);
+ delim=",";
+ }
+ return b.toString();
+ }
+
+ /**
+ * Returns the path of this package
+ * @return the path of this package
+ * @deprecated use {@link #getInstallationPath()} instead
+ */
+ public String getPath() {
+ return getInstallationPath();
+ }
+
+ /**
+ * Checks if this definition was constructed from a path, rather from a
+ * group and name.
+ * @return <code>true</code> if constructed from path.
+ *
+ * @since 2.2.26
+ */
+ public boolean isFromPath() {
+ return fromPath;
+ }
+
+ /**
+ * Returns the path of this package. please note that since 2.3 this also
+ * includes the version, but never the extension (.zip).
+ *
+ * @return the path of this package
+ * @since 2.2
+ */
+ public String getInstallationPath() {
+ StringBuilder b = new StringBuilder(ETC_PACKAGES_PREFIX);
+ if (group.length() > 0) {
+ b.append(group);
+ b.append("/");
+ }
+ b.append(name);
+ if (version.toString().length() > 0) {
+ b.append("-").append(version);
+ }
+ return b.toString();
+ }
+
+ /**
+ * Returns the group id of this package
+ * @return the group id;
+ * @deprecated use {@link #getGroup()} instead.
+ */
+ public String getGroupId() {
+ return group;
+ }
+
+ /**
+ * Returns the group id of this package
+ * @return the group id;
+ * @since 2.2
+ */
+ public String getGroup() {
+ return group;
+ }
+
+ /**
+ * Returns the name of this package which is the last segment of the path.
+ * @return the name of this package.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the version of this package or and empty string if n/a.
+ * @return the version of this package
+ * @since 2.0
+ */
+ public String getVersionString() {
+ return version.toString();
+ }
+
+ /**
+ * Returns a download name in the form
+ * <code>name [ "-" version ] ".zip"</code>
+ * @return the download name
+ * @since 2.0
+ */
+ public String getDownloadName() {
+ StringBuilder str = new StringBuilder(name);
+ if (version.toString().length() > 0) {
+ str.append("-").append(version);
+ }
+ str.append(".zip");
+ return str.toString();
+ }
+
+ /**
+ * Returns the version of this package or <code>null</code> if n/a.
+ * @return the version of this package
+ */
+ public Version getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns a string representation of this id
+ */
+ @Override
+ public String toString() {
+ return str;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return this == o ||
+ o instanceof PackageId && str.equals(o.toString());
+
+ }
+
+ @Override
+ public int hashCode() {
+ return str.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Compares this id with the given one.
+ */
+ public int compareTo(PackageId o) {
+ int comp = group.compareTo(o.getGroup());
+ if (comp != 0) {
+ return comp;
+ }
+ comp = name.compareTo(o.getName());
+ if (comp != 0) {
+ return comp;
+ }
+ return version.compareTo(o.getVersion());
+ }
+
+ /**
+ * Internally get the string representation, colon separated.
+ * @param group group name
+ * @param name name
+ * @param version version
+ * @return string version
+ */
+ private static String getString(String group, String name, Version version) {
+ return getString(group, name, version == null ? "" : version.toString());
+ }
+
+ /**
+ * Internally get the string representation, colon separated.
+ * @param group group name
+ * @param name name
+ * @param version version
+ * @return string version
+ */
+ private static String getString(String group, String name, String version) {
+ StringBuilder b = new StringBuilder();
+ b.append(group).append(':');
+ b.append(name);
+ if (version.length() > 0) {
+ b.append(':').append(version);
+ }
+ return b.toString();
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/SubPackage.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/SubPackage.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/SubPackage.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/SubPackage.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,158 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
+import org.apache.maven.project.MavenProject;
+
+import org.apache.jackrabbit.filevault.maven.packaging.impl.StringFilterSet;
+
+/**
+ * The <code>Embedded</code> class represents an embedded artifact dependency
+ * from the project descriptor. Such an embedding is declared in
+ * <code><embedded></code> elements inside the list style
+ * <code><embeddeds></code> element as follows:
+ *
+ * <pre>
+ * <embedded>
+ * <groupId>artifact.groupId.pattern</groupId>
+ * <artifactId>artifact.artifactId.pattern</artifactId>
+ * <scope>compile</scope>
+ * <type>jar</type>
+ * <classifier>sources</classifier>
+ * <filter>true</filter>
+ * </embedded>
+ * </pre>
+ *
+ * @since 0.8
+ */
+public class SubPackage {
+
+ /**
+ * A group filter string, consisted of one or several comma separated patterns.
+ * @parameter type="String"
+ */
+ private final StringFilterSet groupId = new StringFilterSet();
+
+ /**
+ * A artifact filter string, consisted of one or several comma separated patterns.
+ * @parameter type="String"
+ */
+ private final StringFilterSet artifactId = new StringFilterSet();
+
+ /**
+ * @parameter
+ */
+ private ScopeArtifactFilter scope;
+
+ /**
+ * @parameter
+ */
+ private String type;
+
+ /**
+ * @parameter
+ */
+ private String classifier;
+
+ /**
+ * If <code>true</code> a filter entry will be generated for all embedded artifacts.
+ * @parameter
+ */
+ private boolean filter;
+
+ /**
+ * @parameter
+ */
+ private boolean excludeTransitive;
+
+ public void setGroupId(String groupId) {
+ this.groupId.addEntries(groupId);
+ }
+
+ public void setArtifactId(String artifactId) {
+ this.artifactId.addEntries(artifactId);
+ }
+
+ public void setScope(String scope) {
+ this.scope = new ScopeArtifactFilter(scope);
+ }
+
+ public void setAddFilter(boolean filter) {
+ this.filter = filter;
+ }
+
+ public boolean isFilter() {
+ return filter;
+ }
+
+ public void setExcludeTransitive(boolean excludeTransitive) {
+ this.excludeTransitive = excludeTransitive;
+ }
+
+ public boolean isExcludeTransitive() {
+ return excludeTransitive;
+ }
+
+ public List<Artifact> getMatchingArtifacts(final MavenProject project) {
+
+ // get artifacts depending on whether we exclude transitives or not
+ final Set deps;
+ if (excludeTransitive) {
+ // only direct dependencies, transitives excluded
+ deps = project.getDependencyArtifacts();
+ } else {
+ // all dependencies, transitives included
+ deps = project.getArtifacts();
+ }
+
+ final List<Artifact> matches = new ArrayList<Artifact>();
+ for (Object dep : deps) {
+ final Artifact artifact = (Artifact) dep;
+ if (groupId.contains(artifact.getGroupId())
+ && artifactId.contains(artifact.getArtifactId())
+ && (scope == null || scope.include(artifact))
+ && (type == null || type.equals(artifact.getType()))
+ && (classifier == null || classifier.equals(artifact.getClassifier()))) {
+ matches.add(artifact);
+ }
+ }
+ return matches;
+ }
+
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Sub Packages: ");
+ if (groupId != null) {
+ builder.append("groupId=").append(groupId).append(",");
+ }
+ if (artifactId != null) {
+ builder.append("artifactId=").append(artifactId).append(",");
+ }
+ if (scope != null) {
+ builder.append("scope=").append(scope).append(",");
+ }
+ builder.append("filter=").append(filter);
+ builder.append(",excludeTransitive=").append(excludeTransitive);
+ return builder.toString();
+ }
+}
Added: jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/VaultMojo.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/VaultMojo.java?rev=1808412&view=auto
==============================================================================
--- jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/VaultMojo.java (added)
+++ jackrabbit/commons/filevault-package-maven-plugin/trunk/plugin/src/main/java/org/apache/jackrabbit/filevault/maven/packaging/VaultMojo.java Fri Sep 15 06:24:37 2017
@@ -0,0 +1,854 @@
+/*
+ * 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.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.annotation.Nonnull;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.archiver.ManifestConfiguration;
+import org.apache.maven.archiver.MavenArchiveConfiguration;
+import org.apache.maven.archiver.MavenArchiver;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+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.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.codehaus.plexus.archiver.ArchiveEntry;
+import org.codehaus.plexus.archiver.FileSet;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+
+import aQute.bnd.header.Attrs;
+import aQute.bnd.header.Parameters;
+import aQute.bnd.osgi.Processor;
+import org.apache.jackrabbit.filevault.maven.packaging.impl.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.filevault.maven.packaging.impl.PackageType;
+import org.apache.jackrabbit.filevault.maven.packaging.impl.PathFilterSet;
+import org.apache.jackrabbit.filevault.maven.packaging.impl.PlatformNameFormat;
+
+import static org.codehaus.plexus.archiver.util.DefaultFileSet.fileSet;
+
+/**
+ * Build a content package.
+ */
+@Mojo(
+ name = "package",
+ defaultPhase = LifecyclePhase.PACKAGE,
+ requiresDependencyResolution = ResolutionScope.COMPILE
+)
+public class VaultMojo extends AbstractEmbeddedsMojo {
+
+ private static final String JCR_ROOT = "jcr_root/";
+
+ private static final String VAULT_DIR = "META-INF/vault";
+
+ public static final String PROPERTIES_FILE = VAULT_DIR + "/properties.xml";
+
+ public static final String FILTER_FILE = VAULT_DIR + "/filter.xml";
+
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ private static final String PACKAGE_TYPE = "zip";
+
+ private static final String PACKAGE_EXT = "." + PACKAGE_TYPE;
+
+ public static final String MF_KEY_PACKAGE_TYPE = "Content-Package-Type";
+
+ public static final String MF_KEY_PACKAGE_ID = "Content-Package-Id";
+
+ private static final String MF_KEY_PACKAGE_DEPENDENCIES = "Content-Package-Dependencies";
+
+ public static final String MF_KEY_PACKAGE_ROOTS = "Content-Package-Roots";
+
+ private static final String MF_KEY_PACKAGE_DESC = "Content-Package-Description";
+
+ private static final String MF_KEY_IMPORT_PACKAGE = "Import-Package";
+
+ @Component
+ private ArtifactHandlerManager artifactHandlerManager;
+
+ /**
+ * The directory containing the content to be packaged up into the content
+ * package.
+ */
+ @Parameter(
+ defaultValue = "${project.build.outputDirectory}",
+ required = true)
+ private File builtContentDirectory;
+
+ /**
+ * The directory containing the compiled classes to use to import analysis.
+ */
+ @Parameter(
+ defaultValue = "${project.build.outputDirectory}",
+ required = true)
+ private File classesDirectory;
+
+ /**
+ * The name of the generated package ZIP file without the ".zip" file
+ * extension.
+ */
+ @Parameter(
+ property = "vault.finalName",
+ defaultValue = "${project.build.finalName}",
+ required = true)
+ private String finalName;
+
+ /**
+ * Directory in which the built content package will be output.
+ */
+ @Parameter(
+ defaultValue="${project.build.directory}",
+ required = true)
+ private File outputDirectory;
+
+ /**
+ * Optional file that specifies the source of the workspace filter. The filters specified in the configuration
+ * and injected via emebedds or subpackages are merged into it.
+ */
+ @Parameter
+ private File filterSource;
+
+ /**
+ * The directory containing the content to be packaged up into the content
+ * package.
+ */
+ @Parameter(
+ defaultValue = "${project.build.directory}/vault-work",
+ required = true)
+ private File workDirectory;
+
+ /**
+ * The archive configuration to use. See <a
+ * href="http://maven.apache.org/shared/maven-archiver/index.html">the
+ * documentation for Maven Archiver</a>.
+ */
+ @Parameter
+ private MavenArchiveConfiguration archive;
+
+ /**
+ * Adds a path prefix to all resources useful for shallower source trees.
+ */
+ @Parameter(property = "vault.prefix")
+ private String prefix;
+
+ /**
+ * The groupId used for the generated content package. This will be part of
+ * the target installation path of the content package.
+ */
+ @Parameter(
+ property = "vault.group",
+ defaultValue="${project.groupId}",
+ required = true)
+ private String group;
+
+ /**
+ * The name of the content package
+ */
+ @Parameter(
+ property = "vault.name",
+ defaultValue="${project.artifactId}",
+ required = true)
+ private String name;
+
+ /**
+ * The version of the content package.
+ */
+ @Parameter(
+ property = "vault.version",
+ defaultValue = "${project.version}",
+ required = true)
+ private String version;
+
+ /**
+ * Defines whether the package requires root. This will become the
+ * <code>requiresRoot</code> property of the properties.xml file.
+ */
+ @Parameter(
+ property = "vault.requiresRoot",
+ defaultValue="false",
+ required = true)
+ private boolean requiresRoot;
+
+ /**
+ * Defines whether the package is allowed to contain index definitions. This will become the
+ * <code>allowIndexDefinitions</code> property of the properties.xml file.
+ */
+ @Parameter(
+ property = "vault.allowIndexDefinitions",
+ defaultValue="false",
+ required = true)
+ private boolean allowIndexDefinitions;
+
+ /**
+ * Controls if errors during dependency validation should fail the build.
+ */
+ @Parameter(
+ property = "vault.failOnDependencyErrors",
+ defaultValue="true",
+ required = true)
+ private boolean failOnDependencyErrors;
+
+ /**
+ * Defines the path under which the embedded bundles are placed. defaults to '/apps/bundles/install'
+ * @since 0.0.6
+ */
+ @Parameter(property = "vault.embeddedTarget")
+ private String embeddedTarget;
+
+ /**
+ * Defines the content package type. this is either 'application', 'content', 'container' or 'mixed'.
+ * If omitted, it is calculated automatically based on filter definitions. certain package types imply restrictions,
+ * for example, 'application' and 'content' packages are not allowed to contain sub packages or embedded bundles.
+ *
+ * @since 0.5.12
+ */
+ @Parameter(property = "vault.packageType")
+ private PackageType packageType;
+
+ /**
+ * Sets the package type.
+ * @param type the string representation of the package type
+ * @throws MojoFailureException if an error occurrs
+ */
+ public void setPackageType(String type) throws MojoFailureException {
+ try {
+ packageType = PackageType.valueOf(type.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw new MojoFailureException("Invalid package type specified: " + type +".\n" +
+ "Must be empty or one of 'application', 'content', 'container', 'mixed'");
+ }
+
+ }
+
+ /**
+ * Defines additional bundle dependency via the osgi import-package entry in the manifest.
+ * @since 0.5.12
+ */
+ @Parameter(property = "vault.importPackage")
+ private String importPackage;
+
+ /**
+ * Defines the content of the filter.xml file
+ */
+ @Parameter
+ private final DefaultWorkspaceFilter filters = new DefaultWorkspaceFilter();
+
+ /**
+ * Defines the list of dependencies
+ */
+ @Parameter
+ private Dependency[] dependencies = new Dependency[0];
+
+ /**
+ * Defines the packages that define the repository structure
+ */
+ @Parameter
+ private Dependency[] repositoryStructurePackages = new Dependency[0];
+
+ /**
+ * computed dependency string
+ * @see #computeDependencies()
+ */
+ private String dependenciesString;
+
+ /**
+ * Defines the list of sub packages.
+ */
+ @Parameter
+ private SubPackage[] subPackages = new SubPackage[0];
+
+ /**
+ * File to store the generated manifest snippet.
+ */
+ @Parameter(property = "vault.generatedImportPackage", defaultValue = "${project.build.directory}/vault-generated-import.txt")
+ private File generatedImportPackage;
+
+ /**
+ * Specifies additional properties to be set in the properties.xml file.
+ * These properties cannot overwrite the following predefined properties:
+ * <p>
+ * <table>
+ * <tr><td>group</td><td>Use <i>group</i> parameter to set</td></tr>
+ * <tr><td>name</td><td>Use <i>name</i> parameter to set</td></tr>
+ * <tr><td>version</td><td>Use <i>version</i> parameter to set</td></tr>
+ * <tr><td>groupId</td><td><i>groupId</i> of the Maven project descriptor</td></tr>
+ * <tr><td>artifactId</td><td><i>artifactId</i> of the Maven project descriptor</td></tr>
+ * <tr><td>dependencies</td><td>Use <i>dependencies</i> parameter to set</td></tr>
+ * <tr><td>createdBy</td><td>The value of the <i>user.name</i> system property</td></tr>
+ * <tr><td>created</td><td>The current system time</td></tr>
+ * <tr><td>requiresRoot</td><td>Use <i>requiresRoot</i> parameter to set</td></tr>
+ * <tr><td>allowIndexDefinitions</td><td>Use <i>allowIndexDefinitions</i> parameter to set</td></tr>
+ * <tr><td>packagePath</td><td>Automatically generated from the group and package name</td></tr>
+ * <tr><td>packageType</td><td>Set via the package type parameter</td></tr>
+ * </table>
+ */
+ @Parameter
+ private final Properties properties = new Properties();
+
+ /**
+ * Creates a fileset for the archiver
+ * @param directory the directory
+ * @param prefix the prefix
+ * @return the fileset
+ */
+ @Nonnull
+ private FileSet createFileSet(@Nonnull File directory, @Nonnull String prefix) {
+ return fileSet(directory).prefixed(prefix).includeExclude(null, null).includeEmptyDirs(true);
+ }
+
+ /**
+ * Executes this mojo
+ */
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if (prefix == null) {
+ prefix = "";
+ } else if (!prefix.endsWith("/")) {
+ prefix += "/";
+ }
+
+ final File vaultDir = new File(workDirectory, VAULT_DIR);
+ final File finalFile = new File(outputDirectory, finalName + PACKAGE_EXT);
+
+ try {
+ vaultDir.mkdirs();
+
+ Map<String, File> embeddedFiles = copyEmbeddeds();
+ embeddedFiles.putAll(copySubPackages());
+
+ File filterFile = new File(vaultDir, "filter.xml");
+
+ if (filterSource == null || !filterSource.exists()) {
+ // ignore filtersource
+ DefaultWorkspaceFilter oldFilter;
+ if (filterFile.exists()) {
+ oldFilter = new DefaultWorkspaceFilter();
+ oldFilter.load(filterFile);
+
+ // don't write filter if same
+ if (filters.getSourceAsString().equals(oldFilter.getSourceAsString())) {
+ filterFile = null;
+ } else {
+ filterFile = new File(vaultDir, "filter-plugin-generated.xml");
+ }
+ } else {
+ if (filters.getFilterSets().isEmpty() && prefix.length() > 0) {
+ addWorkspaceFilter(prefix);
+ }
+ }
+ } else {
+ getLog().info("Merging " + filterSource.getPath() + " with inlined filter specifications.");
+ DefaultWorkspaceFilter oldFilter = new DefaultWorkspaceFilter();
+ oldFilter.load(filterSource);
+ oldFilter.merge(filters);
+ filters.getFilterSets().clear();
+ filters.getFilterSets().addAll(oldFilter.getFilterSets());
+ if (filters.getFilterSets().isEmpty() && prefix.length() > 0) {
+ addWorkspaceFilter(prefix);
+ }
+ }
+
+ if (filterFile != null) {
+ FileUtils.fileWrite(filterFile.getAbsolutePath(), filters.getSourceAsString());
+ }
+
+ validatePackageType();
+ computeImportPackage();
+ computeDependencies();
+ if (packageType == PackageType.APPLICATION) {
+ validateDependencies();
+ } else {
+ getLog().info("Ignoring dependency validation due to non-application package type: " + packageType);
+ }
+
+ final Properties vaultProperties = computeProperties();
+ final FileOutputStream fos = new FileOutputStream(new File(vaultDir, "properties.xml"));
+ vaultProperties.storeToXML(fos, project.getName());
+
+ copyFile("/vault/config.xml", new File(vaultDir, "config.xml"));
+ copyFile("/vault/settings.xml", new File(vaultDir, "settings.xml"));
+
+ ContentPackageArchiver contentPackageArchiver = new ContentPackageArchiver();
+ contentPackageArchiver.setIncludeEmptyDirs(true);
+ contentPackageArchiver.addDirectory(workDirectory);
+
+ // include content from build only if it exists
+ if (builtContentDirectory.exists()) {
+ // See GRANITE-16348
+ // we want to build a list of all the root directories in the order they were specified in the filter
+ // but ignore the roots that don't point to a directory
+ List<PathFilterSet> filterSets = filters.getFilterSets();
+ if (filterSets.isEmpty()) {
+ contentPackageArchiver.addFileSet(createFileSet(builtContentDirectory, FileUtils.normalize(JCR_ROOT + prefix)));
+ } else {
+ for (PathFilterSet filterSet : filterSets) {
+ String relPath = PlatformNameFormat.getPlatformPath(filterSet.getRoot());
+ String rootPath = FileUtils.normalize(JCR_ROOT + prefix + relPath);
+
+ // CQ-4204625 skip embedded files, will be added later in the proper way
+ if (embeddedFiles.containsKey(rootPath)) {
+ continue;
+ }
+
+ // check for full coverage aggregate
+ File fullCoverage = new File(builtContentDirectory, relPath + ".xml");
+ if (fullCoverage.isFile()) {
+ rootPath = FileUtils.normalize(JCR_ROOT + prefix + relPath + ".xml");
+ contentPackageArchiver.addFile(fullCoverage, rootPath);
+ continue;
+ }
+
+ File rootDirectory = new File(builtContentDirectory, relPath);
+
+ // traverse the ancestors until we find a existing directory (see CQ-4204625)
+ while ((!rootDirectory.exists() || !rootDirectory.isDirectory())
+ && !builtContentDirectory.equals(rootDirectory)) {
+ rootDirectory = rootDirectory.getParentFile();
+ relPath = StringUtils.chomp(relPath, "/");
+ }
+
+ if (!builtContentDirectory.equals(rootDirectory)) {
+ rootPath = FileUtils.normalize(JCR_ROOT + prefix + relPath);
+ contentPackageArchiver.addFileSet(createFileSet(rootDirectory, rootPath + "/"));
+ }
+ }
+ }
+ }
+
+ for (Map.Entry<String, File> entry : embeddedFiles.entrySet()) {
+ contentPackageArchiver.addFile(entry.getValue(), entry.getKey());
+ }
+
+ //NPR-14102 - Automated check for index definition
+ if (!allowIndexDefinitions) {
+ FileValidator fileValidator = new FileValidator();
+ getLog().info("Scanning files for oak index definitions.");
+ for (ArchiveEntry entry: contentPackageArchiver.getFiles().values()) {
+ if (entry.getType() == ArchiveEntry.FILE) {
+ InputStream in = null;
+ try {
+ in = entry.getInputStream();
+ fileValidator.lookupIndexDefinitionInArtifact(in, entry.getName());
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+ }
+ if (fileValidator.isContainingIndexDef) {
+ getLog().error(fileValidator.getMessageWithPathsOfIndexDef());
+ throw new MojoExecutionException("Package should not contain index definitions, because 'allowIndexDefinitions=false'.");
+ }
+ }
+
+ MavenArchiver mavenArchiver = new MavenArchiver();
+ mavenArchiver.setArchiver(contentPackageArchiver);
+ mavenArchiver.setOutputFile(finalFile);
+ mavenArchiver.createArchive(null, project, getMavenArchiveConfiguration(vaultProperties));
+
+ // set the file for the project's artifact and ensure the
+ // artifact is correctly handled with the "zip" handler
+ // (workaround for MNG-1682)
+ final Artifact projectArtifact = project.getArtifact();
+ projectArtifact.setFile(finalFile);
+ projectArtifact.setArtifactHandler(artifactHandlerManager.getArtifactHandler(PACKAGE_TYPE));
+
+ } catch (Exception e) {
+ throw new MojoExecutionException(e.toString(), e);
+ }
+ }
+
+ /**
+ * Computes the dependency string.
+ */
+ private void computeDependencies() throws IOException {
+ if (dependencies.length > 0) {
+ dependenciesString = PackageDependency.toString(Dependency.resolve(project, getLog(), dependencies));
+ }
+ Dependency.resolve(project, getLog(), repositoryStructurePackages);
+ }
+
+ /**
+ * Checks if the filter roots of this package are covered by the dependencies and also checks for colliding roots
+ * in the dependencies.
+ */
+ private void validateDependencies() throws MojoExecutionException {
+ List<String> errors = new DependencyValidator()
+ .addDependencies(dependencies)
+ .addDependencies(repositoryStructurePackages)
+ .setFilters(filters)
+ .validate()
+ .getErrors();
+
+ if (errors.size() > 0) {
+ String msg = String.format("%d error(s) detected during dependency analysis.", errors.size());
+ if (failOnDependencyErrors) {
+ getLog().error(msg);
+ for (String error: errors) {
+ getLog().error(error);
+ }
+ throw new MojoExecutionException(msg);
+ }
+ getLog().warn(msg);
+ for (String error: errors) {
+ getLog().warn(error);
+ }
+ } else {
+ getLog().info("All dependencies satisfied.");
+ }
+ }
+
+ /**
+ * Computes the import-package definition from the given bundles if not provided by the project.
+ */
+ private void computeImportPackage() throws IOException {
+ TreeMap<String, Attrs> importParams = new TreeMap<String, Attrs>();
+ if (generatedImportPackage.exists()) {
+ String importPackageStr = FileUtils.fileRead(generatedImportPackage);
+ if (importPackageStr.length() > 0) {
+ importParams.putAll(new Parameters(importPackageStr));
+ }
+ }
+
+ // override computed patterns
+ if (importPackage != null) {
+ for (Map.Entry<String, Attrs> entry : new Parameters(importPackage).entrySet()) {
+ boolean delete = false;
+ String pkg = entry.getKey();
+ if ("-*".equals(pkg)) {
+ importParams.clear();
+ continue;
+ }
+ if (pkg.charAt(0) == '-') {
+ pkg = pkg.substring(1);
+ delete = true;
+ }
+ if (pkg.endsWith("*")) {
+ String pkgDot = pkg.substring(0, pkg.length() - 1);
+ if (!pkgDot.endsWith(".")) {
+ // matches both, the packages and sub packages
+ pkg = pkgDot;
+ pkgDot = pkg + ".";
+ }
+ Iterator<Map.Entry<String, Attrs>> iter = importParams.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<String, Attrs> e = iter.next();
+ String pkgName = e.getKey();
+ if (pkgName.equals(pkg) || pkgName.startsWith(pkgDot)) {
+ if (delete) {
+ iter.remove();
+ } else {
+ e.setValue(entry.getValue());
+ }
+ }
+ }
+ } else {
+ if (delete) {
+ importParams.remove(pkg);
+ } else {
+ importParams.put(pkg, entry.getValue());
+ }
+ }
+ }
+ }
+
+ importPackage = Processor.printClauses(importParams);
+ }
+
+ private MavenArchiveConfiguration getMavenArchiveConfiguration(Properties vaultProperties) throws IOException {
+ if (archive == null) {
+ archive = new MavenArchiveConfiguration();
+ archive.setManifest(new ManifestConfiguration());
+
+ archive.setAddMavenDescriptor(true);
+ archive.setCompress(true);
+ archive.setIndex(false);
+ archive.getManifest().setAddDefaultSpecificationEntries(false);
+ archive.getManifest().setAddDefaultImplementationEntries(true);
+
+ PackageId id = new PackageId(group, name, version);
+ archive.addManifestEntry(MF_KEY_PACKAGE_TYPE, packageType.name().toLowerCase());
+ archive.addManifestEntry(MF_KEY_PACKAGE_ID, id.toString());
+ archive.addManifestEntry(MF_KEY_PACKAGE_DESC, vaultProperties.getProperty("description", ""));
+ if (dependenciesString != null && dependenciesString.length() > 0) {
+ archive.addManifestEntry(MF_KEY_PACKAGE_DEPENDENCIES, dependenciesString);
+ }
+ // be sure to avoid duplicates
+ Set<String> rts = new TreeSet<String>();
+ for (PathFilterSet p: filters.getFilterSets()) {
+ rts.add(p.getRoot());
+ }
+ String[] roots = rts.toArray(new String[rts.size()]);
+ Arrays.sort(roots);
+ archive.addManifestEntry(MF_KEY_PACKAGE_ROOTS, StringUtils.join(roots, ","));
+
+ if (StringUtils.isNotEmpty(importPackage)) {
+ archive.addManifestEntry(MF_KEY_IMPORT_PACKAGE, StringUtils.deleteWhitespace(importPackage));
+ }
+ }
+
+ return archive;
+ }
+
+ private Properties computeProperties() {
+ final Properties props = new Properties();
+
+ // find the description of the content package (bug #30546)
+ // this is allowed to be overwritten by the properties map (GRANITE-1527)
+ String description = project.getDescription();
+ if (description == null) {
+ description = project.getName();
+ if (description == null) {
+ description = project.getArtifactId();
+ }
+ }
+ props.put("description", description);
+
+ // add all user defined properties
+ // before the rest of the properties to prevent user
+ // overwriting of predefined properties
+ // (see JavaDoc of properties field for list)
+
+ // but make sure, that we don't have null values in there
+ for (Object o : properties.keySet()) {
+ if (properties.get(o) == null) {
+ properties.put(o, "");
+ }
+ }
+
+ props.putAll(properties);
+
+ // package descriptor properties
+ props.put("group", group);
+ props.put("name", name);
+ props.put("version", version);
+
+ // maven artifact identification
+ props.put("groupId", project.getGroupId());
+ props.put("artifactId", project.getArtifactId());
+
+ // dependencies
+ if (dependenciesString != null && dependenciesString.length() > 0) {
+ props.put("dependencies", dependenciesString);
+ }
+
+ // creation stamp
+ if (!props.containsKey("createdBy")) {
+ props.put("createdBy", System.getProperty("user.name"));
+ }
+ props.put("created", DATE_FORMAT.format(new Date()));
+
+ // configurable properties
+ props.put("requiresRoot", String.valueOf(requiresRoot));
+ props.put("allowIndexDefinitions", String.valueOf(allowIndexDefinitions));
+ props.put("packageType", packageType.name().toLowerCase());
+ return props;
+ }
+
+ private void copyFile(String source, File target) throws IOException {
+
+ // nothing to do if the file exists
+ if (target.exists()) {
+ return;
+ }
+
+ target.getParentFile().mkdirs();
+
+ InputStream ins = getClass().getResourceAsStream(source);
+ if (ins != null) {
+ OutputStream out = null;
+ try {
+ out = new FileOutputStream(target);
+ IOUtil.copy(ins, out);
+ } finally {
+ IOUtil.close(ins);
+ IOUtil.close(out);
+ }
+ }
+ }
+
+ private Map<String, File> copyEmbeddeds() throws IOException, MojoFailureException {
+ Map<String, File> fileMap = new HashMap<String, File>();
+ for (Embedded emb : embeddeds) {
+ final List<Artifact> artifacts = emb.getMatchingArtifacts(project);
+ if (artifacts.isEmpty()) {
+ if (failOnMissingEmbed) {
+ throw new MojoFailureException("Embedded artifact specified " + emb + ", but no matching dependency artifact found. Add the missing dependency or fix the embed definition.");
+ } else {
+ getLog().warn("No matching artifacts for " + emb);
+ continue;
+ }
+ }
+ if (emb.getDestFileName() != null && artifacts.size() > 1) {
+ getLog().warn("destFileName defined but several artifacts match for " + emb);
+ }
+
+ String targetPath = emb.getTarget();
+ if (targetPath == null) {
+ targetPath = embeddedTarget;
+ if (targetPath == null) {
+ final String loc = (prefix.length() == 0)
+ ? "/apps/"
+ : prefix;
+ targetPath = loc + "bundles/install/";
+ getLog().info("No target path set on " + emb + "; assuming default " + targetPath);
+ }
+ }
+ targetPath = makeAbsolutePath(targetPath);
+
+ targetPath = JCR_ROOT + targetPath;
+ targetPath = FileUtils.normalize(targetPath);
+
+ getLog().info("Embedding --- " + emb + " ---");
+ for (final Artifact artifact : artifacts) {
+ final File source = artifact.getFile();
+ String destFileName = emb.getDestFileName();
+
+ // todo: add support for patterns
+ if (destFileName == null) {
+ destFileName = source.getName();
+ }
+ final String targetPathName = targetPath + destFileName;
+ final String targetNodePathName = targetPathName.substring(JCR_ROOT.length() - 1);
+
+ fileMap.put(targetPathName, source);
+ getLog().info(String.format("Embedding %s (from %s) -> %s", artifact.getId(), source.getAbsolutePath(), targetPathName));
+
+ if (emb.isFilter()) {
+ addWorkspaceFilter(targetNodePathName);
+ }
+ }
+ }
+ return fileMap;
+ }
+
+ private Map<String, File> copySubPackages() throws IOException {
+ Map<String, File> fileMap = new HashMap<String, File>();
+ for (SubPackage pack : subPackages) {
+ final List<Artifact> artifacts = pack.getMatchingArtifacts(project);
+ if (artifacts.isEmpty()) {
+ getLog().warn("No matching artifacts for " + pack);
+ continue;
+ }
+
+ // get the package path
+ getLog().info("Embedding --- " + pack + " ---");
+ for (Artifact artifact : artifacts) {
+ final File source = artifact.getFile();
+
+ // load properties
+ ZipFile zip = null;
+ InputStream in = null;
+ Properties props = new Properties();
+ try {
+ zip = new ZipFile(source, ZipFile.OPEN_READ);
+ ZipEntry e = zip.getEntry("META-INF/vault/properties.xml");
+ if (e == null) {
+ getLog().error("Package does not contain properties.xml");
+ throw new IOException("properties.xml missing");
+ }
+ in = zip.getInputStream(e);
+ props.loadFromXML(in);
+ } finally {
+ IOUtil.close(in);
+ if (zip != null) {
+ zip.close();
+ }
+ }
+ PackageId pid = new PackageId(
+ props.getProperty("group"),
+ props.getProperty("name"),
+ props.getProperty("version")
+ );
+ final String targetNodePathName = pid.getInstallationPath() + ".zip";
+ final String targetPathName = "jcr_root" + targetNodePathName;
+
+ fileMap.put(targetPathName, source);
+ getLog().info("Embedding " + artifact.getId() + " -> " + targetPathName);
+ if (pack.isFilter()) {
+ addWorkspaceFilter(targetNodePathName);
+ }
+ }
+ }
+ return fileMap;
+ }
+
+ private void addWorkspaceFilter(final String filterRoot) {
+ filters.add(new PathFilterSet(filterRoot));
+ }
+
+ private String makeAbsolutePath(final String relPath) {
+ final String absPath;
+ if (!relPath.startsWith("/")) {
+ absPath = ((prefix.length() == 0) ? "/" : prefix) + relPath;
+ getLog().info("Relative path resolved to " + absPath);
+ } else {
+ absPath = relPath;
+ }
+
+ return absPath;
+ }
+
+ private void validatePackageType() throws MojoFailureException {
+ if (packageType == null) {
+ // auto detect...
+ boolean hasApps = false;
+ boolean hasOther = false;
+ for (PathFilterSet p: filters.getFilterSets()) {
+ String root = p.getRoot();
+ if ("/apps".equals(root) || root.startsWith("/apps/") || "/libs".equals(root) || root.startsWith("/libs/")) {
+ hasApps = true;
+ } else {
+ hasOther = true;
+ }
+ }
+ if (hasApps && !hasOther) {
+ packageType = PackageType.APPLICATION;
+ } else if (hasOther && !hasApps) {
+ packageType = PackageType.CONTENT;
+ } else {
+ packageType = PackageType.MIXED;
+ }
+ }
+ }
+
+}