ModelParser.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2020 wcm.io
* %%
* Licensed 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.
* #L%
*/
package io.wcm.devops.conga.plugins.aem.maven.model;
import static io.wcm.devops.conga.generator.util.FileUtil.getCanonicalPath;
import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.children;
import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.getVariants;
import static io.wcm.devops.conga.plugins.aem.maven.model.ParserUtil.toStringSet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import io.wcm.devops.conga.model.util.MapExpander;
import io.wcm.devops.conga.plugins.aem.postprocessor.ContentPackagePropertiesPostProcessor;
/**
* Parsers model.yaml files generated by CONGA.
*/
public final class ModelParser {
/**
* Model file.
*/
public static final String MODEL_FILE = "model.yaml";
private static final String PROP_ROLES = "roles";
private static final String PROP_ROLE = "role";
private static final String PROP_CONFIG = "config";
private static final String PROP_FILES = "files";
private static final Logger log = LoggerFactory.getLogger(ModelParser.class);
private final Yaml yaml;
private final File nodeDir;
private final Map<String, Object> modelData;
/**
* @param nodeDir Node directory
*/
public ModelParser(File nodeDir) {
this.yaml = YamlUtil.createYaml();
this.nodeDir = nodeDir;
this.modelData = getModelData();
}
/**
* Returns all content packages and OSGi bundles referenced in this model file.
* @return List of content packages and OSGi bundles.
*/
public List<InstallableFile> getInstallableFilesForNode() {
List<InstallableFile> items = new ArrayList<>();
for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
List<String> variants = getVariants(role);
for (Map<String, Object> fileData : children(role, PROP_FILES)) {
String path = Objects.toString(fileData.get("path"), null);
File file = new File(nodeDir, path);
if (isContentPackage(fileData)) {
items.add(new ModelContentPackageFile(file, fileData, variants));
}
else if (isOsgiBundle(file)) {
items.add(new BundleFile(file, fileData, variants));
}
}
}
return items;
}
/**
* Checks if the node has the given node role assigned.
* @param roleName Node role name
* @return true if role is assigned
*/
public boolean hasRole(String roleName) {
for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
if (StringUtils.equals(Objects.toString(role.get(PROP_ROLE), null), roleName)) {
return true;
}
}
return false;
}
/**
* Collects all assigned "cloudManager.target" values (lists or single values) to any role from the node.
* @return List of cloud manager environment names or "none"
*/
@SuppressWarnings("unchecked")
public Set<String> getCloudManagerTarget() {
Set<String> targets = new LinkedHashSet<>();
for (Map<String, Object> role : children(modelData, PROP_ROLES)) {
Map<String, Object> config = (Map<String, Object>)role.get(PROP_CONFIG);
if (config != null) {
Object targetValue = MapExpander.getDeep(config, "cloudManager.target");
if (targetValue != null) {
targets.addAll(toStringSet(targetValue));
}
}
}
return targets;
}
@SuppressWarnings("java:S112") // runtime exception
private Map<String, Object> getModelData() {
File modelFile = new File(nodeDir, MODEL_FILE);
if (!modelFile.exists() || !modelFile.isFile()) {
throw new RuntimeException("Model file not found: " + getCanonicalPath(modelFile));
}
return parseYaml(modelFile);
}
@SuppressWarnings("java:S112") // runtime exception
private Map<String, Object> parseYaml(File modelFile) {
try {
try (InputStream is = new FileInputStream(modelFile);
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
return yaml.loadAs(reader, Map.class);
}
}
catch (IOException ex) {
throw new RuntimeException("Unable to parse " + getCanonicalPath(modelFile), ex);
}
}
private boolean isContentPackage(Map<String, Object> fileData) {
return fileData.get(ContentPackagePropertiesPostProcessor.MODEL_OPTIONS_PROPERTY) != null;
}
private boolean isOsgiBundle(File file) {
if (!StringUtils.equals(FilenameUtils.getExtension((file.getName())), "jar")) {
return false;
}
try (JarFile jarFile = new JarFile(file)) {
Manifest manifest = jarFile.getManifest();
if (manifest != null) {
return manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) != null;
}
}
catch (IOException ex) {
log.debug("Unable to check for OSGi bundle: {}", file, ex);
}
return false;
}
}