[issue 802] Adding support for programmatically accessible version information

This commit is contained in:
Andrew Phillips 2012-01-06 22:56:57 -05:00
parent bbc821a776
commit 62061d8235
5 changed files with 256 additions and 0 deletions

View File

@ -114,6 +114,21 @@
</dependencies>
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<excludes>
<exclude>META-INF/jclouds-version.properties</exclude>
</excludes>
</resource>
<resource>
<filtering>true</filtering>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>META-INF/jclouds-version.properties</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>

View File

@ -0,0 +1,106 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.jclouds;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import java.io.IOException;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
/**
* @author Andrew Phillips
*/
public class JcloudsVersion {
@VisibleForTesting
static final String VERSION_RESOURCE_FILE = "META-INF/jclouds-version.properties";
private static final String VERSION_PROPERTY_NAME = "jclouds.version";
// TODO: stop supporting x.y.z-rc-n after the 1.3.0 release
// x.y.z or x.y.z-rc.n or x.y.z-rc-n, optionally with -SNAPSHOT suffix - see http://semver.org
private static final Pattern SEMANTIC_VERSION_PATTERN =
Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-rc[-\\.](\\d+))?(?:-SNAPSHOT)?");
private static final JcloudsVersion INSTANCE = new JcloudsVersion();
public final int majorVersion;
public final int minorVersion;
public final int patchVersion;
public final boolean releaseCandidate;
private final String version;
/**
* Non-null iff {@link #releaseCandidate} is {@code true}
*/
public final @Nullable Integer releaseCandidateVersion;
public final boolean snapshot;
@VisibleForTesting
JcloudsVersion() {
this(readVersionPropertyFromClasspath());
}
private static String readVersionPropertyFromClasspath() {
Properties versionProperties = new Properties();
try {
versionProperties.load(checkNotNull(Thread.currentThread().getContextClassLoader().getResourceAsStream(VERSION_RESOURCE_FILE), VERSION_RESOURCE_FILE));
} catch (IOException exception) {
throw new IllegalStateException(format("Unable to load version resource file '%s'", VERSION_RESOURCE_FILE), exception);
}
return checkNotNull(versionProperties.getProperty(VERSION_PROPERTY_NAME), VERSION_PROPERTY_NAME);
}
@VisibleForTesting
JcloudsVersion(String version) {
Matcher versionMatcher = SEMANTIC_VERSION_PATTERN.matcher(version);
checkArgument(versionMatcher.matches(), "Version '%s' did not match expected pattern '%s'",
version, SEMANTIC_VERSION_PATTERN);
this.version = version;
// a match will produce three or four matching groups (release candidate version optional)
majorVersion = Integer.valueOf(versionMatcher.group(1));
minorVersion = Integer.valueOf(versionMatcher.group(2));
patchVersion = Integer.valueOf(versionMatcher.group(3));
String releaseCandidateVersionIfPresent = versionMatcher.group(4);
if (releaseCandidateVersionIfPresent != null) {
releaseCandidate = true;
releaseCandidateVersion = Integer.valueOf(releaseCandidateVersionIfPresent);
} else {
releaseCandidate = false;
releaseCandidateVersion = null;
}
// endsWith("T") would be cheaper but we only do this once...
snapshot = version.endsWith("-SNAPSHOT");
}
@Override
public String toString() {
return version;
}
public static JcloudsVersion get() {
return INSTANCE;
}
}

View File

@ -0,0 +1 @@
jclouds.version=${project.version}

View File

@ -0,0 +1,133 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds 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.jclouds;
import static org.jclouds.JcloudsVersion.VERSION_RESOURCE_FILE;
import static org.testng.Assert.*;
import java.io.InputStream;
import java.util.List;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
/**
* @author Andrew Phillips
*/
@Test(singleThreaded = true)
public class JcloudsVersionTest {
@Test
public void testFailsIfResourceFileMissing() {
ClassLoader original = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(
new ResourceHidingClassLoader(original, VERSION_RESOURCE_FILE));
try {
new JcloudsVersion();
fail("Expected NullPointerException");
} catch (NullPointerException expected) {
} finally {
Thread.currentThread().setContextClassLoader(original);
}
}
@Test(expectedExceptions = { IllegalArgumentException.class })
public void testFailsIfInvalidVersion() {
new JcloudsVersion("${project.version}");
}
@Test
public void testExtractsMajorMinorPatchVersions() {
JcloudsVersion version = new JcloudsVersion("1.2.3");
assertEquals(1, version.majorVersion);
assertEquals(2, version.minorVersion);
assertEquals(3, version.patchVersion);
}
@Test
public void testSupportsNonSnapshot() {
JcloudsVersion version = new JcloudsVersion("1.2.3");
assertFalse(version.snapshot, "Expected non-snapshot");
}
@Test
public void testRecognisesSnapshot() {
JcloudsVersion version = new JcloudsVersion("1.2.3-SNAPSHOT");
assertTrue(version.snapshot, "Expected snapshot");
}
@Test
public void testSupportsNonReleaseCandidate() {
JcloudsVersion version = new JcloudsVersion("1.2.3");
assertFalse(version.releaseCandidate, "Expected non-release candidate");
assertNull(version.releaseCandidateVersion);
}
@Test
public void testRecognisesReleaseCandidate() {
JcloudsVersion version = new JcloudsVersion("1.2.3-rc.4");
assertTrue(version.releaseCandidate, "Expected release candidate");
}
// TODO: remove once x.y.z-rc-n support is dropped after 1.3.0
@Test
public void testRecognisesNonSemverReleaseCandidate() {
JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4");
assertTrue(version.releaseCandidate, "Expected release candidate");
}
@Test
public void testExtractsReleaseCandidateVersion() {
JcloudsVersion version = new JcloudsVersion("1.2.3-rc.4");
assertEquals(Integer.valueOf(4), version.releaseCandidateVersion);
}
// TODO: remove once x.y.z-rc-n support is dropped after 1.3.0
@Test
public void testExtractsNonSemverReleaseCandidateVersion() {
JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4");
assertEquals(Integer.valueOf(4), version.releaseCandidateVersion);
}
@Test
public void testRecognisesReleaseCandidateSnapshot() {
JcloudsVersion version = new JcloudsVersion("1.2.3-rc-4-SNAPSHOT");
assertTrue(version.releaseCandidate, "Expected release candidate");
assertTrue(version.snapshot, "Expected snapshot");
}
private static class ResourceHidingClassLoader extends ClassLoader {
private final ClassLoader delegate;
private final List<String> resourcesToHide;
private ResourceHidingClassLoader(ClassLoader delegate, String... resourcesToHide) {
this.delegate = delegate;
this.resourcesToHide = ImmutableList.copyOf(resourcesToHide);
}
@Override
public InputStream getResourceAsStream(String name) {
return (Iterables.contains(resourcesToHide, name)
? null
: delegate.getResourceAsStream(name));
}
}
}

View File

@ -0,0 +1 @@
jclouds.version=0.0.0-SNAPSHOT