1 /*
2 * This file is part of ClassGraph.
3 *
4 * Author: Luke Hutchison
5 *
6 * Hosted at: https://github.com/classgraph/classgraph
7 *
8 * --
9 *
10 * The MIT License (MIT)
11 *
12 * Copyright (c) 2019 Luke Hutchison
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15 * documentation files (the "Software"), to deal in the Software without restriction, including without
16 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18 * conditions:
19 *
20 * The above copyright notice and this permission notice shall be included in all copies or substantial
21 * portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27 * OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29 package nonapi.io.github.classgraph.utils;
30
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.net.URL;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Locale;
40 import java.util.Properties;
41
42 import javax.xml.parsers.DocumentBuilderFactory;
43 import javax.xml.xpath.XPathConstants;
44 import javax.xml.xpath.XPathFactory;
45
46 import org.w3c.dom.Document;
47
48 import io.github.classgraph.ClassGraph;
49
50 /** Finds the version number of ClassGraph, and the version of the JDK. */
51 public final class VersionFinder {
52
53 /** The Maven package for ClassGraph. */
54 private static final String MAVEN_PACKAGE = "io.github.classgraph";
55
56 /** The Maven artifact for ClassGraph. */
57 private static final String MAVEN_ARTIFACT = "classgraph";
58
59 /** The operating system type. */
60 public static final OperatingSystem OS;
61
62 /** Java version string. */
63 public static final String JAVA_VERSION = getProperty("java.version");
64
65 /** Java major version -- 7 for "1.7", 8 for "1.8.0_244", 9 for "9", 11 for "11-ea", etc. */
66 public static final int JAVA_MAJOR_VERSION;
67
68 /** Java minor version -- 0 for "11.0.4" */
69 public static final int JAVA_MINOR_VERSION;
70
71 /** Java minor version -- 4 for "11.0.4" */
72 public static final int JAVA_SUB_VERSION;
73
74 /** Java is EA release -- true for "11-ea", etc. */
75 public static final boolean JAVA_IS_EA_VERSION;
76
77 static {
78 int javaMajorVersion = 0;
79 int javaMinorVersion = 0;
80 int javaSubVersion = 0;
81 final List<Integer> versionParts = new ArrayList<>();
82 if (JAVA_VERSION != null) {
83 for (final String versionPart : JAVA_VERSION.split("[^0-9]+")) {
84 try {
85 versionParts.add(Integer.parseInt(versionPart));
86 } catch (final NumberFormatException e) {
87 // Skip
88 }
89 }
90 if (!versionParts.isEmpty() && versionParts.get(0) == 1) {
91 // 1.7 or 1.8 -> 7 or 8
92 versionParts.remove(0);
93 }
94 if (versionParts.isEmpty()) {
95 throw new RuntimeException("Could not determine Java version: " + JAVA_VERSION);
96 }
97 javaMajorVersion = versionParts.get(0);
98 if (versionParts.size() > 1) {
99 javaMinorVersion = versionParts.get(1);
100 }
101 if (versionParts.size() > 2) {
102 javaSubVersion = versionParts.get(2);
103 }
104 }
105 JAVA_MAJOR_VERSION = javaMajorVersion;
106 JAVA_MINOR_VERSION = javaMinorVersion;
107 JAVA_SUB_VERSION = javaSubVersion;
108 JAVA_IS_EA_VERSION = JAVA_VERSION != null && JAVA_VERSION.endsWith("-ea");
109 }
110
111 /** The operating system type. */
112 public enum OperatingSystem {
113 /** Windows. */
114 Windows,
115
116 /** Mac OS X. */
117 MacOSX,
118
119 /** Linux. */
120 Linux,
121
122 /** Solaris. */
123 Solaris,
124
125 /** BSD. */
126 BSD,
127
128 /** Unix or AIX. */
129 Unix,
130
131 /** Unknown. */
132 Unknown
133 }
134
135 static {
136 final String osName = getProperty("os.name", "unknown").toLowerCase(Locale.ENGLISH);
137 if (osName == null) {
138 OS = OperatingSystem.Unknown;
139 } else if (osName.contains("mac") || osName.contains("darwin")) {
140 OS = OperatingSystem.MacOSX;
141 } else if (osName.contains("win")) {
142 OS = OperatingSystem.Windows;
143 } else if (osName.contains("nux")) {
144 OS = OperatingSystem.Linux;
145 } else if (osName.contains("sunos") || osName.contains("solaris")) {
146 OS = OperatingSystem.Solaris;
147 } else if (osName.contains("bsd")) {
148 OS = OperatingSystem.Unix;
149 } else if (osName.contains("nix") || osName.contains("aix")) {
150 OS = OperatingSystem.Unix;
151 } else {
152 OS = OperatingSystem.Unknown;
153 }
154 }
155
156 // -------------------------------------------------------------------------------------------------------------
157
158 /**
159 * Constructor.
160 */
161 private VersionFinder() {
162 // Cannot be constructed
163 }
164
165 // -------------------------------------------------------------------------------------------------------------
166
167 /**
168 * Get a system property (returning null if a SecurityException was thrown).
169 *
170 * @param propName
171 * the property name
172 * @return the property value
173 */
174 public static String getProperty(final String propName) {
175 try {
176 return System.getProperty(propName);
177 } catch (final SecurityException e) {
178 return null;
179 }
180 }
181
182 /**
183 * Get a system property (returning null if a SecurityException was thrown).
184 *
185 * @param propName
186 * the property name
187 * @param defaultVal
188 * the default value for the property
189 * @return the property value, or the default if the property is not defined.
190 */
191 public static String getProperty(final String propName, final String defaultVal) {
192 try {
193 return System.getProperty(propName, defaultVal);
194 } catch (final SecurityException e) {
195 return null;
196 }
197 }
198
199 // -------------------------------------------------------------------------------------------------------------
200
201 /**
202 * Get the version number of ClassGraph.
203 *
204 * @return the version number of ClassGraph.
205 */
206 public static synchronized String getVersion() {
207 // Try to get version number from pom.xml (available when running in Eclipse)
208 final Class<?> cls = ClassGraph.class;
209 try {
210 final String className = cls.getName();
211 final URL classpathResource = cls.getResource("/" + JarUtils.classNameToClassfilePath(className));
212 if (classpathResource != null) {
213 final Path absolutePackagePath = Paths.get(classpathResource.toURI()).getParent();
214 final int packagePathSegments = className.length() - className.replace(".", "").length();
215 // Remove package segments from path
216 Path path = absolutePackagePath;
217 for (int i = 0; i < packagePathSegments && path != null; i++) {
218 path = path.getParent();
219 }
220 // Remove up to two more levels for "bin" or "target/classes"
221 for (int i = 0; i < 3 && path != null; i++, path = path.getParent()) {
222 final Path pom = path.resolve("pom.xml");
223 try (InputStream is = Files.newInputStream(pom)) {
224 final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
225 doc.getDocumentElement().normalize();
226 String version = (String) XPathFactory.newInstance().newXPath().compile("/project/version")
227 .evaluate(doc, XPathConstants.STRING);
228 if (version != null) {
229 version = version.trim();
230 if (!version.isEmpty()) {
231 return version;
232 }
233 }
234 } catch (final IOException e) {
235 // Not found
236 }
237 }
238 }
239 } catch (final Exception e) {
240 // Ignore
241 }
242
243 // Try to get version number from maven properties in jar's META-INF directory
244 try (InputStream is = cls.getResourceAsStream(
245 "/META-INF/maven/" + MAVEN_PACKAGE + "/" + MAVEN_ARTIFACT + "/pom.properties")) {
246 if (is != null) {
247 final Properties p = new Properties();
248 p.load(is);
249 final String version = p.getProperty("version", "").trim();
250 if (!version.isEmpty()) {
251 return version;
252 }
253 }
254 } catch (final IOException e) {
255 // Ignore
256 }
257
258 // Fallback to using Java API (version number is obtained from MANIFEST.MF)
259 final Package pkg = cls.getPackage();
260 if (pkg != null) {
261 String version = pkg.getImplementationVersion();
262 if (version == null) {
263 version = "";
264 }
265 version = version.trim();
266 if (version.isEmpty()) {
267 version = pkg.getSpecificationVersion();
268 if (version == null) {
269 version = "";
270 }
271 version = version.trim();
272 }
273 if (!version.isEmpty()) {
274 return version;
275 }
276 }
277 return "unknown";
278 }
279 }
280