| /* |
| * ==================================================================== |
| * 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. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| */ |
| |
| package org.apache.hc.core5.util; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| /** |
| * Provides access to version information for HTTP components. |
| * Static methods are used to extract version information from property |
| * files that are automatically packaged with HTTP component release JARs. |
| * <p> |
| * All available version information is provided in strings, where |
| * the string format is informal and subject to change without notice. |
| * Version information is provided for debugging output and interpretation |
| * by humans, not for automated processing in applications. |
| * </p> |
| * |
| * @since 4.0 |
| */ |
| public class VersionInfo { |
| |
| /** A string constant for unavailable information. */ |
| public final static String UNAVAILABLE = "UNAVAILABLE"; |
| |
| /** The filename of the version information files. */ |
| public final static String VERSION_PROPERTY_FILE = "version.properties"; |
| |
| // the property names |
| public final static String PROPERTY_MODULE = "info.module"; |
| public final static String PROPERTY_RELEASE = "info.release"; |
| /** |
| * @deprecated This will be removed in 6.0. |
| */ |
| @Deprecated |
| public final static String PROPERTY_TIMESTAMP = "info.timestamp"; |
| |
| |
| /** The package that contains the version information. */ |
| private final String infoPackage; |
| |
| /** The module from the version info. */ |
| private final String infoModule; |
| |
| /** The release from the version info. */ |
| private final String infoRelease; |
| |
| /** The timestamp from the version info. */ |
| private final String infoTimestamp; |
| |
| /** The classloader from which the version info was obtained. */ |
| private final String infoClassloader; |
| |
| /** |
| * An empty immutable {@code VersionInfo} array. |
| */ |
| private static final VersionInfo[] EMPTY_VERSION_INFO_ARRAY = new VersionInfo[0]; |
| |
| |
| /** |
| * Instantiates version information. |
| * |
| * @param pckg the package |
| * @param module the module, or {@code null} |
| * @param release the release, or {@code null} |
| * @param time the build time, or {@code null} |
| * @param clsldr the class loader, or {@code null} |
| */ |
| protected VersionInfo(final String pckg, final String module, |
| final String release, final String time, final String clsldr) { |
| Args.notNull(pckg, "Package identifier"); |
| infoPackage = pckg; |
| infoModule = (module != null) ? module : UNAVAILABLE; |
| infoRelease = (release != null) ? release : UNAVAILABLE; |
| infoTimestamp = (time != null) ? time : UNAVAILABLE; |
| infoClassloader = (clsldr != null) ? clsldr : UNAVAILABLE; |
| } |
| |
| |
| /** |
| * Obtains the package name. |
| * The package name identifies the module or informal unit. |
| * |
| * @return the package name, never {@code null} |
| */ |
| public final String getPackage() { |
| return infoPackage; |
| } |
| |
| /** |
| * Obtains the name of the versioned module or informal unit. |
| * This data is read from the version information for the package. |
| * |
| * @return the module name, never {@code null} |
| */ |
| public final String getModule() { |
| return infoModule; |
| } |
| |
| /** |
| * Obtains the release of the versioned module or informal unit. |
| * This data is read from the version information for the package. |
| * |
| * @return the release version, never {@code null} |
| */ |
| public final String getRelease() { |
| return infoRelease; |
| } |
| |
| /** |
| * Obtains the timestamp of the versioned module or informal unit. |
| * This data is read from the version information for the package. |
| * |
| * @return the timestamp, never {@code null} |
| * @deprecated This will be removed in 6.0. |
| */ |
| @Deprecated |
| public final String getTimestamp() { |
| return infoTimestamp; |
| } |
| |
| /** |
| * Obtains the classloader used to read the version information. |
| * This is just the {@code toString} output of the classloader, |
| * since the version information should not keep a reference to |
| * the classloader itself. That could prevent garbage collection. |
| * |
| * @return the classloader description, never {@code null} |
| */ |
| public final String getClassloader() { |
| return infoClassloader; |
| } |
| |
| |
| /** |
| * Provides the version information in human-readable format. |
| * |
| * @return a string holding this version information |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder |
| (20 + infoPackage.length() + infoModule.length() + |
| infoRelease.length() + infoTimestamp.length() + |
| infoClassloader.length()); |
| |
| sb.append("VersionInfo(") |
| .append(infoPackage).append(':').append(infoModule); |
| |
| // If version info is missing, a single "UNAVAILABLE" for the module |
| // is sufficient. Everything else just clutters the output. |
| if (!UNAVAILABLE.equals(infoRelease)) { |
| sb.append(':').append(infoRelease); |
| } |
| |
| sb.append(')'); |
| |
| if (!UNAVAILABLE.equals(infoClassloader)) { |
| sb.append('@').append(infoClassloader); |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * Loads version information for a list of packages. |
| * |
| * @param pckgs the packages for which to load version info |
| * @param clsldr the classloader to load from, or |
| * {@code null} for the thread context classloader |
| * |
| * @return the version information for all packages found, |
| * never {@code null} |
| */ |
| public static VersionInfo[] loadVersionInfo(final String[] pckgs, |
| final ClassLoader clsldr) { |
| Args.notNull(pckgs, "Package identifier array"); |
| final List<VersionInfo> vil = new ArrayList<>(pckgs.length); |
| for (final String pckg : pckgs) { |
| final VersionInfo vi = loadVersionInfo(pckg, clsldr); |
| if (vi != null) { |
| vil.add(vi); |
| } |
| } |
| |
| return vil.toArray(EMPTY_VERSION_INFO_ARRAY); |
| } |
| |
| |
| /** |
| * Loads version information for a package. |
| * |
| * @param pckg the package for which to load version information, |
| * for example "org.apache.http". |
| * The package name should NOT end with a dot. |
| * @param clsldr the classloader to load from, or |
| * {@code null} for the thread context classloader |
| * |
| * @return the version information for the argument package, or |
| * {@code null} if not available |
| */ |
| public static VersionInfo loadVersionInfo(final String pckg, final ClassLoader clsldr) { |
| Args.notNull(pckg, "Package identifier"); |
| final ClassLoader cl = clsldr != null ? clsldr : Thread.currentThread().getContextClassLoader(); |
| |
| Properties vip = null; // version info properties, if available |
| try { |
| // org.apache.http becomes |
| // org/apache/http/version.properties |
| try (final InputStream is = cl.getResourceAsStream(pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE)) { |
| if (is != null) { |
| final Properties props = new Properties(); |
| props.load(is); |
| vip = props; |
| } |
| } |
| } catch (final IOException ex) { |
| // shamelessly munch this exception |
| } |
| |
| VersionInfo result = null; |
| if (vip != null) { |
| result = fromMap(pckg, vip, cl); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Instantiates version information from properties. |
| * |
| * @param pckg the package for the version information |
| * @param info the map from string keys to string values, |
| * for example {@link java.util.Properties} |
| * @param clsldr the classloader, or {@code null} |
| * |
| * @return the version information |
| */ |
| protected static VersionInfo fromMap(final String pckg, final Map<?, ?> info, |
| final ClassLoader clsldr) { |
| Args.notNull(pckg, "Package identifier"); |
| String module = null; |
| String release = null; |
| |
| if (info != null) { |
| module = (String) info.get(PROPERTY_MODULE); |
| if ((module != null) && (module.length() < 1)) { |
| module = null; |
| } |
| |
| release = (String) info.get(PROPERTY_RELEASE); |
| if ((release != null) && ((release.length() < 1) || |
| (release.equals("${project.version}")))) { |
| release = null; |
| } |
| } // if info |
| |
| String clsldrstr = null; |
| if (clsldr != null) { |
| clsldrstr = clsldr.toString(); |
| } |
| |
| return new VersionInfo(pckg, module, release, null, clsldrstr); |
| } |
| |
| /** |
| * Gets software information as {@code "<name>/<release> (Java/<java.version>)"}. If release is |
| * {@link #UNAVAILABLE}, it will be omitted. |
| * <p> |
| * For example: |
| * <pre>"Apache-HttpClient/4.3 (Java/1.6.0_35)"</pre> |
| * |
| * @param name the component name, like "Apache-HttpClient". |
| * @param pkg |
| * the package for which to load version information, for example "org.apache.http". The package name |
| * should NOT end with a dot. |
| * @param cls |
| * the class' class loader to load from, or {@code null} for the thread context class loader |
| * @since 4.3 |
| */ |
| public static String getSoftwareInfo(final String name, final String pkg, final Class<?> cls) { |
| // determine the release version from packaged version info |
| final VersionInfo vi = VersionInfo.loadVersionInfo(pkg, cls.getClassLoader()); |
| final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; |
| final String javaVersion = System.getProperty("java.version"); |
| |
| String nameAndRelease = name; |
| if (!UNAVAILABLE.equals(release)) { |
| nameAndRelease += "/" + release; |
| } |
| |
| return String.format("%s (Java/%s)", nameAndRelease, javaVersion); |
| } |
| |
| } |