/* * Copyright (c) 2008-2017, Hazelcast, 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. */ package com.hazelcast.instance; import com.hazelcast.core.HazelcastException; import com.hazelcast.logging.ILogger; import com.hazelcast.logging.Logger; import java.io.InputStream; import java.lang.reflect.Field; import java.util.Properties; import static com.hazelcast.nio.IOUtil.closeResource; import static com.hazelcast.util.EmptyStatement.ignore; /** * Provides information about current Hazelcast build. */ public final class BuildInfoProvider { /** * Use this in production code to obtain the BuildInfo already parsed when this class was first loaded. * Its properties will not change at runtime. */ public static final BuildInfo BUILD_INFO; public static final String HAZELCAST_INTERNAL_OVERRIDE_VERSION = "hazelcast.internal.override.version"; private static final ILogger LOGGER; static { LOGGER = Logger.getLogger(BuildInfoProvider.class); BUILD_INFO = getBuildInfo(); } private BuildInfoProvider() { } /** * Parses {@code hazelcast-runtime.properties} for {@code BuildInfo}; also checks for overrides in System.properties. * Use this method to obtain and cache a {@code BuildInfo} object or from test code that needs to re-parse properties * on each invocation. * * @return the parsed BuildInfo */ public static BuildInfo getBuildInfo() { // If you have a compilation error at GeneratedBuildProperties then run 'mvn clean install' // the GeneratedBuildProperties class is generated at a compile-time BuildInfo buildInfo = readBuildPropertiesClass(GeneratedBuildProperties.class, null); try { Class<?> enterpriseClass = BuildInfoProvider.class.getClassLoader() .loadClass("com.hazelcast.instance.GeneratedEnterpriseBuildProperties"); if (enterpriseClass.getClassLoader() == BuildInfoProvider.class.getClassLoader()) { //only read the enterprise properties if there were loaded by the same classloader //as BuildInfoProvider and not e.g. a parent classloader. buildInfo = readBuildPropertiesClass(enterpriseClass, buildInfo); } } catch (ClassNotFoundException e) { ignore(e); } Properties jetProperties = loadPropertiesFromResource("jet-runtime.properties"); setJetProperties(jetProperties, buildInfo); return buildInfo; } static void setJetProperties(Properties properties, BuildInfo buildInfo) { if (properties.isEmpty()) { return; } String version = properties.getProperty("jet.version"); String build = properties.getProperty("jet.build"); String revision = properties.getProperty("jet.git.revision"); JetBuildInfo jetBuildInfo = new JetBuildInfo(version, build, revision); buildInfo.setJetBuildInfo(jetBuildInfo); } private static Properties loadPropertiesFromResource(String resourceName) { InputStream properties = BuildInfoProvider.class.getClassLoader().getResourceAsStream(resourceName); Properties runtimeProperties = new Properties(); try { if (properties != null) { runtimeProperties.load(properties); } } catch (Exception ignored) { ignore(ignored); } finally { closeResource(properties); } return runtimeProperties; } private static BuildInfo readBuildPropertiesClass(Class<?> clazz, BuildInfo upstreamBuildInfo) { String version = readStaticStringField(clazz, "VERSION"); String build = readStaticStringField(clazz, "BUILD"); String revision = readStaticStringField(clazz, "REVISION"); String distribution = readStaticStringField(clazz, "DISTRIBUTION"); if (!revision.isEmpty() && revision.equals("${git.commit.id.abbrev}")) { revision = ""; } int buildNumber = Integer.parseInt(build); boolean enterprise = !"Hazelcast".equals(distribution); String serialVersionString = readStaticStringField(clazz, "SERIALIZATION_VERSION"); byte serialVersion = Byte.parseByte(serialVersionString); return overrideBuildInfo(version, build, revision, buildNumber, enterprise, serialVersion, upstreamBuildInfo); } private static BuildInfo overrideBuildInfo(String version, String build, String revision, int buildNumber, boolean enterprise, byte serialVersion, BuildInfo upstreamBuildInfo) { Integer hazelcastBuild = Integer.getInteger("hazelcast.build", -1); if (hazelcastBuild != -1) { build = String.valueOf(hazelcastBuild); buildNumber = hazelcastBuild; } String overridingVersion = System.getProperty(HAZELCAST_INTERNAL_OVERRIDE_VERSION); if (overridingVersion != null) { LOGGER.info("Overriding hazelcast version with system property value " + overridingVersion); version = overridingVersion; } return new BuildInfo(version, build, revision, buildNumber, enterprise, serialVersion, upstreamBuildInfo); } //todo: move elsewhere private static String readStaticStringField(Class<?> clazz, String fieldName) { try { Field field = clazz.getField(fieldName); return (String) field.get(null); } catch (NoSuchFieldException e) { throw new HazelcastException(e); } catch (IllegalAccessException e) { throw new HazelcastException(e); } } }