/* * Copyright (C) 2011 JFrog Ltd. * * 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. */ package org.jfrog.build.extractor; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.google.common.base.Charsets; import com.google.common.base.Predicate; import com.google.common.collect.Maps; import com.google.common.io.Files; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.jfrog.build.api.Build; import org.jfrog.build.api.BuildInfoConfigProperties; import org.jfrog.build.api.BuildInfoProperties; import org.jfrog.build.api.util.Log; import org.jfrog.build.extractor.clientConfiguration.ClientProperties; import org.jfrog.build.extractor.clientConfiguration.IncludeExcludePatterns; import org.jfrog.build.extractor.clientConfiguration.PatternMatcher; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * @author Noam Y. Tenne */ public abstract class BuildInfoExtractorUtils { public static final Predicate<Object> BUILD_INFO_PREDICATE = new PrefixPredicate(BuildInfoProperties.BUILD_INFO_PREFIX); public static final Predicate<Object> BUILD_INFO_PROP_PREDICATE = new PrefixPredicate(BuildInfoProperties.BUILD_INFO_PROP_PREFIX); public static final Predicate<Object> ENV_PREDICATE = new PrefixPredicate(BuildInfoProperties.BUILD_INFO_ENVIRONMENT_PREFIX); public static final Predicate<Object> CLIENT_PREDICATE = new PrefixPredicate(ClientProperties.ARTIFACTORY_PREFIX); public static final Predicate<Object> MATRIX_PARAM_PREDICATE = new PrefixPredicate(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX); public static Properties mergePropertiesWithSystemAndPropertyFile(Properties existingProps) { return mergePropertiesWithSystemAndPropertyFile(existingProps, null); } public static Properties mergePropertiesWithSystemAndPropertyFile(Properties existingProps, Log log) { Properties props = new Properties(); String propsFilePath = getAdditionalPropertiesFile(existingProps, log); if (StringUtils.isNotBlank(propsFilePath)) { File propertiesFile = new File(propsFilePath); InputStream inputStream = null; try { if (propertiesFile.exists()) { inputStream = new FileInputStream(propertiesFile); props.load(inputStream); } } catch (IOException e) { throw new RuntimeException( "Unable to load build info properties from file: " + propertiesFile.getAbsolutePath(), e); } finally { IOUtils.closeQuietly(inputStream); } } props.putAll(existingProps); props.putAll(System.getProperties()); return props; } public static Map<String, ?> filterStringEntries(Map<String, ?> map) { return Maps.filterValues(map, new Predicate<Object>() { public boolean apply(Object input) { return input != null && input instanceof String; } }); } public static Properties filterDynamicProperties(Properties source, Predicate<Object> filter) { Properties properties = new Properties(); if (source != null) { properties.putAll(Maps.filterKeys(source, filter)); } return properties; } public static Properties stripPrefixFromProperties(Properties source, String prefix) { Properties props = new Properties(); for (Map.Entry<Object, Object> entry : source.entrySet()) { String key = entry.getKey().toString(); props.put(StringUtils.removeStart(key, prefix), entry.getValue()); } return props; } public static Properties getEnvProperties(Properties startProps, Log log) { IncludeExcludePatterns patterns = new IncludeExcludePatterns( startProps.getProperty(BuildInfoConfigProperties.PROP_ENV_VARS_INCLUDE_PATTERNS), startProps.getProperty(BuildInfoConfigProperties.PROP_ENV_VARS_EXCLUDE_PATTERNS)); Properties props = new Properties(); // Add all the startProps that starts with BuildInfoProperties.BUILD_INFO_ENVIRONMENT_PREFIX for (Map.Entry<Object, Object> startEntry : startProps.entrySet()) { if (StringUtils.startsWith((String) startEntry.getKey(), BuildInfoProperties.BUILD_INFO_ENVIRONMENT_PREFIX)) { props.put(startEntry.getKey(), startEntry.getValue()); } } // Add all system environment that match the patterns Map<String, String> envMap = System.getenv(); for (Map.Entry<String, String> entry : envMap.entrySet()) { String varKey = entry.getKey(); if (PatternMatcher.pathConflicts(varKey, patterns)) { continue; } props.put(BuildInfoProperties.BUILD_INFO_ENVIRONMENT_PREFIX + varKey, entry.getValue()); } Map<String, String> sysProps = new HashMap(System.getProperties()); Map<String, String> filteredSysProps = Maps.difference(sysProps, System.getenv()).entriesOnlyOnLeft(); for (Map.Entry<String, String> entry : filteredSysProps.entrySet()) { String varKey = entry.getKey(); if (PatternMatcher.pathConflicts(varKey, patterns)) { continue; } props.put(varKey, entry.getValue()); } // TODO: [by FSI] Test if this is needed! Since start props are used now String propsFilePath = getAdditionalPropertiesFile(startProps, log); if (StringUtils.isNotBlank(propsFilePath)) { File propertiesFile = new File(propsFilePath); InputStream inputStream = null; try { if (propertiesFile.exists()) { inputStream = new FileInputStream(propertiesFile); Properties propertiesFromFile = new Properties(); propertiesFromFile.load(inputStream); props.putAll(filterDynamicProperties(propertiesFromFile, ENV_PREDICATE)); } } catch (IOException e) { throw new RuntimeException( "Unable to load build info properties from file: " + propertiesFile.getAbsolutePath(), e); } finally { IOUtils.closeQuietly(inputStream); } } return props; } //TODO: [by YS] duplicates ArtifactoryBuildInfoClient. The client should depend on this module //TODO: [by yl] introduce a commons module for common impl and also move PropertyUtils there private static JsonFactory createJsonFactory() { JsonFactory jsonFactory = new JsonFactory(); ObjectMapper mapper = new ObjectMapper(jsonFactory); mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector()); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jsonFactory.setCodec(mapper); return jsonFactory; } public static String buildInfoToJsonString(Build buildInfo) throws IOException { JsonFactory jsonFactory = createJsonFactory(); StringWriter writer = new StringWriter(); JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator(writer); jsonGenerator.useDefaultPrettyPrinter(); jsonGenerator.writeObject(buildInfo); String result = writer.getBuffer().toString(); return result; } public static Build jsonStringToBuildInfo(String json) throws IOException { JsonFactory jsonFactory = createJsonFactory(); JsonParser parser = jsonFactory.createJsonParser(new StringReader(json)); return jsonFactory.getCodec().readValue(parser, Build.class); } public static <T extends Serializable> String buildInfoToJsonString(T buildComponent) throws IOException { JsonFactory jsonFactory = createJsonFactory(); StringWriter writer = new StringWriter(); JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator(writer); jsonGenerator.useDefaultPrettyPrinter(); jsonGenerator.writeObject(buildComponent); String result = writer.getBuffer().toString(); return result; } public static <T extends Serializable> T jsonStringToGeneric(String json, Class<T> clazz) throws IOException { JsonFactory jsonFactory = createJsonFactory(); JsonParser parser = jsonFactory.createJsonParser(new StringReader(json)); return jsonFactory.getCodec().readValue(parser, clazz); } public static void saveBuildInfoToFile(Build build, File toFile) throws IOException { String buildInfoJson = buildInfoToJsonString(build); if (!toFile.getParentFile().exists()) { toFile.getParentFile().mkdirs(); } if (!toFile.exists()) { toFile.createNewFile(); } Files.write(buildInfoJson, toFile, Charsets.UTF_8); } private static String getAdditionalPropertiesFile(Properties additionalProps, Log log) { String key = BuildInfoConfigProperties.PROP_PROPS_FILE; String filePath = System.getProperty(key); String propFoundPath = "System.getProperty(" + key + ")"; if (StringUtils.isBlank(filePath) && additionalProps != null) { filePath = additionalProps.getProperty(key); propFoundPath = "additionalProps.getProperty(" + key + ")"; } if (StringUtils.isBlank(filePath)) { // Jenkins prefixes these variables with "env." so let's try that filePath = additionalProps.getProperty("env." + key); if (StringUtils.isBlank(filePath)) { filePath = System.getenv(key); propFoundPath = "System.getenv(" + key + ")"; } else { propFoundPath = "additionalProps.getProperty(env." + key + ")"; } } if (StringUtils.isBlank(filePath)) { // Jenkins prefixes these variables with "env." so let's try that key = BuildInfoConfigProperties.ENV_BUILDINFO_PROPFILE; filePath = additionalProps.getProperty("env." + key); if (StringUtils.isBlank(filePath)) { filePath = System.getenv(key); propFoundPath = "System.getenv(" + key + ")"; } else { propFoundPath = "additionalProps.getProperty(env." + key + ")"; } } if (log != null) { if (StringUtils.isBlank(filePath)) { log.debug("[buildinfo] Not using buildInfo properties file for this build."); } else { log.debug("[buildinfo] Properties file '" + filePath + "' retrieved from '" + propFoundPath + "'"); } } return filePath; } public static String getArtifactId(String moduleId, String artifactName) { return moduleId + ":" + artifactName; } public static String getTypeString(String type, String classifier, String extension) { String result = type; // Only use classifier if jar type if ("jar".equals(type)) { // add classifier if it exists if (StringUtils.isNotBlank(classifier)) { result = classifier; } } // Add extension if not jar, ivy or pom type // and current type does not end with the extension (avoid war-war, zip-zip, source-jar-jar, ...) if (!"jar".equals(result) && !"pom".equals(type) && !"ivy".equals(type)) { if (StringUtils.isNotBlank(extension) && !result.endsWith(extension)) { result = result + "-" + extension; } } return result; } public static String getModuleIdString(String organisation, String name, String version) { return organisation + ':' + name + ':' + version; } private static class PrefixPredicate implements Predicate<Object> { private String prefix; protected PrefixPredicate(String prefix) { this.prefix = prefix; } public boolean apply(Object o) { return o != null && ((String) o).startsWith(prefix); } } }