1 package com.fasterxml.jackson.core.util; 2 3 import java.io.*; 4 import java.util.Properties; 5 import java.util.regex.Pattern; 6 7 import com.fasterxml.jackson.core.Version; 8 import com.fasterxml.jackson.core.Versioned; 9 10 /** 11 * Functionality for supporting exposing of component {@link Version}s. 12 * Also contains other misc methods that have no other place to live in. 13 *<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. 17 *<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. 22 *<p> 23 * Note that functionality for reading "VERSION.txt" was removed completely 24 * from Jackson 2.6. 25 */ 26 public class VersionUtil 27 { 28 private final static Pattern V_SEP = Pattern.compile("[-_./;:]"); 29 30 /* 31 /********************************************************** 32 /* Instance life-cycle 33 /********************************************************** 34 */ 35 VersionUtil()36 protected VersionUtil() { } 37 38 @Deprecated // since 2.9 version()39 public Version version() { return Version.unknownVersion(); } 40 41 /* 42 /********************************************************** 43 /* Static load methods 44 /********************************************************** 45 */ 46 47 /** 48 * Helper method that will try to load version information for specified 49 * class. Implementation is as follows: 50 * 51 * First, tries to load version info from a class named 52 * "PackageVersion" in the same package as the class. 53 * 54 * If no version information is found, {@link Version#unknownVersion()} is returned. 55 */ versionFor(Class<?> cls)56 public static Version versionFor(Class<?> cls) 57 { 58 Version version = packageVersionFor(cls); 59 return version == null ? Version.unknownVersion() : version; 60 } 61 62 /** 63 * Loads version information by introspecting a class named 64 * "PackageVersion" in the same package as the given class. 65 *<p> 66 * If the class could not be found or does not have a public 67 * static Version field named "VERSION", returns null. 68 */ packageVersionFor(Class<?> cls)69 public static Version packageVersionFor(Class<?> cls) 70 { 71 Version v = null; 72 try { 73 String versionInfoClassName = cls.getPackage().getName() + ".PackageVersion"; 74 Class<?> vClass = Class.forName(versionInfoClassName, true, cls.getClassLoader()); 75 // However, if class exists, it better work correctly, no swallowing exceptions 76 try { 77 v = ((Versioned) vClass.getDeclaredConstructor().newInstance()).version(); 78 } catch (Exception e) { 79 throw new IllegalArgumentException("Failed to get Versioned out of "+vClass); 80 } 81 } catch (Exception e) { // ok to be missing (not good but acceptable) 82 ; 83 } 84 return (v == null) ? Version.unknownVersion() : v; 85 } 86 87 /** 88 * Will attempt to load the maven version for the given groupId and 89 * artifactId. Maven puts a pom.properties file in 90 * META-INF/maven/groupId/artifactId, containing the groupId, 91 * artifactId and version of the library. 92 * 93 * @param cl the ClassLoader to load the pom.properties file from 94 * @param groupId the groupId of the library 95 * @param artifactId the artifactId of the library 96 * @return The version 97 * 98 * @deprecated Since 2.6: functionality not used by any official Jackson component, should be 99 * moved out if anyone needs it 100 */ 101 @SuppressWarnings("resource") 102 @Deprecated // since 2.6 mavenVersionFor(ClassLoader cl, String groupId, String artifactId)103 public static Version mavenVersionFor(ClassLoader cl, String groupId, String artifactId) 104 { 105 InputStream pomProperties = cl.getResourceAsStream("META-INF/maven/" 106 + groupId.replaceAll("\\.", "/")+ "/" + artifactId + "/pom.properties"); 107 if (pomProperties != null) { 108 try { 109 Properties props = new Properties(); 110 props.load(pomProperties); 111 String versionStr = props.getProperty("version"); 112 String pomPropertiesArtifactId = props.getProperty("artifactId"); 113 String pomPropertiesGroupId = props.getProperty("groupId"); 114 return parseVersion(versionStr, pomPropertiesGroupId, pomPropertiesArtifactId); 115 } catch (IOException e) { 116 // Ignore 117 } finally { 118 _close(pomProperties); 119 } 120 } 121 return Version.unknownVersion(); 122 } 123 124 /** 125 * Method used by <code>PackageVersion</code> classes to decode version injected by Maven build. 126 */ parseVersion(String s, String groupId, String artifactId)127 public static Version parseVersion(String s, String groupId, String artifactId) 128 { 129 if (s != null && (s = s.trim()).length() > 0) { 130 String[] parts = V_SEP.split(s); 131 return new Version(parseVersionPart(parts[0]), 132 (parts.length > 1) ? parseVersionPart(parts[1]) : 0, 133 (parts.length > 2) ? parseVersionPart(parts[2]) : 0, 134 (parts.length > 3) ? parts[3] : null, 135 groupId, artifactId); 136 } 137 return Version.unknownVersion(); 138 } 139 parseVersionPart(String s)140 protected static int parseVersionPart(String s) { 141 int number = 0; 142 for (int i = 0, len = s.length(); i < len; ++i) { 143 char c = s.charAt(i); 144 if (c > '9' || c < '0') break; 145 number = (number * 10) + (c - '0'); 146 } 147 return number; 148 } 149 _close(Closeable c)150 private final static void _close(Closeable c) { 151 try { 152 c.close(); 153 } catch (IOException e) { } 154 } 155 156 /* 157 /********************************************************** 158 /* Orphan utility methods 159 /********************************************************** 160 */ 161 throwInternal()162 public final static void throwInternal() { 163 throw new RuntimeException("Internal error: this code path should never get executed"); 164 } 165 } 166