Skip to content

Commit ef442f7

Browse files
committed
fix: Address PR review comments
- Remove duplicate ApiTrackerMain class (keep only ApiTrackerRunner) - Replace hardcoded class list with dynamic classpath scanning - Change logging from FINE to INFO for key operations - Remove hardcoded version references The API tracker now dynamically discovers classes in the JSON API packages by scanning both directories and JAR files on the classpath. This makes it more robust and eliminates the need to manually update class lists.
1 parent 29eb89f commit ef442f7

File tree

2 files changed

+101
-202
lines changed

2 files changed

+101
-202
lines changed

json-java21-api-tracker/src/main/java/io/github/simbo1905/tracker/ApiTracker.java

Lines changed: 101 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -79,53 +79,119 @@ enum Nothing implements ApiTracker {}
7979
/// Discovers all classes in the local JSON API packages
8080
/// @return sorted set of classes from jdk.sandbox.java.util.json and jdk.sandbox.internal.util.json
8181
static Set<Class<?>> discoverLocalJsonClasses() {
82-
LOGGER.fine("Discovering local JSON classes");
82+
LOGGER.info("Starting class discovery for JSON API packages");
8383
final var classes = new TreeSet<Class<?>>((a, b) -> a.getName().compareTo(b.getName()));
8484

85-
// Known public API classes
86-
final var publicApiClasses = List.of(
87-
"jdk.sandbox.java.util.json.Json",
88-
"jdk.sandbox.java.util.json.JsonValue",
89-
"jdk.sandbox.java.util.json.JsonObject",
90-
"jdk.sandbox.java.util.json.JsonArray",
91-
"jdk.sandbox.java.util.json.JsonString",
92-
"jdk.sandbox.java.util.json.JsonNumber",
93-
"jdk.sandbox.java.util.json.JsonBoolean",
94-
"jdk.sandbox.java.util.json.JsonNull",
95-
"jdk.sandbox.java.util.json.JsonParseException"
85+
// Packages to scan
86+
final var packages = List.of(
87+
"jdk.sandbox.java.util.json",
88+
"jdk.sandbox.internal.util.json"
9689
);
9790

98-
// Known internal implementation classes
99-
final var internalClasses = List.of(
100-
"jdk.sandbox.internal.util.json.JsonParser",
101-
"jdk.sandbox.internal.util.json.JsonObjectImpl",
102-
"jdk.sandbox.internal.util.json.JsonArrayImpl",
103-
"jdk.sandbox.internal.util.json.JsonStringImpl",
104-
"jdk.sandbox.internal.util.json.JsonNumberImpl"
105-
);
91+
final var classLoader = Thread.currentThread().getContextClassLoader();
92+
93+
for (final var packageName : packages) {
94+
try {
95+
final var path = packageName.replace('.', '/');
96+
final var resources = classLoader.getResources(path);
97+
98+
while (resources.hasMoreElements()) {
99+
final var url = resources.nextElement();
100+
LOGGER.fine(() -> "Scanning resource: " + url);
101+
102+
if ("file".equals(url.getProtocol())) {
103+
// Handle directory scanning
104+
scanDirectory(new java.io.File(url.toURI()), packageName, classes);
105+
} else if ("jar".equals(url.getProtocol())) {
106+
// Handle JAR scanning
107+
scanJar(url, packageName, classes);
108+
}
109+
}
110+
} catch (Exception e) {
111+
LOGGER.log(Level.WARNING, "Error scanning package: " + packageName, e);
112+
}
113+
}
106114

107-
// Load all known classes
108-
Stream.concat(publicApiClasses.stream(), internalClasses.stream())
109-
.forEach(className -> {
115+
LOGGER.info("Discovered " + classes.size() + " classes in JSON API packages");
116+
return Collections.unmodifiableSet(classes);
117+
}
118+
119+
/// Scans a directory for class files
120+
static void scanDirectory(java.io.File directory, String packageName, Set<Class<?>> classes) {
121+
if (!directory.exists() || !directory.isDirectory()) {
122+
return;
123+
}
124+
125+
final var files = directory.listFiles();
126+
if (files == null) {
127+
return;
128+
}
129+
130+
for (final var file : files) {
131+
if (file.isDirectory()) {
132+
scanDirectory(file, packageName + "." + file.getName(), classes);
133+
} else if (file.getName().endsWith(".class") && !file.getName().contains("$")) {
134+
final var className = packageName + '.' +
135+
file.getName().substring(0, file.getName().length() - 6);
110136
try {
111137
final var clazz = Class.forName(className);
112138
classes.add(clazz);
113-
LOGGER.finer(() -> "Loaded class: " + className);
114-
} catch (ClassNotFoundException e) {
115-
LOGGER.fine(() -> "Class not found (might not exist yet): " + className);
139+
LOGGER.fine(() -> "Found class: " + className);
140+
} catch (ClassNotFoundException | NoClassDefFoundError e) {
141+
LOGGER.fine(() -> "Could not load class: " + className);
116142
}
117-
});
118-
119-
LOGGER.fine(() -> "Discovered " + classes.size() + " classes");
120-
return Collections.unmodifiableSet(classes);
143+
}
144+
}
145+
}
146+
147+
/// Scans a JAR file for classes in the specified package
148+
static void scanJar(java.net.URL jarUrl, String packageName, Set<Class<?>> classes) {
149+
try {
150+
final var jarPath = jarUrl.getPath();
151+
final var exclamation = jarPath.indexOf('!');
152+
if (exclamation < 0) {
153+
return;
154+
}
155+
156+
final var jarFilePath = jarPath.substring(5, exclamation); // Remove "file:"
157+
final var packagePath = packageName.replace('.', '/');
158+
159+
try (final var jarFile = new java.util.jar.JarFile(jarFilePath)) {
160+
final var entries = jarFile.entries();
161+
162+
while (entries.hasMoreElements()) {
163+
final var entry = entries.nextElement();
164+
final var entryName = entry.getName();
165+
166+
if (entryName.startsWith(packagePath) &&
167+
entryName.endsWith(".class") &&
168+
!entryName.contains("$")) {
169+
170+
final var className = entryName
171+
.substring(0, entryName.length() - 6)
172+
.replace('/', '.');
173+
174+
try {
175+
final var clazz = Class.forName(className);
176+
classes.add(clazz);
177+
LOGGER.fine(() -> "Found class in JAR: " + className);
178+
} catch (ClassNotFoundException | NoClassDefFoundError e) {
179+
LOGGER.fine(() -> "Could not load class from JAR: " + className);
180+
}
181+
}
182+
}
183+
}
184+
} catch (Exception e) {
185+
LOGGER.log(Level.WARNING, "Error scanning JAR: " + jarUrl, e);
186+
}
121187
}
122188

123189
/// Fetches upstream source files from GitHub for the given local classes
124190
/// @param localClasses set of local classes to fetch upstream sources for
125191
/// @return map of className to source code (or error message if fetch failed)
126192
static Map<String, String> fetchUpstreamSources(Set<Class<?>> localClasses) {
127193
Objects.requireNonNull(localClasses, "localClasses must not be null");
128-
LOGGER.fine(() -> "Fetching upstream sources for " + localClasses.size() + " classes");
194+
LOGGER.info("Fetching upstream sources for " + localClasses.size() + " classes");
129195

130196
final var results = new LinkedHashMap<String, String>();
131197
final var httpClient = HttpClient.newBuilder()
@@ -197,7 +263,7 @@ static String mapToUpstreamPath(String className) {
197263
/// @return JSON representation of the class's public API
198264
static JsonObject extractLocalApi(Class<?> clazz) {
199265
Objects.requireNonNull(clazz, "clazz must not be null");
200-
LOGGER.fine(() -> "Extracting local API for: " + clazz.getName());
266+
LOGGER.info("Extracting local API for: " + clazz.getName());
201267

202268
final var apiMap = new LinkedHashMap<String, JsonValue>();
203269

@@ -350,7 +416,7 @@ static JsonObject extractUpstreamApi(String sourceCode, String className) {
350416
return JsonObject.of(errorMap);
351417
}
352418

353-
LOGGER.fine(() -> "Extracting upstream API for: " + className);
419+
LOGGER.info("Extracting upstream API for: " + className);
354420

355421
final var compiler = ToolProvider.getSystemJavaCompiler();
356422
if (compiler == null) {
@@ -927,7 +993,7 @@ static JsonObject runFullComparison() {
927993

928994
// Discover local classes
929995
final var localClasses = discoverLocalJsonClasses();
930-
LOGGER.info(() -> "Found " + localClasses.size() + " local classes");
996+
LOGGER.info("Found " + localClasses.size() + " local classes");
931997

932998
// Fetch upstream sources
933999
final var upstreamSources = fetchUpstreamSources(localClasses);
@@ -969,7 +1035,7 @@ static JsonObject runFullComparison() {
9691035
final var duration = Duration.between(startTime, Instant.now());
9701036
reportMap.put("durationMs", JsonNumber.of(duration.toMillis()));
9711037

972-
LOGGER.info(() -> "Comparison completed in " + duration.toMillis() + "ms");
1038+
LOGGER.info("Comparison completed in " + duration.toMillis() + "ms");
9731039

9741040
return JsonObject.of(reportMap);
9751041
}

json-java21-api-tracker/src/main/java/io/github/simbo1905/tracker/ApiTrackerMain.java

Lines changed: 0 additions & 167 deletions
This file was deleted.

0 commit comments

Comments
 (0)