
net.snowflake.common.core.ComponentInfo Maven / Gradle / Ivy
/*
* Copyright (c) 2013 Snowflake Computing Inc. All right reserved.
*/
package net.snowflake.common.core;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.InputStream;
import java.net.URI;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
/**
* Component information, like version, SVN rev, etc is available through this
* class.
*
* It works by interrogating the class passed to the initialize() method.
* Current implementation retrieves the version information from the jar that
* the passed in class had been loaded from, if any.
*
* @author ppovinec
*/
public class ComponentInfo
{
/**
* Supported version format pattern
*/
public static final String VERSION_FORMAT_PATTERN =
"[0-9]+(\\.[0-9]+)(\\.[0-9]+)(\\.[0-9a-zA-Z_-]+)?";
/**
* The delimiter between the numbers in the version number
*/
private static final String VERSION_DELIMITER = "\\.";
/**
* Version info - purely a data class. All fields set to -1 if the value could
* not be determined.
*/
public static class Version
{
private final int majorVersion;
private final int minorVersion;
private final int patchVersion;
private String buildId;
public Version()
{
this.majorVersion = 0;
this.minorVersion = 0;
this.patchVersion = 0;
this.buildId = null;
}
public Version(int majorVersion,
int minorVersion,
int patchVersion,
String buildId)
{
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.patchVersion = patchVersion;
this.buildId = buildId;
}
public Version(String version)
{
// only initialize if version has correct format
if (version != null && version.matches(VERSION_FORMAT_PATTERN))
{
String[] versionDecomposed = version.split(VERSION_DELIMITER);
if (versionDecomposed.length >= 3)
{
this.majorVersion = Integer.parseInt(versionDecomposed[0]);
this.minorVersion = Integer.parseInt(versionDecomposed[1]);
this.patchVersion = Integer.parseInt(versionDecomposed[2]);
return;
}
}
this.majorVersion = 0;
this.minorVersion = 0;
this.patchVersion = 0;
}
/**
* Generate an absolute version ranking allowing to compare two versions
* @return version rank
*/
@JsonIgnore
public long getVersionRank()
{
return (((long)this.majorVersion) << 32) +
(((long)this.minorVersion) << 16) +
((long)this.patchVersion);
}
public int getMajorVersion()
{
return majorVersion;
}
public int getMinorVersion()
{
return minorVersion;
}
public int getPatchVersion()
{
return patchVersion;
}
public String getBuildId()
{
return buildId;
}
/**
* Backward compatibility, extract svn revision from build id
* @return SVN revision
*/
@JsonIgnore
public int getSvnRevisionObsolete()
{
try
{
return (buildId != null)? Integer.parseInt(this.buildId) : -1;
}
catch (NumberFormatException ex)
{
return 0;
}
}
/**
* Return the GS version number as a string so that it can be displayed by the
* UI
* @param showBuildNumber true if we should add the build number (internal)
* @return version of that GS instance as a string
*/
public String getVersionAsString(boolean showBuildNumber)
{
String versionStr;
if (majorVersion == -1)
{
versionStr = "Dev";
}
else
{
versionStr = ((majorVersion > -1) ? majorVersion : 0) + "." +
((minorVersion > -1) ? minorVersion : 0) + "." +
((patchVersion > -1) ? patchVersion : 0);
if (majorVersion == 0)
versionStr += " (Beta)";
}
if (showBuildNumber && buildId != null)
{
versionStr += " b" + buildId;
}
return versionStr;
}
/**
* This is to be able to de-serialize old export files
* @param svnRevision svn revision number
*/
public void setSvnRevision(int svnRevision)
{
this.buildId = String.valueOf(svnRevision);
}
@Override
public String toString()
{
return "majorVersion=" + majorVersion +
", minorVersion=" + minorVersion +
", patchVersion=" + patchVersion +
", buildId=" + buildId;
}
}
private static Version theVersion;
private static boolean initialized = false;
/**
* Get the version info part of the ComponentInfo.
*
* @return version info
* @throws IllegalStateException if the ComponentInfo has not been initialized
*/
public static Version getVersion()
{
if (!initialized)
{
throw new IllegalStateException("ComponentInfo not initialized");
}
return theVersion;
}
/**
* Initialize the ComponentInfo by retrieving all the necessary information
* from the available sources.
*
* @throws Exception if unable to complete the initialization
*/
public static void initialize() throws Exception
{
initialize(net.snowflake.common.core.ComponentInfo.class);
}
/**
* Initialize the ComponentInfo by retrieving all the necessary information
* from the available sources.
*
* @param klass that should be interrogated
* @throws Exception if unable to complete the initialization
*/
public static void initialize(Class> klass) throws Exception
{
String implVersion;
String buildId;
// Get the URI of the source of the passed in class.
URI jarURI
= klass.getProtectionDomain()
.getCodeSource().
getLocation().toURI();
// If this class is not loaded from a jar, assume we are in dev, and all
// versions are returned as -1.
if (!jarURI.toString().endsWith(".jar"))
{
theVersion = new Version(-1, -1, -1, null);
initialized = true;
return;
}
// Open the jar as a stream and read its manifest.
InputStream is = null;
try
{
is = jarURI.toURL().openStream();
if (is == null)
{
throw new RuntimeException("Couldn't open jar:" + jarURI);
}
JarInputStream jarStream = new JarInputStream(is);
Manifest mf = jarStream.getManifest();
if (mf == null)
{
throw new IllegalStateException("Couldn't find manifest in:"
+ jarURI.toURL());
}
// Parse the manifest to retrieve implementation version and SVN version.
Attributes mainAttribs = mf.getMainAttributes();
implVersion = mainAttribs.getValue("Snowflake-Version");
buildId = mainAttribs.getValue("Snowflake-BuildId");
}
catch (Exception ex)
{
// Simply rethrow...
throw ex;
}
finally
{
if (is != null)
{
is.close();
}
}
// Parse implementation version as major.minor. Anything that doesn't
// conform is silently translated to -1.-1 (this is mostly to workaround
// the version strings like 1.0-SNAPSHOT in dev.
int major = -1;
int minor = -1;
int patch = -1;
if (implVersion != null)
{
try
{
String[] majorMinorPatch = implVersion.split("\\.");
if (majorMinorPatch != null && majorMinorPatch.length >= 2)
{
major = Integer.parseInt(majorMinorPatch[0]);
minor = Integer.parseInt(majorMinorPatch[1]);
if (majorMinorPatch.length >= 3)
patch = Integer.parseInt(majorMinorPatch[2]);
else
patch = 0;
}
}
catch (Exception ex)
{
// Swallow. Will use default of -1.
}
}
// Only Version in ComponentInfo now, so we're done with the initialization.
theVersion = new Version(major, minor, patch, buildId);
initialized = true;
}
}