org.pentaho.reporting.libraries.base.versioning.VersionHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.libraries.base.versioning.VersionHelper.java

Source

/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2017 Hitachi Vantara..  All rights reserved.
*/

package org.pentaho.reporting.libraries.base.versioning;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

/**
 * A utility class for reading versioning information from a Manifest file.
 *
 * @author Thomas Morgner
 */
public class VersionHelper {
    private static class ManifestCache {
        private Map<String, Manifest> manifests;
        private Map<String, Manifest> manifestsByURL;

        public ManifestCache() {
            manifests = new HashMap<String, Manifest>();
            manifestsByURL = new HashMap<String, Manifest>();
        }

        public synchronized Manifest get(final String title) {
            return manifests.get(title);
        }

        public synchronized void set(final String title, final String url, final Manifest manifest) {
            if (title != null) {
                manifests.put(title, manifest);
            }
            manifestsByURL.put(url, manifest);
        }

        public synchronized Manifest getByURL(final String url) {
            return manifestsByURL.get(url);
        }
    }

    private static final Log logger = LogFactory.getLog(VersionHelper.class);
    private static final ManifestCache manifestCache = new ManifestCache();
    public static final String SNAPSHOT_TOKEN = "SNAPSHOT";
    private String version;
    private String title;
    private String productId;
    private String releaseMilestone;
    private String releaseMinor;
    private String releaseMajor;
    private String releaseNumber;
    private String releasePatch;
    private String releaseBuildNumber;
    private ProjectInformation projectInformation;

    /**
     * Loads the versioning information for the given project-information structure using the project information's
     * internal name as lookup key.
     *
     * @param projectInformation the project we load information for.
     */
    public VersionHelper(final ProjectInformation projectInformation) {
        if (projectInformation == null) {
            throw new NullPointerException();
        }

        this.projectInformation = projectInformation;

        Manifest manifest = manifestCache.get(projectInformation.getInternalName());
        if (manifest == null) {
            final ClassLoader loader = projectInformation.getClass().getClassLoader();
            try {
                final Enumeration resources = loader.getResources("META-INF/MANIFEST.MF");
                while (resources.hasMoreElements()) {
                    final URL url = (URL) resources.nextElement();
                    final String urlAsText = url.toURI().toString();
                    Manifest maybeManifest = manifestCache.getByURL(urlAsText);
                    if (maybeManifest == null) {
                        final InputStream inputStream = url.openStream();
                        try {
                            maybeManifest = new Manifest(new BufferedInputStream(inputStream));
                        } finally {
                            inputStream.close();
                        }
                    }

                    final Attributes attr = getAttributes(maybeManifest, projectInformation.getInternalName());
                    final String maybeTitle = getValue(attr, "Implementation-ProductID", null);
                    if (maybeTitle != null) {
                        manifestCache.set(maybeTitle, urlAsText, maybeManifest);
                        if (maybeTitle.equals(projectInformation.getInternalName())) {
                            manifest = maybeManifest;
                            break;
                        }
                    } else {
                        manifestCache.set(null, urlAsText, maybeManifest);
                    }
                }

            } catch (Exception e) {
                // Ignore; Maybe log.
                if (logger.isDebugEnabled()) {
                    logger.debug("Failed to read manifest for retrieving library version information for "
                            + projectInformation.getProductId(), e);
                }
            }
        }
        if (manifest != null) {
            init(manifest);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to create version information for " + projectInformation.getInternalName());
            }
            version = "TRUNK.development";
            title = projectInformation.getInternalName();
            productId = projectInformation.getInternalName();
            releaseMajor = "999";
            releaseMinor = "999";
            releaseMilestone = "999";
            releasePatch = "0";
            releaseBuildNumber = SNAPSHOT_TOKEN;
            releaseNumber = createReleaseVersion();
        }
    }

    /**
     * Initializes the instance, reaading the properties from the input stream.
     *
     * @param props the manifest.
     * @return true, if the manifest contains version information about this library, false otherwise.
     */
    private boolean init(final Manifest props) {
        try {
            final Attributes attr = getAttributes(props, projectInformation.getInternalName());
            final String maybeTitle = getValue(attr, "Implementation-ProductID", null);
            if (ObjectUtilities.equal(projectInformation.getInternalName(), maybeTitle) == false) {
                return false;
            }

            title = getValue(attr, "Implementation-Title", maybeTitle);
            version = getValue(attr, "Implementation-Version", "");

            parseVersion(version);

            productId = maybeTitle;
            if (productId.length() == 0) {
                productId = createProductId();
            }

            return true;
        } catch (final Exception e) {
            return false;
        }
    }

    protected void parseVersion(String version) {
        if (version == null || version.length() == 0) {
            version = "TRUNK.development";
        }

        releaseMajor = "999";
        releaseMinor = "999";
        releaseMilestone = "999";
        releasePatch = "0";
        releaseBuildNumber = SNAPSHOT_TOKEN;
        if (version.startsWith("TRUNK") == false) {
            // format is something like 3.8.0[.x]-GA.12345
            final int dashPos = version.indexOf('-');
            final String versionNumber;
            final String implIndicator;
            if (dashPos != -1) {
                versionNumber = version.substring(0, dashPos);
                implIndicator = version.substring(dashPos + 1);
            } else {
                versionNumber = version;
                implIndicator = "";
            }
            if (StringUtils.isEmpty(versionNumber) == false) {
                final StringTokenizer tokNum = new StringTokenizer(versionNumber, ".");
                if (tokNum.hasMoreTokens()) {
                    releaseMajor = tokNum.nextToken();
                }
                if (tokNum.hasMoreTokens()) {
                    releaseMinor = tokNum.nextToken();
                }
                if (tokNum.hasMoreTokens()) {
                    releaseMilestone = tokNum.nextToken();
                }
                if (tokNum.hasMoreTokens()) {
                    releasePatch = tokNum.nextToken();
                }
                final StringTokenizer tokImpl = new StringTokenizer(implIndicator, ".");
                if (tokImpl.hasMoreTokens()) {
                    releaseBuildNumber = tokImpl.nextToken();
                }
            }
        }
        releaseNumber = createReleaseVersion();
    }

    /**
     * Looks up the attributes for the given module specified by <code>name</code> in the given Manifest.
     *
     * @param props the manifest where to search for the attributes.
     * @param name  the name of the module.
     * @return the attributes for the module or the main attributes if the jar contains no such module.
     */
    private Attributes getAttributes(final Manifest props, final String name) {
        final Attributes attributes = props.getAttributes(name);
        if (attributes == null) {
            return props.getMainAttributes();
        }
        return attributes;
    }

    /**
     * Looks up a single value in the given attribute collection using the given key. If the key is not contained in the
     * attributes, this method returns the default value specified as parameter.
     *
     * @param attrs        the attributes where to lookup the key.
     * @param name         the name of the key to use for the lookup.
     * @param defaultValue the default value to return in case the attributes contain no such key.
     * @return the value from the attributes or the default values.
     */
    private String getValue(final Attributes attrs, final String name, final String defaultValue) {
        final String value = attrs.getValue(name);
        if (value == null) {
            return defaultValue;
        }
        return value.trim();
    }

    /**
     * Creates a product-id string, which is the implementation title plus the optional version information.
     *
     * @return the product id string.
     */
    private String createProductId() {
        if (version.trim().length() == 0) {
            return title;
        }
        return title + '-' + version;
    }

    /**
     * Creates a version string using the major, minor and milestone version information and the build number.
     *
     * @return the release version.
     */
    private String createReleaseVersion() {
        final StringBuilder buffer = new StringBuilder(50);
        buffer.append(releaseMajor);
        buffer.append('.');
        buffer.append(releaseMinor);
        buffer.append('.');
        buffer.append(releaseMilestone);
        if (releasePatch.length() > 0) {
            buffer.append('-');
            buffer.append(releasePatch);
        }
        if (releaseBuildNumber.length() > 0) {
            buffer.append(" (Build ");
            buffer.append(releaseBuildNumber);
            buffer.append(')');
        }
        return buffer.toString();
    }

    /**
     * Returns the full version string as computed by createVersion().
     *
     * @return the version string.
     */
    public String getVersion() {
        return version;
    }

    /**
     * Returns the implementation title as specified in the manifest.
     *
     * @return the implementation title.
     */
    public String getTitle() {
        return title;
    }

    /**
     * Returns the product id as computed by createProductId().
     *
     * @return the product id.
     * @see #createProductId()
     */
    public String getProductId() {
        return productId;
    }

    /**
     * Returns the release milestone number. Defaults to 999 if not given in the manifest.
     *
     * @return the milestone number.
     */
    public String getReleaseMilestone() {
        return releaseMilestone;
    }

    /**
     * Returns the release minor number. Defaults to 999 if not given in the manifest.
     *
     * @return the minor version number.
     */
    public String getReleaseMinor() {
        return releaseMinor;
    }

    /**
     * Returns the release major number. Defaults to 999 if not given in the manifest.
     *
     * @return the major version number.
     */
    public String getReleaseMajor() {
        return releaseMajor;
    }

    /**
     * Returns the release candidate token. Defaults to 999 if not given in the manifest.
     *
     * @return the candidate token.
     * @deprecated No longer used.
     */
    @Deprecated
    public String getReleaseCandidateToken() {
        return "";
    }

    /**
     * Returns the release patch number. Defaults to zero if not given in the manifest.
     *
     * @return the patch version number.
     */
    public String getReleasePatch() {
        return releasePatch;
    }

    /**
     * Returns the release number.
     *
     * @return the release number.
     */
    public String getReleaseNumber() {
        return releaseNumber;
    }

    /**
     * Returns the release build number.
     *
     * @return the build-number).
     */
    public String getReleaseBuildNumber() {
        return releaseBuildNumber;
    }
}