blob: f9517fe3bf4b1dfbe40a06d3f2f3ffd20444ef47 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.qpid.server.configuration;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Centralised record of Qpid common properties.
*
* Qpid build specific information like project name, version number, and source code repository revision number
* are captured by this class and exposed via public static methods.
*
*/
public class CommonProperties
{
private static final Logger LOGGER = LoggerFactory.getLogger(CommonProperties.class);
/**
* The timeout used by the IO layer for timeouts such as send timeout in IoSender, and the close timeout for IoSender and IoReceiver
*/
public static final String IO_NETWORK_TRANSPORT_TIMEOUT_PROP_NAME = "qpid.io_network_transport_timeout";
public static final int IO_NETWORK_TRANSPORT_TIMEOUT_DEFAULT = 60000;
public static final String QPID_SECURITY_TLS_PROTOCOL_ALLOW_LIST = "qpid.security.tls.protocolAllowList";
public static final String QPID_SECURITY_TLS_PROTOCOL_ALLOW_LIST_DEFAULT = "TLSv1\\.[0-9]+";
public static final String QPID_SECURITY_TLS_PROTOCOL_DENY_LIST = "qpid.security.tls.protocolDenyList";
public static final String QPID_SECURITY_TLS_PROTOCOL_DENY_LIST_DEFAULT = "TLSv1\\.[0-1]";
public static final String QPID_SECURITY_TLS_CIPHER_SUITE_ALLOW_LIST = "qpid.security.tls.cipherSuiteAllowList";
public static final String QPID_SECURITY_TLS_CIPHER_SUITE_ALLOW_LIST_DEFAULT = "";
public static final String QPID_SECURITY_TLS_CIPHER_SUITE_DENY_LIST = "qpid.security.tls.cipherSuiteDenyList";
public static final String QPID_SECURITY_TLS_CIPHER_SUITE_DENY_LIST_DEFAULT = "";
private static final String MANIFEST_HEADER_IMPLEMENTATION_BUILD = "Implementation-Build";
/** Defines the name of the version suffix property. */
private static final String RELEASE_VERSION_SUFFIX = "qpid.version.suffix";
/** Defines the default value for all properties that cannot be loaded. */
private static final String DEFAULT = "unknown";
/** Holds the product name. */
private static final String productName;
/** Holds the product version. */
private static final String releaseVersion;
/** Holds the source code revision. */
private static final String buildVersion;
private static final Properties properties = new Properties();
private static final String QPID_VERSION = "qpid.version";
static
{
Manifest jarManifest = getJarManifestFor(CommonProperties.class);
Attributes mainAttributes = jarManifest.getMainAttributes();
Package p = CommonProperties.class.getPackage();
buildVersion = mainAttributes.getValue(MANIFEST_HEADER_IMPLEMENTATION_BUILD) != null ? mainAttributes.getValue(MANIFEST_HEADER_IMPLEMENTATION_BUILD) : DEFAULT;
productName = p.getImplementationTitle() != null ? p.getImplementationTitle() : DEFAULT;
String version = getImplementationVersion(p);
System.setProperty(QPID_VERSION, version);
boolean loadFromFile = true;
String initialProperties = System.getProperty("qpid.common_properties_file");
if (initialProperties == null)
{
initialProperties = "qpid-common.properties";
loadFromFile = false;
}
loadProperties(properties, initialProperties, loadFromFile);
String versionSuffix = properties.getProperty(RELEASE_VERSION_SUFFIX);
releaseVersion = versionSuffix == null || "".equals(versionSuffix) ? version : version + ";" + versionSuffix;
Set<String> propertyNames = new HashSet<>(properties.stringPropertyNames());
propertyNames.removeAll(System.getProperties().stringPropertyNames());
for (String propName : propertyNames)
{
System.setProperty(propName, properties.getProperty(propName));
}
}
public static void ensureIsLoaded()
{
//noop; to call from static initialization blocks of other classes to provoke CommonProperties class initialization
}
public static Properties asProperties()
{
return new Properties(properties);
}
/**
* Gets the product name.
*
* @return The product name.
*/
public static String getProductName()
{
return productName;
}
/**
* Gets the product version.
*
* @return The product version.
*/
public static String getReleaseVersion()
{
return releaseVersion;
}
/**
* Gets the source code revision.
*
* @return The source code revision.
*/
public static String getBuildVersion()
{
return buildVersion;
}
/**
* Extracts all of the version information as a printable string.
*
* @return All of the version information as a printable string.
*/
public static String getVersionString()
{
return getProductName() + " - " + getReleaseVersion() + " build: " + getBuildVersion();
}
private CommonProperties()
{
//no instances
}
private static void loadProperties(Properties properties, String resourceLocation, boolean loadFromFile)
{
try
{
URL propertiesResource;
if (loadFromFile)
{
propertiesResource = (new File(resourceLocation)).toURI().toURL();
}
else
{
propertiesResource = CommonProperties.class.getClassLoader().getResource(resourceLocation);
}
if (propertiesResource != null)
{
try (InputStream propertyStream = propertiesResource.openStream())
{
if (propertyStream != null)
{
properties.load(propertyStream);
}
}
catch (IOException e)
{
LOGGER.warn("Could not load properties file '{}'.", resourceLocation, e);
}
}
}
catch (MalformedURLException e)
{
LOGGER.warn("Could not open properties file '{}'.", resourceLocation, e);
}
}
private static String getImplementationVersion(final Package p)
{
String version = p.getImplementationVersion();
if (version == null)
{
version = DEFAULT;
final String path = CommonProperties.class.getPackage().getName().replace(".", "/");
final String fallbackPath = "/" + path + "/fallback-version.txt";
final InputStream in = CommonProperties.class.getResourceAsStream(fallbackPath);
if (in != null)
{
try(BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.US_ASCII)))
{
version = reader.readLine();
}
catch (Exception e)
{
LOGGER.trace("Problem reading version from fallback resource : {} ", fallbackPath, e);
}
}
}
return version;
}
private static Manifest getJarManifestFor(final Class<?> clazz)
{
final Manifest emptyManifest = new Manifest();
String className = clazz.getSimpleName() + ".class";
String classPath = clazz.getResource(className).toString();
if (!classPath.startsWith("jar"))
{
return emptyManifest;
}
String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) +
"/META-INF/MANIFEST.MF";
try (InputStream is = new URL(manifestPath).openStream())
{
return new Manifest(is);
}
catch (IOException e)
{
// Ignore
}
return emptyManifest;
}
}