com.tc.util.ProductInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.tc.util.ProductInfo.java

Source

/*
 * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
 */
package com.tc.util;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Utility class to retrieve the build information for the product.
 */
public final class ProductInfo {
    public static final String ENTERPRISE = "Enterprise";
    public static final String OPENSOURCE = "Opensource";

    private static final ResourceBundleHelper bundleHelper = new ResourceBundleHelper(ProductInfo.class);

    private static final String DATE_FORMAT = "yyyyMMdd-HHmmss";
    private static final Pattern KITIDPATTERN = Pattern.compile("(\\d+\\.\\d+.\\d+).*");
    private static final String BUILD_DATA_RESOURCE_NAME = "/build-data.txt";
    private static final String PATCH_DATA_RESOURCE_NAME = "/patch-data.txt";

    private static final String BUILD_DATA_ROOT_KEY = "terracotta.build.";
    private static final String BUILD_DATA_VERSION_KEY = "version";
    private static final String BUILD_DATA_TIMESTAMP_KEY = "timestamp";
    private static final String BUILD_DATA_REVISION_KEY = "revision";
    private static final String BUILD_DATA_EE_REVISION_KEY = "ee.revision";
    private static final String BUILD_DATA_BRANCH_KEY = "branch";
    private static final String PATCH_DATA_ROOT_KEY = "terracotta.patch.";
    private static final String PATCH_DATA_LEVEL_KEY = "level";
    public static final String UNKNOWN_VALUE = "[unknown]";
    public static final String DEFAULT_LICENSE = "Unlimited development";

    private static ProductInfo INSTANCE = null;

    private final String moniker;
    private final String timestamp;
    private final String branch;
    private final String edition;
    private final String revision;
    private final String ee_revision;
    private final String kitID;

    private final String patchLevel;
    private final String patchTimestamp;
    private final String patchRevision;
    private final String patchEERevision;
    private final String patchBranch;

    private final String buildVersion;
    private String buildID;
    private String copyright;
    private final String license = DEFAULT_LICENSE;

    // XXX: Can't have a logger in this class...
    // private static final TCLogger logger = TCLogging.getLogger(ProductInfo.class);
    // private static final TCLogger consoleLogger = CustomerLogging.getConsoleLogger();

    /**
     * Construct a ProductInfo by reading properties from streams (most commonly by loading properties files as resources
     * from the classpath). If an IOException occurs while loading the build or patch streams, the System will exit. These
     * resources are always expected to be in the path and are necessary to do version compatibility checks.
     * 
     * @param buildData Build properties in stream conforming to Java Properties file format, must not be null
     * @param patchData Patch properties in stream conforming to Java Properties file format, null if none
     * @throws NullPointerException If buildData is null and assertions are enabled
     * @throws IOException If there is an error reading the build or patch data streams
     * @throws ParseException If there is an error reading the timestamp format in build or patch data streams
     */
    ProductInfo(InputStream buildData, InputStream patchData) throws IOException {
        Assert.assertNotNull("buildData", buildData);

        Properties properties = new Properties();
        moniker = bundleHelper.getString("moniker");
        properties.load(buildData);
        if (patchData != null)
            properties.load(patchData);

        // Get all release build properties
        this.buildVersion = getBuildProperty(properties, BUILD_DATA_VERSION_KEY, UNKNOWN_VALUE);
        this.edition = detectEdition();
        if (!isOpenSource() && !isEnterprise() && !isDevMode()) {
            throw new AssertionError("Can't recognize kit edition: " + edition);
        }

        this.timestamp = getBuildProperty(properties, BUILD_DATA_TIMESTAMP_KEY, UNKNOWN_VALUE);
        this.branch = getBuildProperty(properties, BUILD_DATA_BRANCH_KEY, UNKNOWN_VALUE);
        this.revision = getBuildProperty(properties, BUILD_DATA_REVISION_KEY, UNKNOWN_VALUE);
        this.ee_revision = getBuildProperty(properties, BUILD_DATA_EE_REVISION_KEY, UNKNOWN_VALUE);

        // Get all patch build properties
        this.patchLevel = getPatchProperty(properties, PATCH_DATA_LEVEL_KEY, UNKNOWN_VALUE);
        this.patchTimestamp = getPatchProperty(properties, BUILD_DATA_TIMESTAMP_KEY, UNKNOWN_VALUE);
        this.patchRevision = getPatchProperty(properties, BUILD_DATA_REVISION_KEY, UNKNOWN_VALUE);
        this.patchEERevision = getPatchProperty(properties, BUILD_DATA_EE_REVISION_KEY, UNKNOWN_VALUE);
        this.patchBranch = getPatchProperty(properties, BUILD_DATA_BRANCH_KEY, UNKNOWN_VALUE);

        Matcher matcher = KITIDPATTERN.matcher(buildVersion);
        kitID = matcher.matches() ? matcher.group(1) : UNKNOWN_VALUE;
    }

    private static final String detectEdition() {
        String edition = OPENSOURCE;
        try {
            Class.forName("com.tc.util.ProductInfoEnterpriseBundle");
            edition = ENTERPRISE;
        } catch (ClassNotFoundException e) {
            // ignore
        }
        return edition;
    }

    static Date parseTimestamp(String timestampString) throws java.text.ParseException {
        return (timestampString == null) ? null : new SimpleDateFormat(DATE_FORMAT).parse(timestampString);
    }

    public static synchronized ProductInfo getInstance() {
        if (INSTANCE == null) {
            try {
                InputStream buildData = getData(BUILD_DATA_RESOURCE_NAME);
                InputStream patchData = getData(PATCH_DATA_RESOURCE_NAME);
                INSTANCE = new ProductInfo(buildData, patchData);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return INSTANCE;
    }

    static InputStream getData(String name) {
        CodeSource codeSource = ProductInfo.class.getProtectionDomain().getCodeSource();
        if (codeSource != null && codeSource.getLocation() != null) {
            URL source = codeSource.getLocation();

            if (source.getProtocol().equals("file") && source.toExternalForm().endsWith(".jar")) {
                URL res;
                try {
                    res = new URL("jar:" + source.toExternalForm() + "!" + name);
                    InputStream in = res.openStream();
                    if (in != null) {
                        return in;
                    }
                } catch (MalformedURLException e) {
                    throw new AssertionError(e);
                } catch (IOException e) {
                    // must not be embedded in this jar -- resolve via loader path
                }
            } else if (source.getProtocol().equals("file") && (new File(source.getPath()).isDirectory())) {
                File local = new File(source.getPath(), name);

                if (local.isFile()) {
                    try {
                        return new FileInputStream(local);
                    } catch (FileNotFoundException e) {
                        throw new AssertionError(e);
                    }
                }
            }
        }

        return ProductInfo.class.getResourceAsStream(name);
    }

    static InputStream getBuildData() {
        return getData(BUILD_DATA_RESOURCE_NAME);
    }

    static InputStream getPatchData() {
        return getData(PATCH_DATA_RESOURCE_NAME);
    }

    private String getBuildProperty(Properties properties, String name, String defaultValue) {
        return getProperty(properties, BUILD_DATA_ROOT_KEY, name, defaultValue);
    }

    private String getPatchProperty(Properties properties, String name, String defaultValue) {
        return getProperty(properties, PATCH_DATA_ROOT_KEY, name, defaultValue);
    }

    private String getProperty(Properties properties, String root, String name, String defaultValue) {
        String out = properties.getProperty(root + name);
        if (StringUtils.isBlank(out))
            out = defaultValue;
        return out;
    }

    public static void printRawData() {
        try {
            InputStream buildData = getBuildData();
            if (buildData != null)
                IOUtils.copy(buildData, System.out);

            InputStream patchData = getPatchData();
            if (patchData != null)
                IOUtils.copy(patchData, System.out);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public boolean isDevMode() {
        return this.buildVersion.endsWith(UNKNOWN_VALUE);
    }

    public String moniker() {
        return moniker;
    }

    public String edition() {
        return edition;
    }

    public String version() {
        return buildVersion;
    }

    /**
     * Remains for backward compatible reason. It returns the maven artifact version we use for TC artifacts
     * http://jira.terracotta.org/jira/browse/DEV-3130
     */
    public String mavenArtifactsVersion() {
        return buildVersion;
    }

    /**
     * Version used during kit build for marketing purpose: 3.1.0-FC, 3.1.0-stable1, 3.1.0-nightly It should not be used
     * to compare version between TC products. Use version() call for that purpose
     */
    public String buildVersion() {
        return buildVersion;
    }

    public String kitID() {
        return kitID;
    }

    public String buildTimestamp() {
        return timestamp;
    }

    public String buildTimestampAsString() {
        return timestamp;
    }

    public String buildBranch() {
        return branch;
    }

    public String copyright() {
        if (copyright == null) {
            copyright = bundleHelper.getString("copyright");
        }
        return copyright;
    }

    public String license() {
        return license;
    }

    public String buildRevision() {
        return revision;
    }

    public String buildRevisionFromEE() {
        return ee_revision;
    }

    public boolean isPatched() {
        return !UNKNOWN_VALUE.equals(patchLevel);
    }

    public String patchLevel() {
        return patchLevel;
    }

    public String patchTimestamp() {
        return patchTimestamp;
    }

    public String patchTimestampAsString() {
        return patchTimestamp;
    }

    public String patchRevision() {
        return patchRevision;
    }

    public String patchEERevision() {
        return patchEERevision;
    }

    public String patchBranch() {
        return patchBranch;
    }

    public String toShortString() {
        return moniker + " " + (isOpenSource() ? "" : (edition + " ")) + buildVersion;
    }

    public String toLongString() {
        return toShortString() + ", as of " + buildID();
    }

    public String buildID() {
        if (buildID == null) {
            buildID = buildTimestampAsString() + " (Revision " + revision + " from " + branch + ")";
        }
        return buildID;
    }

    public String toLongPatchString() {
        return toShortPatchString() + ", as of " + patchBuildID();
    }

    public String toShortPatchString() {
        return "Patch Level " + patchLevel;
    }

    public String patchBuildID() {
        return patchTimestampAsString() + " (Revision " + patchRevision + " from " + patchBranch + ")";
    }

    public boolean isOpenSource() {
        return OPENSOURCE.equalsIgnoreCase(edition);
    }

    public boolean isEnterprise() {
        return ENTERPRISE.equalsIgnoreCase(edition);
    }

    @Override
    public String toString() {
        return toShortString();
    }

    private static void printHelp(Options options) {
        new HelpFormatter().printHelp("java " + ProductInfo.class.getName(), options);
    }

    public static void main(String[] args) {
        Options options = new Options();
        options.addOption("v", "verbose", false, bundleHelper.getString("option.verbose"));
        options.addOption("r", "raw", false, bundleHelper.getString("option.raw"));
        options.addOption("h", "help", false, bundleHelper.getString("option.help"));

        CommandLineParser parser = new GnuParser();
        try {
            CommandLine cli = parser.parse(options, args);

            if (cli.hasOption("h")) {
                printHelp(options);
                System.exit(0);
            }

            if (cli.hasOption("v")) {
                System.out.println(getInstance().toLongString());
                if (getInstance().isPatched())
                    System.out.println(getInstance().toLongPatchString());
                System.exit(0);
            }

            if (cli.hasOption("r")) {
                printRawData();
                System.exit(0);
            }

            System.out.println(getInstance().toShortString());
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            System.out.println();
            printHelp(options);
            System.exit(1);
        }
    }
}