View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2020 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.devops.conga.plugins.aem.maven.allpackage;
21  
22  import java.util.Collection;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Optional;
27  import java.util.Set;
28  import java.util.function.Function;
29  import java.util.stream.Collectors;
30  
31  import org.apache.commons.lang3.Strings;
32  
33  import io.wcm.devops.conga.plugins.aem.maven.model.InstallableFile;
34  
35  final class RunModeUtil {
36  
37    static final String RUNMODE_AUTHOR = "author";
38    static final String RUNMODE_PUBLISH = "publish";
39  
40    private RunModeUtil() {
41      // static methods only
42    }
43  
44    /**
45     * Checks if the given package is to be installed on both author and publish instances
46     * @param file Content package
47     * @return true if author and publish run mode (or no run mode = no restriction)
48     */
49    public static boolean isAuthorAndPublish(InstallableFile file) {
50      Set<String> runModes = mapVariantsToRunModes(file.getVariants());
51      return (!runModes.contains(RUNMODE_AUTHOR) && !runModes.contains(RUNMODE_PUBLISH))
52          || (runModes.contains(RUNMODE_AUTHOR) && runModes.contains(RUNMODE_PUBLISH));
53    }
54  
55    /**
56     * Checks if the given variants map to author run mode, but not to publish run mode.
57     * @param file Content package
58     * @return true if only author run modes
59     */
60    public static boolean isOnlyAuthor(InstallableFile file) {
61      Set<String> runModes = mapVariantsToRunModes(file.getVariants());
62      return runModes.contains(RUNMODE_AUTHOR) && !runModes.contains(RUNMODE_PUBLISH);
63    }
64  
65    /**
66     * Checks if the given variants map to publish run mode, but not to author run mode.
67     * @param file Content package
68     * @return true if only publish run modes
69     */
70    public static boolean isOnlyPublish(InstallableFile file) {
71      Set<String> runModes = mapVariantsToRunModes(file.getVariants());
72      return runModes.contains(RUNMODE_PUBLISH) && !runModes.contains(RUNMODE_AUTHOR);
73    }
74  
75    private static Set<String> mapVariantsToRunModes(Collection<String> variants) {
76      return variants.stream()
77          .map(RunModeUtil::mapVariantToRunMode)
78          .collect(Collectors.toSet());
79    }
80  
81    /**
82     * Maps well-known variant names from CONGA AEM definitions to the corresponding run modes.
83     * If the variant name is not well-known the variant name is used as run mode.
84     * @param variant Variant
85     * @return Run mode
86     */
87    private static String mapVariantToRunMode(String variant) {
88      if ("aem-author".equals(variant)) {
89        return RUNMODE_AUTHOR;
90      }
91      else if ("aem-publish".equals(variant)) {
92        return RUNMODE_PUBLISH;
93      }
94      return variant;
95    }
96  
97    /**
98     * Builds an optimized list of file sets separated for each environment run mode, but combined for author and publish
99     * variant files. Those files are reduced to single items if they are present in both author and publish variants. The
100    * order of the resulting file sets if driven by the first file set(s) in the list, additional files from other file
101    * sets are added at the end of the result list(s).
102    * @param fileSets Existing list of filesets
103    * @param fileSetFactory Creates a new (empty) file set for given environment run mode
104    * @return Optimized list of file sets (one per environment run mode)
105    */
106   public static <T extends InstallableFile, S extends FileSet<T>> Collection<S> eliminateAuthorPublishDuplicates(
107       List<S> fileSets, Function<String, S> fileSetFactory) {
108     Map<String, S> result = new LinkedHashMap<>();
109     fileSets.forEach(fileSet -> fileSet.getEnvironmentRunModes().forEach(environmentRunMode -> {
110         FileSet<T> resultFileSet = result.computeIfAbsent(environmentRunMode, fileSetFactory);
111         fileSet.getFiles().forEach(file -> {
112           Optional<T> existingFile = resultFileSet.getFiles().stream()
113               .filter(item -> isSameFileNameHash(item, file))
114               .findFirst();
115           if (existingFile.isPresent()) {
116             // if file was already added from other file set: eliminate duplicate, but add run modes
117             existingFile.get().getVariants().addAll(file.getVariants());
118           }
119           else {
120             resultFileSet.getFiles().add(file);
121           }
122         });
123     }));
124     // eliminate author+publish run modes if both are set on same file
125     result.values().forEach(
126         fileSet -> fileSet.getFiles().forEach(file -> removeAuthorPublishRunModeIfBothPresent(file.getVariants())));
127     return result.values();
128   }
129 
130   private static boolean isSameFileNameHash(InstallableFile file1, InstallableFile file2) {
131     if (!Strings.CS.equals(file1.getFile().getName(), file2.getFile().getName())) {
132       return false;
133     }
134     return file1.getHashCode().equals(file2.getHashCode());
135   }
136 
137   /**
138    * Removes author and publish runmodes from given set if both are present.
139    * @param runModes Run modes
140    */
141   private static void removeAuthorPublishRunModeIfBothPresent(Set<String> runModes) {
142     if (runModes.contains(RUNMODE_AUTHOR) && runModes.contains(RUNMODE_PUBLISH)) {
143       runModes.remove(RUNMODE_AUTHOR);
144       runModes.remove(RUNMODE_PUBLISH);
145     }
146   }
147 
148 }