de.triology.versionname.VersionNames Maven / Gradle / Ivy
/**
* The MIT License (MIT)
*
* Copyright (c) 2016 TRIOLOGY GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package de.triology.versionname;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
/**
* Provides access to version names written to files such as the manifest or a properties file.
*/
public class VersionNames {
private static final Logger LOG = LoggerFactory.getLogger(VersionNames.class);
/**
* The path of the properties file that is used for looking up the version name by default.
*/
static final String DEFAULT_PROPERTIES_FILE_PATH = "/app.properties";
/**
* The property within {@link #DEFAULT_PROPERTIES_FILE_PATH} that is used for looking up the version name by
* default.
*/
static final String DEFAULT_PROPERTY = "versionName";
/**
* The path of the manifest that is used for looking up the version name by default.
*/
static final String DEFAULT_MANIFEST_PATH = "META-INF/MANIFEST.MF";
/**
* The attribute within {@link #DEFAULT_MANIFEST_PATH} that is used for looking up the version name by
* default.
*/
static final String DEFAULT_MANIFEST_ATTRIBUTE = "versionName";
/**
* The version string that is returned if anything goes wrong.
*/
static final String VERSION_STRING_ON_ERROR = "";
/**
* Class that allows access to resources. Can be overwritten in a test.
*/
private static ClassLoader classLoader = VersionNames.class.getClassLoader();
/**
* Utility class. Do not instantiate.
*/
private VersionNames() {
}
/**
* Reads the version name from a default properties file /app.properties
and property
* versionName
.
*
* @return the version name or empty string if anything goes wrong. In case of error, see log for details.
*/
public static String getVersionNameFromProperties() {
return getVersionNameFromProperties(DEFAULT_PROPERTIES_FILE_PATH, DEFAULT_PROPERTY);
}
/**
* Reads the version name from propertiesFilePath
and property
.
*
* @param propertiesFilePath path to the properties file to open from classpath, relative to this class.
* @param property property within propertiesFilePath
that is used for looking up the version name
* @return the version name or empty string if anything goes wrong. In case of error, see log for details.
*/
public static String getVersionNameFromProperties(String propertiesFilePath, String property) {
return new VersionName() {
@Override
protected String handleResourceStream(InputStream resourceAsStream, String key) throws IOException {
Properties props = new Properties();
props.load(resourceAsStream);
Object versionNameObject = props.get(key);
// Properties.get() seems to always return strings. But: In theory, it could return any other object.
if (versionNameObject instanceof String) {
return (String) versionNameObject;
} else if (versionNameObject != null) {
return versionNameObject.toString();
} else {
return null;
}
}
}.fromResource(propertiesFilePath, property);
}
/**
* Reads the version name from manifest file located at META-INF/MANIFEST.MF
and attribute
* versionName
.
*
* @return the version name or empty string if anything goes wrong. In case of error, see log for details.
*/
public static String getVersionNameFromManifest() {
return getVersionNameFromManifest(DEFAULT_MANIFEST_PATH, DEFAULT_MANIFEST_ATTRIBUTE);
}
/**
* Reads the version name from manifestFilePath
and attribute
.
*
* @param manifestFilePath path to the manifest file to open from classpath, relative to this class.
* @param attribute attribute within manifestFilePath
that is used for looking up the version name
* @return the version name or empty string if anything goes wrong. In case of error, see log for details.
*/
public static String getVersionNameFromManifest(String manifestFilePath, String attribute) {
return new VersionName() {
@Override
protected String handleResourceStream(InputStream resourceAsStream, String key) throws IOException {
Manifest manifest = new Manifest(resourceAsStream);
Attributes attributes = manifest.getMainAttributes();
return attributes.getValue(key);
}
}.fromResource(manifestFilePath, attribute);
}
/**
* Handles the generic part of version number loading. Gets specific resource stream from classloader and takes care
* of the error handling, logging and stream closing.
* Concrete classes need to implement the logic for getting the resource stream in
* {@link #handleResourceStream(InputStream, String)}.
*/
abstract static class VersionName {
static final String LOG_RESOURCE_NOT_FOUND = "Cannot read version name. Resource \"{}\" not found on classpath";
static final String LOG_KEY_NULL = "Cannot read version name from resource. Key is null";
static final String LOG_RESOURCE_PATH_NULL = "Cannot read version name. Resource path is null";
static final String LOG_NOT_FOUND_IN_RESOURCE = "Version name not found in {}";
static final String LOG_EXCEPTION_READING_FROM_RESOURCE = "Exception while reading version name from {}";
static final String LOG_EXCEPTION_ON_CLOSE = "Unable to close resource stream after reading version number";
/**
* Template method that implements the actual version number logic.
*
* @param resourceStream the resource to load the version number from. Never null
* @param key the key within the resource stream. Never null
* @return the version number, or null
, if none found
* @throws IOException - if an I/O error has occurred
*/
protected abstract String handleResourceStream(InputStream resourceStream, String key) throws IOException;
/**
* Get the version name from the specified resourcePath
at the specified key
.
*
* @param resourcePath path to the resource to open from classpath, relative to this class. Can be null
* @param key the key within the resource stream. Can be null
* @return the version number or {@link #VERSION_STRING_ON_ERROR}, if none found. Never null
*/
public String fromResource(String resourcePath, String key) {
String versionName = VERSION_STRING_ON_ERROR;
if (resourcePath == null) {
LOG.error(LOG_RESOURCE_PATH_NULL);
} else if (key == null) {
LOG.error(LOG_KEY_NULL);
} else {
versionName = processResource(resourcePath, key);
}
// Never return null
if (versionName == null) {
LOG.error(LOG_NOT_FOUND_IN_RESOURCE, resourcePath);
versionName = VERSION_STRING_ON_ERROR;
}
return versionName;
}
/**
* Actual logic for opening and closing the stream. Calls template method
* {@link #handleResourceStream(InputStream, String)}.
*/
private String processResource(String resourcePath, String key) {
// Actual reading logic
InputStream resourceStream = classLoader.getResourceAsStream(resourcePath);
try {
if (resourceStream != null) {
return handleResourceStream(resourceStream, key);
} else {
LOG.error(LOG_RESOURCE_NOT_FOUND, resourcePath);
}
} catch (IOException e) {
LOG.error(LOG_EXCEPTION_READING_FROM_RESOURCE, resourcePath, e);
} finally {
if (resourceStream != null) {
try {
resourceStream.close();
} catch (IOException e) {
LOG.warn(LOG_EXCEPTION_ON_CLOSE, e);
}
}
}
return null;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy