Skip to content

Commit b8fba0a

Browse files
committed
conga-aem-maven-plugin: Auto-generate package dependencies for 'all-package' goal
1 parent 0fee5d2 commit b8fba0a

9 files changed

Lines changed: 327 additions & 23 deletions

File tree

tooling/conga-aem-maven-plugin/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@
104104
<scope>compile</scope>
105105
</dependency>
106106

107+
<dependency>
108+
<groupId>org.slf4j</groupId>
109+
<artifactId>slf4j-simple</artifactId>
110+
<scope>test</scope>
111+
</dependency>
112+
<dependency>
113+
<groupId>org.xmlunit</groupId>
114+
<artifactId>xmlunit-core</artifactId>
115+
<version>2.6.3</version>
116+
<scope>test</scope>
117+
</dependency>
118+
<dependency>
119+
<groupId>org.xmlunit</groupId>
120+
<artifactId>xmlunit-legacy</artifactId>
121+
<version>2.6.3</version>
122+
<scope>test</scope>
123+
</dependency>
107124
<dependency>
108125
<groupId>org.zeroturnaround</groupId>
109126
<artifactId>zt-zip</artifactId>

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/AllPackageMojo.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ public final class AllPackageMojo extends AbstractMojo {
6060
@Parameter(property = "conga.allPackage.group", required = true)
6161
private String group;
6262

63+
/**
64+
* Automatically generate dependencies between content packages based on file order in CONGA configuration.
65+
*/
66+
@Parameter(property = "conga.allPackage.autoDependencies", defaultValue = "true")
67+
private boolean autoDependencies;
68+
6369
/**
6470
* Selected environments to generate. It's only allowed to define a single environment for this mojo,
6571
* but to be compatible with the other CONGA plugins it's uses the same semantic for defining multiple
@@ -158,8 +164,12 @@ private void buildAllPackage(File nodeDir, ModelParser modelParser) throws MojoF
158164

159165
List<ContentPackageFile> contentPackages = modelParser.getContentPackagesForNode(nodeDir);
160166
File targetFile = new File(target, packageName + ".zip");
167+
168+
AllPackageBuilder builder = new AllPackageBuilder(targetFile, groupName, packageName)
169+
.autoDependencies(this.autoDependencies);
170+
161171
try {
162-
if (AllPackageBuilder.build(targetFile, contentPackages, groupName, packageName)) {
172+
if (builder.build(contentPackages)) {
163173
getLog().info("Generated " + getCanonicalPath(targetFile));
164174
}
165175
else {

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/allpackage/AllPackageBuilder.java

Lines changed: 132 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,26 @@
2121

2222
import static io.wcm.devops.conga.plugins.aem.maven.allpackage.RunModeUtil.RUNMODE_AUTHOR;
2323
import static io.wcm.devops.conga.plugins.aem.maven.allpackage.RunModeUtil.RUNMODE_PUBLISH;
24+
import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_DEPENDENCIES;
2425

2526
import java.io.File;
27+
import java.io.FileOutputStream;
2628
import java.io.IOException;
29+
import java.io.InputStream;
30+
import java.util.Enumeration;
2731
import java.util.List;
32+
import java.util.Properties;
2833
import java.util.stream.Collectors;
34+
import java.util.zip.ZipEntry;
35+
import java.util.zip.ZipFile;
36+
import java.util.zip.ZipOutputStream;
37+
38+
import org.apache.commons.io.FileUtils;
39+
import org.apache.commons.io.IOUtils;
40+
import org.apache.commons.lang3.StringUtils;
41+
import org.apache.jackrabbit.vault.packaging.Dependency;
42+
import org.apache.jackrabbit.vault.packaging.DependencyUtil;
43+
import org.apache.jackrabbit.vault.packaging.VersionRange;
2944

3045
import io.wcm.devops.conga.plugins.aem.maven.model.ContentPackageFile;
3146
import io.wcm.tooling.commons.contentpackagebuilder.ContentPackage;
@@ -37,21 +52,39 @@
3752
*/
3853
public final class AllPackageBuilder {
3954

40-
private AllPackageBuilder() {
41-
// static methods only
42-
}
55+
private final File targetFile;
56+
private final String groupName;
57+
private final String packageName;
58+
private boolean autoDependencies;
4359

4460
/**
45-
* Build "all" content package.
4661
* @param targetFile Target file
47-
* @param contentPackages Content packages (invalid will be filtered out)
4862
* @param groupName Group name
4963
* @param packageName Package name
64+
*/
65+
public AllPackageBuilder(File targetFile, String groupName, String packageName) {
66+
this.targetFile = targetFile;
67+
this.groupName = groupName;
68+
this.packageName = packageName;
69+
}
70+
71+
/**
72+
* @param value Automatically generate dependencies between content packages based on file order in CONGA
73+
* configuration.
74+
* @return this
75+
*/
76+
public AllPackageBuilder autoDependencies(boolean value) {
77+
this.autoDependencies = value;
78+
return this;
79+
}
80+
81+
/**
82+
* Build "all" content package.
83+
* @param contentPackages Content packages (invalid will be filtered out)
5084
* @return true if "all" package was generated, false if not valid package was found.
85+
* @throws IOException I/O exception
5186
*/
52-
public static boolean build(File targetFile,
53-
List<ContentPackageFile> contentPackages,
54-
String groupName, String packageName) throws IOException {
87+
public boolean build(List<ContentPackageFile> contentPackages) throws IOException {
5588

5689
// collect AEM content packages for this node
5790
List<ContentPackageFile> validContentPackages = contentPackages.stream()
@@ -72,12 +105,22 @@ public static boolean build(File targetFile,
72105
builder.filter(new PackageFilter(rootPath));
73106

74107
// build content package
108+
ContentPackageFile previousPkg = null;
75109
try (ContentPackage contentPackage = builder.build(targetFile)) {
76110
for (ContentPackageFile pkg : validContentPackages) {
77111
String path = buildPackagePath(pkg, rootPath);
78-
contentPackage.addFile(path, pkg.getFile());
112+
if (autoDependencies && previousPkg != null) {
113+
// wire previous package in package dependency
114+
addFileWithDependency(contentPackage, path, pkg, previousPkg);
115+
}
116+
else {
117+
// add package file directly
118+
contentPackage.addFile(path, pkg.getFile());
119+
}
120+
previousPkg = pkg;
79121
}
80122
}
123+
81124
return true;
82125
}
83126

@@ -113,4 +156,84 @@ else if (RunModeUtil.isOnlyPublish(pkg)) {
113156
return rootPath + "/" + pkg.getPackageType() + "/install" + runModeSuffix + "/" + pkg.getFile().getName();
114157
}
115158

159+
/**
160+
* Rewrite content package ZIP file while adding to "all" package:
161+
* Add dependency to previous package in CONGA configuration file oder.
162+
* @param contentPackage Target content page
163+
* @param path Path in target content package
164+
* @param pkg Package to add
165+
* @param previousPkg Previous package to get dependency information from
166+
* @throws IOException I/O error
167+
*/
168+
private static void addFileWithDependency(ContentPackage contentPackage, String path,
169+
ContentPackageFile pkg, ContentPackageFile previousPkg) throws IOException {
170+
171+
// create temp zip file to create rewritten copy of package
172+
File tempFile = File.createTempFile("pkg", ".zip");
173+
174+
// open original content package
175+
try (ZipFile zipFileIn = new ZipFile(pkg.getFile())) {
176+
177+
// iterate through entries and write them to the temp. zip file
178+
try (FileOutputStream fos = new FileOutputStream(tempFile);
179+
ZipOutputStream zipOut = new ZipOutputStream(fos)) {
180+
Enumeration<? extends ZipEntry> zipInEntries = zipFileIn.entries();
181+
while (zipInEntries.hasMoreElements()) {
182+
ZipEntry zipInEntry = zipInEntries.nextElement();
183+
ZipEntry zipOutEntry = new ZipEntry(zipInEntry.getName());
184+
if (!zipInEntry.isDirectory()) {
185+
zipOut.putNextEntry(zipOutEntry);
186+
if (StringUtils.equals(zipInEntry.getName(), "META-INF/vault/properties.xml")) {
187+
// if entry is properties.xml, update dependency information
188+
try (InputStream is = zipFileIn.getInputStream(zipInEntry)) {
189+
Properties props = new Properties();
190+
props.loadFromXML(is);
191+
updateDependencies(props, previousPkg);
192+
props.storeToXML(zipOut, null);
193+
}
194+
}
195+
else {
196+
// otherwise transfer the binary data 1:1
197+
try (InputStream is = zipFileIn.getInputStream(zipInEntry)) {
198+
IOUtils.copy(is, zipOut);
199+
}
200+
}
201+
zipOut.closeEntry();
202+
}
203+
}
204+
}
205+
206+
// add temp zip file to "all" content package
207+
contentPackage.addFile(path, tempFile);
208+
}
209+
finally {
210+
FileUtils.deleteQuietly(tempFile);
211+
}
212+
}
213+
214+
/**
215+
* Add dependency information to dependencies string in properties (if it does not exist already).
216+
* @param props Properties
217+
* @param dependencyFile Dependency package
218+
*/
219+
private static void updateDependencies(Properties props, ContentPackageFile dependencyFile) {
220+
String[] existingDepsStrings = StringUtils.split(props.getProperty(NAME_DEPENDENCIES), ",");
221+
Dependency[] existingDeps = null;
222+
if (existingDepsStrings != null && existingDepsStrings.length > 0) {
223+
existingDeps = Dependency.fromString(existingDepsStrings);
224+
}
225+
226+
Dependency newDependency = new Dependency(dependencyFile.getGroup(), dependencyFile.getName(),
227+
VersionRange.fromString(dependencyFile.getVersion()));
228+
Dependency[] deps;
229+
if (existingDeps != null) {
230+
deps = DependencyUtil.add(existingDeps, newDependency);
231+
}
232+
else {
233+
deps = new Dependency[] { newDependency };
234+
}
235+
236+
props.put(NAME_DEPENDENCIES, Dependency.toString(deps));
237+
}
238+
116239
}

tooling/conga-aem-maven-plugin/src/main/java/io/wcm/devops/conga/plugins/aem/maven/model/ContentPackageFile.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
*/
2020
package io.wcm.devops.conga.plugins.aem.maven.model;
2121

22+
import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_GROUP;
23+
import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_NAME;
2224
import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_PACKAGE_TYPE;
25+
import static org.apache.jackrabbit.vault.packaging.PackageProperties.NAME_VERSION;
2326

2427
import java.io.File;
2528
import java.util.Collections;
@@ -41,9 +44,14 @@ public final class ContentPackageFile {
4144
private final Integer delayAfterInstallSec;
4245
private final Integer httpSocketTimeoutSec;
4346

47+
private final String name;
48+
private final String group;
49+
private final String version;
4450
private final String packageType;
51+
4552
private final List<String> variants;
4653

54+
@SuppressWarnings("unchecked")
4755
ContentPackageFile(File file, Map<String, Object> fileData, Map<String, Object> roleData) {
4856
this.file = file;
4957

@@ -53,18 +61,17 @@ public final class ContentPackageFile {
5361
this.delayAfterInstallSec = (Integer)fileData.get("delayAfterInstallSec");
5462
this.httpSocketTimeoutSec = (Integer)fileData.get("httpSocketTimeoutSec");
5563

56-
this.packageType = getPackageType(fileData);
57-
this.variants = getVariants(roleData);
58-
}
59-
60-
@SuppressWarnings("unchecked")
61-
private static String getPackageType(Map<String, Object> fileData) {
6264
Map<String, Object> contentPackageProperties = (Map<String, Object>)fileData.get(
6365
ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY);
64-
if (contentPackageProperties != null) {
65-
return (String)contentPackageProperties.get(NAME_PACKAGE_TYPE);
66+
if (contentPackageProperties == null) {
67+
throw new IllegalArgumentException(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY + " missing.");
6668
}
67-
return null;
69+
this.name = (String)contentPackageProperties.get(NAME_NAME);
70+
this.group = (String)contentPackageProperties.get(NAME_GROUP);
71+
this.version = (String)contentPackageProperties.get(NAME_VERSION);
72+
this.packageType = (String)contentPackageProperties.get(NAME_PACKAGE_TYPE);
73+
74+
this.variants = getVariants(roleData);
6875
}
6976

7077
@SuppressWarnings("unchecked")
@@ -102,6 +109,18 @@ public Integer getHttpSocketTimeoutSec() {
102109
return this.httpSocketTimeoutSec;
103110
}
104111

112+
public String getName() {
113+
return this.name;
114+
}
115+
116+
public String getGroup() {
117+
return this.group;
118+
}
119+
120+
public String getVersion() {
121+
return this.version;
122+
}
123+
105124
public String getPackageType() {
106125
return this.packageType;
107126
}

0 commit comments

Comments
 (0)