blob: 8b01016b85cc0850468c459899c538331be1248f [file] [log] [blame]
Tatu Salorantaf15531c2011-12-22 23:00:40 -08001package com.fasterxml.jackson.core.util;
2
3import java.io.*;
James Roper78774992012-01-03 20:43:57 +01004import java.util.Properties;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08005import java.util.regex.Pattern;
6
7import com.fasterxml.jackson.core.Version;
Tatu Saloranta45466132013-01-11 10:20:48 -08008import com.fasterxml.jackson.core.Versioned;
Tatu Salorantaf15531c2011-12-22 23:00:40 -08009
10/**
11 * Functionality for supporting exposing of component {@link Version}s.
Tatu Salorantaa629bb92013-04-07 10:33:04 -070012 * Also contains other misc methods that have no other place to live in.
Tatu Saloranta48d26092011-12-28 23:11:49 -080013 *<p>
14 * Note that this class can be used in two roles: first, as a static
15 * utility class for loading purposes, and second, as a singleton
16 * loader of per-module version information.
Tatu Salorantac3ef9f32013-02-04 20:35:22 -080017 *<p>
18 * Note that method for accessing version information changed between versions
19 * 2.1 and 2.2; earlier code used file named "VERSION.txt"; but this has serious
20 * performance issues on some platforms (Android), so a replacement system
21 * was implemented to use class generation and dynamic class loading.
Tatu Salorantaf15531c2011-12-22 23:00:40 -080022 */
23public class VersionUtil
24{
Tatu Salorantafedd7952014-01-14 20:41:04 -080025 private final static Pattern V_SEP = Pattern.compile("[-_./;:]");
Tatu Saloranta48d26092011-12-28 23:11:49 -080026
Tatu Salorantafedd7952014-01-14 20:41:04 -080027 private final Version _v;
Tatu Saloranta48d26092011-12-28 23:11:49 -080028
29 /*
30 /**********************************************************
Tatu Saloranta1313ea32013-09-06 21:36:34 -070031 /* Instance life-cycle
Tatu Saloranta48d26092011-12-28 23:11:49 -080032 /**********************************************************
33 */
34
35 protected VersionUtil()
36 {
37 Version v = null;
38 try {
39 /* Class we pass only matters for resource-loading: can't use this Class
40 * (as it's just being loaded at this point), nor anything that depends on it.
41 */
42 v = VersionUtil.versionFor(getClass());
43 } catch (Exception e) { // not good to dump to stderr; but that's all we have at this low level
Tatu Saloranta1313ea32013-09-06 21:36:34 -070044 System.err.println("ERROR: Failed to load Version information from "+getClass());
Tatu Saloranta48d26092011-12-28 23:11:49 -080045 }
46 if (v == null) {
47 v = Version.unknownVersion();
48 }
Tatu Salorantafedd7952014-01-14 20:41:04 -080049 _v = v;
Tatu Saloranta48d26092011-12-28 23:11:49 -080050 }
51
Tatu Salorantafedd7952014-01-14 20:41:04 -080052 public Version version() { return _v; }
Tatu Saloranta48d26092011-12-28 23:11:49 -080053
54 /*
55 /**********************************************************
56 /* Static load methods
57 /**********************************************************
58 */
Tatu Salorantaf15531c2011-12-22 23:00:40 -080059
60 /**
61 * Helper method that will try to load version information for specified
Ben Gertzfieldf440fa02013-01-03 16:38:28 -080062 * class. Implementation is as follows:
63 *
64 * First, tries to load version info from a class named
65 * "PackageVersion" in the same package as the class.
66 *
67 * Next, if that fails, class loader that loaded specified class is
68 * asked to load resource with name "VERSION" from same location
69 * (package) as class itself had.
70 *
71 * If no version information is found, {@link Version#unknownVersion()} is returned.
Tatu Salorantaf15531c2011-12-22 23:00:40 -080072 */
Tatu Saloranta1313ea32013-09-06 21:36:34 -070073 @SuppressWarnings("resource")
Tatu Salorantaf15531c2011-12-22 23:00:40 -080074 public static Version versionFor(Class<?> cls)
75 {
Ben Gertzfieldf440fa02013-01-03 16:38:28 -080076 Version packageVersion = packageVersionFor(cls);
77 if (packageVersion != null) {
78 return packageVersion;
79 }
Tatu Saloranta1313ea32013-09-06 21:36:34 -070080 final InputStream in = cls.getResourceAsStream("VERSION.txt");
81 if (in == null) {
Francis Galiegue6ebc7592012-11-13 17:40:58 +010082 return Version.unknownVersion();
Tatu Saloranta1313ea32013-09-06 21:36:34 -070083 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -080084 try {
Francis Galiegue6ebc7592012-11-13 17:40:58 +010085 InputStreamReader reader = new InputStreamReader(in, "UTF-8");
Tatu Saloranta1313ea32013-09-06 21:36:34 -070086 return doReadVersion(reader);
Francis Galiegue6ebc7592012-11-13 17:40:58 +010087 } catch (UnsupportedEncodingException e) {
88 return Version.unknownVersion();
89 } finally {
Tatu Saloranta1313ea32013-09-06 21:36:34 -070090 _close(in);
Francis Galiegue6ebc7592012-11-13 17:40:58 +010091 }
92 }
93
Ben Gertzfieldf440fa02013-01-03 16:38:28 -080094 /**
95 * Loads version information by introspecting a class named
96 * "PackageVersion" in the same package as the given class.
97 *
98 * If the class could not be found or does not have a public
99 * static Version field named "VERSION", returns null.
100 */
101 public static Version packageVersionFor(Class<?> cls)
102 {
103 try {
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700104 String versionInfoClassName = cls.getPackage().getName() + ".PackageVersion";
Tatu Saloranta838dc4c2013-09-06 21:41:44 -0700105 Class<?> vClass = Class.forName(versionInfoClassName, true, cls.getClassLoader());
106 // However, if class exists, it better work correctly, no swallowing exceptions
107 try {
108 return ((Versioned) vClass.newInstance()).version();
109 } catch (Exception e) {
110 throw new IllegalArgumentException("Failed to get Versioned out of "+vClass);
111 }
Tatu Saloranta45466132013-01-11 10:20:48 -0800112 } catch (Exception e) { // ok to be missing (not good, acceptable)
Ben Gertzfieldf440fa02013-01-03 16:38:28 -0800113 return null;
114 }
115 }
116
Tatu Salorantafedd7952014-01-14 20:41:04 -0800117 private static Version doReadVersion(final Reader r)
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100118 {
119 String version = null, group = null, artifact = null;
120
Tatu Salorantafedd7952014-01-14 20:41:04 -0800121 final BufferedReader br = new BufferedReader(r);
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100122 try {
123 version = br.readLine();
Tatu Saloranta838dc4c2013-09-06 21:41:44 -0700124 if (version != null) {
125 group = br.readLine();
126 if (group != null) {
127 artifact = br.readLine();
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700128 }
Tatu Saloranta838dc4c2013-09-06 21:41:44 -0700129 }
130 } catch (IOException ignored) {
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700131 } finally {
132 _close(br);
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100133 }
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100134 // We don't trim() version: parseVersion() takes care ot that
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700135 if (group != null) {
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100136 group = group.trim();
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700137 }
138 if (artifact != null) {
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100139 artifact = artifact.trim();
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700140 }
Francis Galiegue6ebc7592012-11-13 17:40:58 +0100141 return parseVersion(version, group, artifact);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800142 }
143
Tatu Saloranta96e131f2011-12-28 22:50:23 -0800144 /**
James Roper78774992012-01-03 20:43:57 +0100145 * Will attempt to load the maven version for the given groupId and
146 * artifactId. Maven puts a pom.properties file in
147 * META-INF/maven/groupId/artifactId, containing the groupId,
148 * artifactId and version of the library.
149 *
Tatu Salorantafedd7952014-01-14 20:41:04 -0800150 * @param cl the ClassLoader to load the pom.properties file from
James Roper78774992012-01-03 20:43:57 +0100151 * @param groupId the groupId of the library
152 * @param artifactId the artifactId of the library
153 * @return The version
154 */
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700155 @SuppressWarnings("resource")
Tatu Salorantafedd7952014-01-14 20:41:04 -0800156 public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId)
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700157 {
Tatu Salorantafedd7952014-01-14 20:41:04 -0800158 InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/"
Tatu Saloranta838dc4c2013-09-06 21:41:44 -0700159 + groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties");
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700160 if (pomProperties != null) {
James Roper78774992012-01-03 20:43:57 +0100161 try {
162 Properties props = new Properties();
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700163 props.load(pomProperties);
James Roper78774992012-01-03 20:43:57 +0100164 String versionStr = props.getProperty("version");
165 String pomPropertiesArtifactId = props.getProperty("artifactId");
166 String pomPropertiesGroupId = props.getProperty("groupId");
167 return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId);
168 } catch (IOException e) {
169 // Ignore
170 } finally {
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700171 _close(pomProperties);
James Roper78774992012-01-03 20:43:57 +0100172 }
173 }
174 return Version.unknownVersion();
175 }
176
Tatu Salorantafedd7952014-01-14 20:41:04 -0800177 public static Version parseVersion(String s, String groupId, String artifactId)
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800178 {
Tatu Salorantafedd7952014-01-14 20:41:04 -0800179 if (s != null && (s = s.trim()).length() > 0) {
180 String[] parts = V_SEP.split(s);
Tatu Saloranta838dc4c2013-09-06 21:41:44 -0700181 return new Version(parseVersionPart(parts[0]),
182 (parts.length > 1) ? parseVersionPart(parts[1]) : 0,
183 (parts.length > 2) ? parseVersionPart(parts[2]) : 0,
184 (parts.length > 3) ? parts[3] : null,
185 groupId, artifactId);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800186 }
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700187 return null;
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800188 }
189
Tatu Salorantafedd7952014-01-14 20:41:04 -0800190 protected static int parseVersionPart(String s) {
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800191 int number = 0;
Tatu Salorantafedd7952014-01-14 20:41:04 -0800192 for (int i = 0, len = s.length(); i < len; ++i) {
193 char c = s.charAt(i);
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800194 if (c > '9' || c < '0') break;
195 number = (number * 10) + (c - '0');
196 }
197 return number;
198 }
Tatu Salorantaa629bb92013-04-07 10:33:04 -0700199
Tatu Saloranta1313ea32013-09-06 21:36:34 -0700200 private final static void _close(Closeable c) {
201 try {
202 c.close();
203 } catch (IOException e) { }
204 }
205
Tatu Salorantaa629bb92013-04-07 10:33:04 -0700206 /*
207 /**********************************************************
208 /* Orphan utility methods
209 /**********************************************************
210 */
211
212 public final static void throwInternal() {
213 throw new RuntimeException("Internal error: this code path should never get executed");
214 }
Tatu Salorantaf15531c2011-12-22 23:00:40 -0800215}