/*******************************************************************************
* Copyright 2014 butor.com
*
* 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 org.butor.utils;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.jar.Attributes.Name;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ResourceInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VersionInfo {
private static final String BUILD_TIME = "Build-Time";
private static final String IMPLEMENTATION_BUILD = "Implementation-Build";
private static final String DEVELOPMENT_VERSION = "DevelopmentVersion";
private static final String DEVELOPMENT_BUILD = "DevelopmentBuild";
private static final String UNKNOWN_TIMESTAMP = "Unknown";
private static final Logger logger = LoggerFactory.getLogger(VersionInfo.class);
public final String GROUP_ID;
public final String ARTIFACT_ID;
public final String VERSION;
public final String BUILD;
public final String TIMESTAMP;
/**
*
* Example of usage :
*
* VersionInfo vInfo = new VersionInfoBuilder().setGroupId("org.butor").setArtifactId("butor-dao").build();
*
* You will find version information in vInfo of the package org.butor/butor-dao
*
* VERSION : Version as it is defined in the pom file. (i.e butor-dao-0.9.0-SNAPSHOT) BUILD : Hash of the commit
* (i.e 27c26bb05986) TIMESTAMP : Timestamp of the packaging (i.e 20140818-1145)
*
*
*
* @author tbussier
*
*/
public static class VersionInfoBuilder {
String groupId;
String artifactId;
public VersionInfoBuilder setGroupId(String groupId) {
this.groupId = groupId;
return this;
}
public VersionInfoBuilder setArtifactId(String artifactId) {
this.artifactId = artifactId;
return this;
}
public VersionInfo build() {
return new VersionInfo(groupId, artifactId);
}
}
public static class VersionInfoMapBuilder {
private static final Pattern groupIdArtifact = Pattern.compile("META-INF/maven/(.*)/(.*)/pom.properties");
private static final Predicate<String> ALWAYS_TRUE = Predicates.alwaysTrue();
public static final Predicate<String> BUTOR_GROUP_ID_PREDICATE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return (input != null && input.startsWith("com.butor/"));
}
};
/**
* Return a Map<String,VersionInfo> that contain all potential versions found in the classpath.
*
* @return
*/
public Map<String, VersionInfo> build() {
return build(ALWAYS_TRUE);
}
/**
* Return a Map<String,VersionInfo> that contain all potential versions found in the classpath.
*
* The key consist of groupId/artifactId
*
* Allow to provide a key predicate that will be used for filtering,
*
* @param keyFilterPredicate
* @return
*/
public Map<String, VersionInfo> build(Predicate<String> keyFilterPredicate) {
ClassPath cp;
try {
cp = ClassPath.from(VersionInfo.class.getClassLoader());
} catch (IOException e) {
logger.warn("Unable to fetch versioning information from classpath ! {}", e);
return Collections.emptyMap();
}
Iterable<ResourceInfo> pomPropFiles = Iterables.filter(cp.getResources().asList(),
new Predicate<ResourceInfo>() {
@Override
public boolean apply(ResourceInfo input) {
if (input.getResourceName().contains("pom.properties")) {
Matcher m = groupIdArtifact.matcher(input.getResourceName());
return m.matches();
}
return false;
}
});
Iterable<VersionInfo> versionsResources = Iterables.transform(pomPropFiles,
new Function<ResourceInfo, VersionInfo>() {
@Override
public VersionInfo apply(ResourceInfo input) {
Matcher m = groupIdArtifact.matcher(input.getResourceName());
if (m.matches()) {
String groupId = m.group(1);
String artifactId = m.group(2);
return new VersionInfo(groupId, artifactId);
}
return null;
}
});
Map<String, VersionInfo> versionMap = Maps.uniqueIndex(versionsResources,
new Function<VersionInfo, String>() {
@Override
public String apply(VersionInfo input) {
return String.format("%s/%s", input.GROUP_ID, input.ARTIFACT_ID);
}
});
return Maps.filterKeys(versionMap, keyFilterPredicate);
}
}
private VersionInfo(String groupId, String artifactId) {
String version = DEVELOPMENT_VERSION;
String build = DEVELOPMENT_BUILD;
String timestamp = UNKNOWN_TIMESTAMP;
GROUP_ID = groupId;
ARTIFACT_ID = artifactId;
try {
Manifest manifest = getManifest(groupId, artifactId);
version = extractVersionFromManifest(manifest);
build = extractBuildNumberFromManifest(manifest);
timestamp = extractTimestampFromManifest(manifest);
} catch (Throwable e) {
logger.error("Unexpected Error : {}", e);
}
VERSION = version;
BUILD = build;
TIMESTAMP = timestamp;
}
private static Manifest getManifest(String groupId, String artifactId) throws Exception {
if (groupId != null && artifactId != null) {
// look for pom.properties in META-INF/maven/groupid/artifactid/pom.properties, then
final String pomPropPath = String.format("META-INF/maven/%s/%s/pom.properties", groupId, artifactId);
URL pomRessource = ClassLoader.getSystemResource(pomPropPath);
if (pomRessource != null) {
// then look for the manifest for that jar
URL manifestRessource = new URL(pomRessource.toString().replace(pomPropPath, "META-INF/MANIFEST.MF"));
if (manifestRessource != null) {
try {
logger.info("Opening MANIFEST.MF : {}", manifestRessource);
Manifest manifest = new Manifest(manifestRessource.openStream());
return manifest;
} catch (Exception e) {
}
}
}
}
return null;
};
private String extractVersionFromManifest(Manifest manifest) {
String versionNumber = manifest.getMainAttributes().getValue(Name.IMPLEMENTATION_VERSION);
if (versionNumber == null) {
versionNumber = DEVELOPMENT_VERSION;
}
return versionNumber;
}
private String extractBuildNumberFromManifest(Manifest manifest) {
String buildNumber = manifest.getMainAttributes().getValue(IMPLEMENTATION_BUILD);
if (buildNumber == null) {
buildNumber = DEVELOPMENT_BUILD;
}
return buildNumber;
}
private String extractTimestampFromManifest(Manifest manifest) {
String timestamp = manifest.getMainAttributes().getValue(BUILD_TIME);
if (timestamp == null) {
timestamp = UNKNOWN_TIMESTAMP;
}
return timestamp;
}
@Override
public String toString() {
return String.format("VersionInfo [GROUP_ID=%s, ARTIFACT_ID=%s, VERSION=%s, BUILD=%s, TIMESTAMP=%s]", GROUP_ID,
ARTIFACT_ID, VERSION, BUILD, TIMESTAMP);
}
}