/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * 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. * * Initial developer(s): Linas Virbalas * Contributor(s): */ package com.continuent.tungsten.common.utils; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import org.apache.log4j.Logger; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; /** * Parses .manifest.json file and makes properties easily accessible. * * @author <a href="mailto:linas.virbalas@continuent.com">Linas Virbalas</a> * @version 1.0 */ public class ManifestParser { private static Logger logger = Logger.getLogger(ManifestParser.class); private static final String manifestFileName = ".manifest.json"; private JSONParser parser = null; private JSONObject rootJSONObject = null; private String manifestPath = null; /** * Prepare JSONParser and find out the home folder. Call parse() afterwards. * * @see #parse() */ public ManifestParser() { parser = new JSONParser(); findHome(); } /** * Read and parse manifest file. No exceptions are thrown upon failure * intentionally. * * @return true, if parsed successfully, otherwise false. * @see #isFileParsed() * @see #getReleaseWithBuildNumber() */ public boolean parse() { try { String jsonManifest = readManifestFile(manifestPath); Object obj = parser.parse(jsonManifest); rootJSONObject = (JSONObject) obj; return true; } catch (IOException ioe) { logger.error("Error reading " + manifestPath + ": " + ioe.toString()); return false; } catch (ParseException pe) { logger.error("Error parsing JSON from " + manifestPath + ": " + pe.getPosition()); logger.error(pe); return false; } } /** * Find the home folder of installation. */ private void findHome() { String home = System.getProperty("replicator.home.dir"); if (home == null) home = System.getProperty("manager.home"); if (home == null) home = System.getProperty("cluster.home"); if (home == null) home = System.getProperty("user.dir"); if (home != null) // Home determined. manifestPath = home + File.separator + ".." + File.separator + manifestFileName; else manifestPath = manifestFileName; // Not found - use current folder. } /** * Path to the manifest file. */ public String getManifestPath() { return manifestPath; } private String returnStringOrNull(Object val) { if (val == null) return null; else return (String) val; } private Number returnNumberOrNull(Object val) { if (val == null) return null; else return (Number) val; } private Object getFromJSONObject(JSONObject object, Object key) { if (object != null) return object.get(key); else return null; } private JSONObject getHudson() { return (JSONObject) getFromJSONObject(rootJSONObject, "hudson"); } private JSONObject getVersion() { return (JSONObject) getFromJSONObject(rootJSONObject, "version"); } /** * Returns product name from the manifest. */ public String getProduct() { Object val = getFromJSONObject(rootJSONObject, "product"); return returnStringOrNull(val); } /** * Returns major (first) number of the version. */ public Number getVersionMajor() { Object val = getFromJSONObject(getVersion(), "major"); return returnNumberOrNull(val); } /** * Returns minor (middle) number of the version. */ public Number getVersionMinor() { Object val = getFromJSONObject(getVersion(), "minor"); return returnNumberOrNull(val); } /** * Returns revision (last) number of the version. */ public Number getVersionRevision() { Object val = getFromJSONObject(getVersion(), "revision"); return returnNumberOrNull(val); } /** * Returns concatenated version in format: MAJOR.MINOR.REVISION * * @return a) Version in MAJOR.MINOR.REVISION format;<br/> * b) null if non of the major, minor or revision numbers are * defined.<br/> * c) If only one or two numbers are missing, returns question mark * (?) in place of it. Eg.: 1.3.? */ public String getVersionFull() { Number major = getVersionMajor(); Number minor = getVersionMinor(); Number revision = getVersionRevision(); if (major == null && minor == null && revision == null) return null; else { StringBuilder builder = new StringBuilder(); if (major != null) builder.append(major); else builder.append("?"); builder.append("."); if (minor != null) builder.append(minor); else builder.append("?"); builder.append("."); if (revision != null) builder.append(revision); else builder.append("?"); return builder.toString(); } } public String getBuildDate() { Object val = getFromJSONObject(rootJSONObject, "date"); return returnStringOrNull(val); } /** * Returns Hudson's build number from the manifest. */ public Number getHudsonBuildNumber() { Object val = getFromJSONObject(getHudson(), "buildNumber"); return returnNumberOrNull(val); } /** * Returns Hudson's used SVN revision from the manifest. */ public String getHudsonSVNRevision() { Object val = getFromJSONObject(getHudson(), "SVNRevision"); return returnStringOrNull(val); } /** * Returns a representative string of release name, version and build * number. This is a safe method in a sense it will never return a null. * * @return Release and build number or an error message that manifest file * couldn't be read. */ public String getReleaseWithBuildNumber() { String product = getProduct(); String version = getVersionFull(); Number build = getHudsonBuildNumber(); StringBuilder builder = new StringBuilder(); if (product != null) builder.append(product); if (version != null) { builder.append(" "); builder.append(version); } if (build != null) { builder.append(" build "); builder.append(build); } if (product == null && version == null && build == null) { builder.append("Unable to determine release name - does "); builder.append(manifestPath); builder.append(" exist?"); } return builder.toString(); } /** * Convenience method for a single line call to get a representative string * of release name, version and build number. */ public static String parseReleaseWithBuildNumber() { ManifestParser manifest = new ManifestParser(); manifest.parse(); return manifest.getReleaseWithBuildNumber(); } /** * Convenience method for a single line call to log a representative string * of release name, version and build number. * * @param logger Logger to log into. */ public static void logReleaseWithBuildNumber(Logger logger) { logger.info(ManifestParser.parseReleaseWithBuildNumber()); } /** * @return True, if manifest was successfully parsed. */ public boolean isFileParsed() { if (rootJSONObject != null) return true; else return false; } /** * Reads the whole manifest file into a String. * * @throws Exception if the file cannot be found. */ private String readManifestFile(String file) throws IOException { BufferedReader br = new BufferedReader(new FileReader(file)); try { StringBuilder builder = new StringBuilder(); String line = null; String newLine = System.getProperty("line.separator"); while ((line = br.readLine()) != null) { builder.append(line); builder.append(newLine); } return builder.toString(); } finally { try { if (br != null) br.close(); } catch (IOException ex) { logger.warn("Unable to close file " + file + ": " + ex.toString()); } } } }