null
* @param release the release, or null
* @param time the build time, or null
* @param clsldr the class loader, or 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 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 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 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 null
*/
public final String getTimestamp() {
return infoTimestamp;
}
/**
* Obtains the classloader used to read the version information.
* This is just the 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 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);
}
if (!UNAVAILABLE.equals(infoTimestamp)) {
sb.append(':').append(infoTimestamp);
}
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
* null
for the thread context classloader
*
* @return the version information for all packages found,
* never null
*/
public static VersionInfo[] loadVersionInfo(final String[] pckgs,
final ClassLoader clsldr) {
Args.notNull(pckgs, "Package identifier array");
final Listnull
for the thread context classloader
*
* @return the version information for the argument package, or
* 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 {
// ch.boye.httpclientandroidlib becomes
// org/apache/http/version.properties
final InputStream is = cl.getResourceAsStream
(pckg.replace('.', '/') + "/" + VERSION_PROPERTY_FILE);
if (is != null) {
try {
final Properties props = new Properties();
props.load(is);
vip = props;
} finally {
is.close();
}
}
} 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 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;
String timestamp = 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("${pom.version}")))) {
release = null;
}
timestamp = (String) info.get(PROPERTY_TIMESTAMP);
if ((timestamp != null) &&
((timestamp.length() < 1) ||
(timestamp.equals("${mvn.timestamp}")))
) {
timestamp = null;
}
} // if info
String clsldrstr = null;
if (clsldr != null) {
clsldrstr = clsldr.toString();
}
return new VersionInfo(pckg, module, release, timestamp, clsldrstr);
}
/**
* Sets the user agent to {@code ""Apache-HttpClient/4.3 (Java 1.5 minimum; Java/1.6.0_35)"* * @param name the component name, like "Apache-HttpClient". * @param pkg * the package for which to load version information, for example "ch.boye.httpclientandroidlib". The package name * should NOT end with a dot. * @param cls * the class' class loader to load from, or
null
for the thread context class loader
* @since 4.3
*/
public static String getUserAgent(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");
return name + "/" + release + " (Java 1.5 minimum; Java/" + javaVersion + ")";
}
} // class VersionInfo