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.model;
21  
22  import static io.wcm.devops.conga.generator.util.FileUtil.getCanonicalPath;
23  import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.children;
24  import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.getVariants;
25  import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.toStringSet;
26  
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.Reader;
33  import java.nio.charset.StandardCharsets;
34  import java.util.ArrayList;
35  import java.util.LinkedHashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Objects;
39  import java.util.Set;
40  import java.util.jar.JarFile;
41  import java.util.jar.Manifest;
42  
43  import org.apache.commons.io.FilenameUtils;
44  import org.apache.commons.lang3.StringUtils;
45  import org.osgi.framework.Constants;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  import org.yaml.snakeyaml.Yaml;
49  
50  import io.wcm.devops.conga.model.util.MapExpander;
51  import io.wcm.devops.conga.plugins.aem.postprocessor.ContentPackagePropertiesPostProcessor;
52  
53  /**
54   * Parsers model.yaml files generated by CONGA.
55   */
56  public final class ModelParser {
57  
58    /**
59     * Model file.
60     */
61    public static final String MODEL_FILE = "model.yaml";
62  
63    private static final String PROP_ROLES = "roles";
64    private static final String PROP_ROLE = "role";
65    private static final String PROP_CONFIG = "config";
66    private static final String PROP_FILES = "files";
67  
68    private static final Logger log = LoggerFactory.getLogger(ModelParser.class);
69  
70    private final Yaml yaml;
71    private final File nodeDir;
72    private final Map<String, Object> modelData;
73  
74    /**
75     * @param nodeDir Node directory
76     */
77    public ModelParser(File nodeDir) {
78      this.yaml = YamlUtil.createYaml();
79      this.nodeDir = nodeDir;
80      this.modelData = getModelData();
81    }
82  
83    /**
84     * Returns all content packages and OSGi bundles referenced in this model file.
85     * @return List of content packages and OSGi bundles.
86     */
87    public List<InstallableFile> getInstallableFilesForNode() {
88      List<InstallableFile> items = new ArrayList<>();
89      for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
90        List<String> variants = getVariants(role);
91        for (Map<String, Object> fileData : children(role, PROP_FILES)) {
92          String path = Objects.toString(fileData.get("path"), null);
93          File file = new File(nodeDir, path);
94          if (isContentPackage(fileData)) {
95            items.add(new ModelContentPackageFile(file, fileData, variants));
96          }
97          else if (isOsgiBundle(file)) {
98            items.add(new BundleFile(file, fileData, variants));
99          }
100       }
101     }
102     return items;
103   }
104 
105   /**
106    * Checks if the node has the given node role assigned.
107    * @param roleName Node role name
108    * @return true if role is assigned
109    */
110   public boolean hasRole(String roleName) {
111     for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
112       if (StringUtils.equals(Objects.toString(role.get(PROP_ROLE), null), roleName)) {
113         return true;
114       }
115     }
116     return false;
117   }
118 
119   /**
120    * Collects all assigned "cloudManager.target" values (lists or single values) to any role from the node.
121    * @return List of cloud manager environment names or "none"
122    */
123   @SuppressWarnings("unchecked")
124   public Set<String> getCloudManagerTarget() {
125     Set<String> targets = new LinkedHashSet<>();
126     for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
127       Map<String, Object> config = (Map<String, Object>)role.get(PROP_CONFIG);
128       if (config != null) {
129         Object targetValue = MapExpander.getDeep(config, "cloudManager.target");
130         if (targetValue != null) {
131           targets.addAll(toStringSet(targetValue));
132         }
133       }
134     }
135     return targets;
136   }
137 
138   @SuppressWarnings("java:S112") // runtime exception
139   private Map<String, Object> getModelData() {
140     File modelFile = new File(nodeDir, MODEL_FILE);
141     if (!modelFile.exists() || !modelFile.isFile()) {
142       throw new RuntimeException("Model file not found: " + getCanonicalPath(modelFile));
143     }
144     return parseYaml(modelFile);
145   }
146 
147   @SuppressWarnings("java:S112") // runtime exception
148   private Map<String, Object> parseYaml(File modelFile) {
149     try {
150       try (InputStream is = new FileInputStream(modelFile);
151           Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
152         return yaml.loadAs(reader, Map.class);
153       }
154     }
155     catch (IOException ex) {
156       throw new RuntimeException("Unable to parse " + getCanonicalPath(modelFile), ex);
157     }
158   }
159 
160   private boolean isContentPackage(Map<String, Object> fileData) {
161     return fileData.get(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY) != null;
162   }
163 
164   private boolean isOsgiBundle(File file) {
165     if (!StringUtils.equals(FilenameUtils.getExtension((file.getName())), "jar")) {
166       return false;
167     }
168     try (JarFile jarFile = new JarFile(file)) {
169       Manifest manifest = jarFile.getManifest();
170       if (manifest != null) {
171         return manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) != null;
172       }
173     }
174     catch (IOException ex) {
175       log.debug("Unable to check for OSGi bundle: {}", file, ex);
176     }
177     return false;
178   }
179 
180 }