2424import static io .wcm .devops .conga .plugins .aem .maven .allpackage .RunModeUtil .RUNMODE_PUBLISH ;
2525import static org .apache .jackrabbit .vault .packaging .PackageProperties .NAME_DEPENDENCIES ;
2626import static org .apache .jackrabbit .vault .packaging .PackageProperties .NAME_NAME ;
27+ import static org .apache .jackrabbit .vault .packaging .PackageProperties .NAME_PACKAGE_TYPE ;
2728
2829import java .io .File ;
29- import java .io .FileInputStream ;
3030import java .io .FileOutputStream ;
3131import java .io .IOException ;
3232import java .io .InputStream ;
6464
6565/**
6666 * Builds "all" package based on given set of content packages.
67+ * <p>
68+ * General concept:
69+ * </p>
70+ * <ul>
71+ * <li>Iterates through all content packages that are generated or collected by CONGA and contained in the
72+ * model.json</li>
73+ * <li>Enforces the order defined in CONGA by automatically adding dependencies to all packages reflecting the file
74+ * order in model.json</li>
75+ * <li>Because the dependency chain may be different for each runmode (author/publish), each package is added once for
76+ * each runmode (so usually twice, for author+publish)</li>
77+ * <li>To avoid conflicts with duplicate packages with different dependencies all package names are changed and a
78+ * runmode suffix (.author or .publish) is added, same applies to the install folder</li>
79+ * <li>To avoid problems with nested sub packages, the sub packages are extracted from the packages and treated in the
80+ * same way as other packages</li>
81+ * </ul>
6782 */
6883public final class AllPackageBuilder {
6984
@@ -134,7 +149,7 @@ private Log getLog() {
134149 * @param cloudManagerTarget Target environments/run modes the packages should be attached to
135150 * @throws IllegalArgumentException If and invalid package type is detected
136151 */
137- public void add (List <ContentPackageFile > contentPackages , Set <String > cloudManagerTarget ) {
152+ public void add (List <? extends ContentPackageFile > contentPackages , Set <String > cloudManagerTarget ) {
138153
139154 // collect list of cloud manager environment run modes
140155 List <String > environmentRunModes = new ArrayList <>();
@@ -217,7 +232,6 @@ public boolean build(Map<String, String> properties) throws IOException {
217232 for (String environmentRunMode : fileSet .getEnvironmentRunModes ()) {
218233 List <ContentPackageFile > previousPackages = new ArrayList <>();
219234 for (ContentPackageFile pkg : fileSet .getContentPackages ()) {
220- String path = buildPackagePath (pkg , rootPath , environmentRunMode );
221235
222236 ContentPackageFile previousPkg = null ;
223237
@@ -232,14 +246,22 @@ public boolean build(Map<String, String> properties) throws IOException {
232246 }
233247
234248 // set package name, wire previous package in package dependency
235- File tempPackageFile = processContentPackage (pkg . getFile (), pkg , previousPkg , environmentRunMode , allPackagesFromFileSets );
249+ List < TemporaryContentPackageFile > processedFiles = processContentPackage (pkg , previousPkg , environmentRunMode , allPackagesFromFileSets );
236250
237- // add temp zip file to "all" content package
251+ // add processed content packages to "all" content package - and delete the temporary files
238252 try {
239- contentPackage .addFile (path , tempPackageFile );
253+ for (TemporaryContentPackageFile processedFile : processedFiles ) {
254+ String path = buildPackagePath (processedFile , rootPath , environmentRunMode );
255+ contentPackage .addFile (path , processedFile .getFile ());
256+ if (log .isDebugEnabled ()) {
257+ log .debug (" Add " + processedFile .getPackageInfoWithDependencies ());
258+ }
259+ }
240260 }
241261 finally {
242- FileUtils .deleteQuietly (tempPackageFile );
262+ processedFiles .stream ()
263+ .map (TemporaryContentPackageFile ::getFile )
264+ .forEach (FileUtils ::deleteQuietly );
243265 }
244266
245267 previousPackages .add (pkg );
@@ -284,7 +306,7 @@ private static String buildRootPath(String groupName, String packageName) {
284306
285307 /**
286308 * Generate suffix for instance and environment run modes.
287- * @param pkg Package
309+ * @param pkg Content package
288310 * @return Package path
289311 */
290312 private static String buildRunModeSuffix (ContentPackageFile pkg , String environmentRunMode ) {
@@ -308,6 +330,10 @@ else if (RunModeUtil.isOnlyPublish(pkg)) {
308330 * @return Package path
309331 */
310332 private static String buildPackagePath (ContentPackageFile pkg , String rootPath , String environmentRunMode ) {
333+ if (!isValidPackageType (pkg )) {
334+ throw new IllegalArgumentException ("Package " + pkg .getPackageInfo () + " has invalid package type: '" + pkg .getPackageType () + "'." );
335+ }
336+
311337 String runModeSuffix = buildRunModeSuffix (pkg , environmentRunMode );
312338
313339 // add run mode suffix to both install folder path and package file name
@@ -317,33 +343,33 @@ private static String buildPackagePath(ContentPackageFile pkg, String rootPath,
317343 if (pkg .getVersion () != null && pkg .getFile ().getName ().contains (pkg .getVersion ())) {
318344 versionSuffix = "-" + pkg .getVersion ();
319345 }
320- String fileName = pkg .getName () + runModeSuffix + versionSuffix
346+ String fileName = pkg .getName () + versionSuffix
321347 + "." + FilenameUtils .getExtension (pkg .getFile ().getName ());
322348 return path + "/" + fileName ;
323349 }
324350
325351 /**
326352 * Rewrite content package ZIP file while adding to "all" package:
327353 * Add dependency to previous package in CONGA configuration file oder.
328- * @param contentPackageFile The actual content package file to process
329354 * @param pkg Package to process (can be parent packe of the actual file)
330355 * @param previousPkg Previous package to get dependency information from.
331356 * Is null if no previous package exists or auto dependency mode is switched off.
332357 * @param environmentRunMode Environment run mode
333358 * @param allPackagesFromFileSets Set with all packages from all file sets as dependency instances
334- * @return Returns a *temporary* file - has to be deleted when processing the result is completed.
359+ * @return Returns a list of content package *temporary* files - have to be deleted when processing is completed.
335360 * @throws IOException I/O error
336361 */
337- private File processContentPackage (File contentPackageFile , ContentPackageFile pkg ,
362+ private List < TemporaryContentPackageFile > processContentPackage (ContentPackageFile pkg ,
338363 ContentPackageFile previousPkg , String environmentRunMode ,
339364 Set <Dependency > allPackagesFromFileSets ) throws IOException {
340365
366+ List <TemporaryContentPackageFile > result = new ArrayList <>();
367+
341368 // create temp zip file to create rewritten copy of package
342- File tempFile = File .createTempFile (" pkg" , ".zip" );
369+ File tempFile = File .createTempFile (FilenameUtils . getBaseName ( pkg . getFile (). getName ()) , ".zip" );
343370
344371 // open original content package
345- try (ZipFile zipFileIn = new ZipFile (contentPackageFile )) {
346- String dependenciesString = null ;
372+ try (ZipFile zipFileIn = new ZipFile (pkg .getFile ())) {
347373
348374 // iterate through entries and write them to the temp. zip file
349375 try (FileOutputStream fos = new FileOutputStream (tempFile );
@@ -353,48 +379,54 @@ private File processContentPackage(File contentPackageFile, ContentPackageFile p
353379 ZipEntry zipInEntry = zipInEntries .nextElement ();
354380 if (!zipInEntry .isDirectory ()) {
355381 try (InputStream is = zipFileIn .getInputStream (zipInEntry )) {
382+ boolean processedEntry = false ;
356383
357384 // if entry is properties.xml, update dependency information
358385 if (StringUtils .equals (zipInEntry .getName (), "META-INF/vault/properties.xml" )) {
359386 Properties props = new Properties ();
360387 props .loadFromXML (is );
361388 addSuffixToPackageName (props , pkg , environmentRunMode );
362389 if (autoDependenciesMode != AutoDependenciesMode .OFF ) {
363- dependenciesString = updateDependencies (props , previousPkg , environmentRunMode , allPackagesFromFileSets );
390+ updateDependencies (props , previousPkg , environmentRunMode , allPackagesFromFileSets );
391+ }
392+
393+ // if package type is missing package properties, put in the type defined in model
394+ if (props .get (NAME_PACKAGE_TYPE ) == null ) {
395+ props .put (NAME_PACKAGE_TYPE , pkg .getPackageType ());
364396 }
365397
366398 ZipEntry zipOutEntry = new ZipEntry (zipInEntry .getName ());
367399 zipOut .putNextEntry (zipOutEntry );
368400 props .storeToXML (zipOut , null );
401+ processedEntry = true ;
369402 }
370403
371404 // process sub-packages as well: add runmode suffix and update dependencies
372405 else if (StringUtils .equals (FilenameUtils .getExtension (zipInEntry .getName ()), "zip" )) {
373- String path = FilenameUtils .getPath (zipInEntry .getName ());
374- String basename = FilenameUtils .getBaseName (zipInEntry .getName ());
375- String runModeSuffix = buildRunModeSuffix (pkg , environmentRunMode );
376-
377- File tempSubPackageFile = File .createTempFile ("subpkg-" + basename + runModeSuffix , ".zip" );
406+ File tempSubPackageFile = File .createTempFile (FilenameUtils .getBaseName (zipInEntry .getName ()), ".zip" );
378407 try (FileOutputStream subPackageFos = new FileOutputStream (tempSubPackageFile )) {
379408 IOUtils .copy (is , subPackageFos );
380409 }
381- File resultSubPackageFile = processContentPackage (tempSubPackageFile , pkg , null , environmentRunMode , allPackagesFromFileSets );
382- try (FileInputStream subPackageFis = new FileInputStream (resultSubPackageFile )) {
383- // add runmode suffix to install path and to filename
384- String newPath = StringUtils .removeEnd (path , "/" ) + runModeSuffix + "/"
385- + basename + runModeSuffix + ".zip" ;
386-
387- ZipEntry zipOutEntry = new ZipEntry (newPath );
388- zipOut .putNextEntry (zipOutEntry );
389- IOUtils .copy (subPackageFis , zipOut );
410+
411+ // check if contained ZIP file is really a content package
412+ // then process it as well, remove if from the content package is was contained it
413+ // and add it as "1st level package" to the all package
414+ TemporaryContentPackageFile tempSubPackage = new TemporaryContentPackageFile (tempSubPackageFile , pkg .getVariants ());
415+ if (!isValidPackageType (tempSubPackage )) {
416+ throw new IllegalArgumentException ("Package " + pkg .getPackageInfo () + " contains sub package " + tempSubPackage .getPackageInfo ()
417+ + " with invalid package type: '" + StringUtils .defaultString (tempSubPackage .getPackageType ()) + "'" );
418+ }
419+ if (StringUtils .isNoneBlank (tempSubPackage .getGroup (), tempSubPackage .getName ())) {
420+ result .addAll (processContentPackage (tempSubPackage , previousPkg , environmentRunMode , allPackagesFromFileSets ));
421+ processedEntry = true ;
390422 }
391- finally {
392- FileUtils .deleteQuietly (resultSubPackageFile );
423+ else {
424+ FileUtils .deleteQuietly (tempSubPackageFile );
393425 }
394426 }
395427
396428 // otherwise transfer the binary data 1:1
397- else {
429+ if (! processedEntry ) {
398430 ZipEntry zipOutEntry = new ZipEntry (zipInEntry .getName ());
399431 zipOut .putNextEntry (zipOutEntry );
400432 IOUtils .copy (is , zipOut );
@@ -404,26 +436,21 @@ else if (StringUtils.equals(FilenameUtils.getExtension(zipInEntry.getName()), "z
404436 zipOut .closeEntry ();
405437 }
406438 }
407-
408- if (log .isDebugEnabled ()) {
409- log .debug ("Processed " + getCanonicalPath (contentPackageFile )
410- + (dependenciesString != null ? " with dependencies: " + dependenciesString : "" ));
411- }
412439 }
413440
414- return tempFile ;
441+ result . add ( new TemporaryContentPackageFile ( tempFile , pkg . getVariants ())) ;
415442 }
443+ return result ;
416444 }
417445
418446 /**
419447 * Add dependency information to dependencies string in properties (if it does not exist already).
420448 * @param props Properties
421449 * @param dependencyFile Dependency package
422450 * @param allPackagesFromFileSets Set with all packages from all file sets as dependency instances
423- * @throws IOException I/O exception
424451 */
425- private static String updateDependencies (Properties props , ContentPackageFile dependencyFile , String environmentRunMode ,
426- Set <Dependency > allPackagesFromFileSets ) throws IOException {
452+ private static void updateDependencies (Properties props , ContentPackageFile dependencyFile , String environmentRunMode ,
453+ Set <Dependency > allPackagesFromFileSets ) {
427454 String [] existingDepsStrings = StringUtils .split (props .getProperty (NAME_DEPENDENCIES ), "," );
428455 Dependency [] existingDeps = null ;
429456 if (existingDepsStrings != null && existingDepsStrings .length > 0 ) {
@@ -448,10 +475,6 @@ private static String updateDependencies(Properties props, ContentPackageFile de
448475 if (deps != null ) {
449476 String dependenciesString = Dependency .toString (deps );
450477 props .put (NAME_DEPENDENCIES , dependenciesString );
451- return dependenciesString ;
452- }
453- else {
454- return null ;
455478 }
456479 }
457480
0 commit comments