Skip to content

Commit b128c65

Browse files
committed
Validation using user-provided custom converters doesn't appear to work
Fixes #532 Signed-off-by: azerr <[email protected]>
1 parent 3413c27 commit b128c65

File tree

2 files changed

+354
-230
lines changed

2 files changed

+354
-230
lines changed

microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/commons/runtime/ProjectClassLoader.java

Lines changed: 177 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -20,125 +20,187 @@
2020
import java.nio.file.Files;
2121
import java.nio.file.Path;
2222
import java.nio.file.Paths;
23-
import java.util.*;
23+
import java.util.ArrayList;
24+
import java.util.Collections;
25+
import java.util.Enumeration;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Set;
2429
import java.util.concurrent.ConcurrentHashMap;
2530

2631
/**
27-
* ProjectClassLoader loads classes from the project output directories and project jars
28-
* using a parent-last strategy. Classes from the parent ClassLoader are used only if
29-
* the class cannot be found in the project directories or jars.
32+
* ProjectClassLoader loads classes from the project output directories and
33+
* project jars using a parent-last strategy. Classes from the parent
34+
* ClassLoader are used only if the class cannot be found in the project
35+
* directories or jars.
3036
*/
3137
class ProjectClassLoader extends ClassLoader {
3238

33-
private final ClassLoader jarClassLoader; // classloader for project jars
34-
private final List<Path> classesDirectories; // output folders (target/classes, build/classes)
35-
private final Map<String, Class<?>> projectClassCache = new ConcurrentHashMap<>();
36-
37-
/**
38-
* Create a ProjectClassLoader for a set of paths (jars + dirs).
39-
*
40-
* @param classpath Paths to jars and/or directories
41-
* @param parent Parent ClassLoader
42-
*/
43-
public ProjectClassLoader(Set<String> classpath, ClassLoader parent) {
44-
super(parent);
45-
46-
List<URL> jarUrls = new ArrayList<>();
47-
List<Path> dirPaths = new ArrayList<>();
48-
49-
for (String entry : classpath) {
50-
try {
51-
Path path = Paths.get(entry);
52-
if (Files.isDirectory(path)) {
53-
dirPaths.add(path);
54-
} else {
55-
jarUrls.add(path.toUri().toURL());
56-
}
57-
} catch (MalformedURLException e) {
58-
e.printStackTrace();
59-
}
60-
}
61-
62-
// jarClassLoader has null parent: we handle delegation ourselves
63-
this.jarClassLoader = new URLClassLoader(jarUrls.toArray(new URL[0]), null);
64-
this.classesDirectories = dirPaths;
65-
}
66-
67-
/**
68-
* Parent-last loadClass: tries project dirs -> project jars -> parent
69-
*/
70-
@Override
71-
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
72-
// 1. Check project cache
73-
Class<?> cached = projectClassCache.get(name);
74-
if (cached != null) {
75-
if (resolve) resolveClass(cached);
76-
return cached;
77-
}
78-
79-
// 2. Try project output directories
80-
try {
81-
Class<?> cls = findClass(name);
82-
if (resolve) resolveClass(cls);
83-
return cls;
84-
} catch (ClassNotFoundException ignored) {
85-
}
86-
87-
// 3. Try project jars
88-
try {
89-
Class<?> cls = jarClassLoader.loadClass(name);
90-
if (resolve) resolveClass(cls);
91-
projectClassCache.put(name, cls); // cache for future
92-
return cls;
93-
} catch (ClassNotFoundException ignored) {
94-
}
95-
96-
// 4. Fallback to parent classloader
97-
return super.loadClass(name, resolve);
98-
}
99-
100-
/**
101-
* Finds a class in the project output directories.
102-
*/
103-
@Override
104-
protected Class<?> findClass(String name) throws ClassNotFoundException {
105-
// already cached?
106-
Class<?> cached = projectClassCache.get(name);
107-
if (cached != null) return cached;
108-
109-
String relPath = name.replace('.', '/') + ".class";
110-
111-
for (Path dir : classesDirectories) {
112-
Path classFile = dir.resolve(relPath);
113-
if (Files.exists(classFile)) {
114-
try {
115-
byte[] bytes = Files.readAllBytes(classFile);
116-
Class<?> cls = defineClass(name, bytes, 0, bytes.length);
117-
projectClassCache.put(name, cls);
118-
return cls;
119-
} catch (IOException e) {
120-
throw new RuntimeException("Failed to read class file: " + classFile, e);
121-
}
122-
}
123-
}
124-
125-
throw new ClassNotFoundException(name);
126-
}
127-
128-
/**
129-
* Clears cached classes (to call after project rebuild)
130-
*/
131-
public void clearProjectClassCache() {
132-
projectClassCache.clear();
133-
}
134-
135-
/**
136-
* Returns the URLs of jars used by this loader (useful for reflection or converters)
137-
*/
138-
public URL[] getJarURLs() {
139-
if (jarClassLoader instanceof URLClassLoader) {
140-
return ((URLClassLoader) jarClassLoader).getURLs();
141-
}
142-
return new URL[0];
143-
}
39+
private final URLClassLoader jarClassLoader; // classloader for project jars
40+
private final List<Path> classesDirectories; // output folders (target/classes, build/classes)
41+
private final Map<String, Class<?>> projectClassCache = new ConcurrentHashMap<>();
42+
43+
/**
44+
* Create a ProjectClassLoader for a set of paths (jars + dirs).
45+
*
46+
* @param classpath Paths to jars and/or directories
47+
* @param parent Parent ClassLoader
48+
*/
49+
public ProjectClassLoader(Set<String> classpath, ClassLoader parent) {
50+
super(parent);
51+
52+
List<URL> jarUrls = new ArrayList<>();
53+
List<Path> dirPaths = new ArrayList<>();
54+
55+
for (String entry : classpath) {
56+
try {
57+
Path path = Paths.get(entry);
58+
if (Files.isDirectory(path)) {
59+
dirPaths.add(path);
60+
} else {
61+
jarUrls.add(path.toUri().toURL());
62+
}
63+
} catch (MalformedURLException e) {
64+
e.printStackTrace();
65+
}
66+
}
67+
68+
// jarClassLoader has null parent: we handle delegation ourselves
69+
this.jarClassLoader = new URLClassLoader(jarUrls.toArray(new URL[0]), null);
70+
this.classesDirectories = dirPaths;
71+
}
72+
73+
/**
74+
* Parent-last loadClass: tries project dirs -> project jars -> parent
75+
*/
76+
@Override
77+
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
78+
// 1. Check project cache
79+
Class<?> cached = projectClassCache.get(name);
80+
if (cached != null) {
81+
if (resolve)
82+
resolveClass(cached);
83+
return cached;
84+
}
85+
86+
// 2. Try project output directories
87+
try {
88+
Class<?> cls = findClass(name);
89+
if (resolve)
90+
resolveClass(cls);
91+
return cls;
92+
} catch (ClassNotFoundException ignored) {
93+
}
94+
95+
// 3. Try project jars
96+
try {
97+
Class<?> cls = jarClassLoader.loadClass(name);
98+
if (resolve)
99+
resolveClass(cls);
100+
projectClassCache.put(name, cls); // cache for future
101+
return cls;
102+
} catch (ClassNotFoundException ignored) {
103+
}
104+
105+
// 4. Fallback to parent classloader
106+
return super.loadClass(name, resolve);
107+
}
108+
109+
/**
110+
* Finds a class in the project output directories.
111+
*/
112+
@Override
113+
protected Class<?> findClass(String name) throws ClassNotFoundException {
114+
// already cached?
115+
Class<?> cached = projectClassCache.get(name);
116+
if (cached != null)
117+
return cached;
118+
119+
String relPath = name.replace('.', '/') + ".class";
120+
121+
for (Path dir : classesDirectories) {
122+
Path classFile = dir.resolve(relPath);
123+
if (Files.exists(classFile)) {
124+
try {
125+
byte[] bytes = Files.readAllBytes(classFile);
126+
Class<?> cls = defineClass(name, bytes, 0, bytes.length);
127+
projectClassCache.put(name, cls);
128+
return cls;
129+
} catch (IOException e) {
130+
throw new RuntimeException("Failed to read class file: " + classFile, e);
131+
}
132+
}
133+
}
134+
135+
throw new ClassNotFoundException(name);
136+
}
137+
138+
@Override
139+
protected URL findResource(String name) {
140+
// 1. Project output directories
141+
for (Path dir : classesDirectories) {
142+
Path resourceFile = dir.resolve(name);
143+
if (Files.exists(resourceFile)) {
144+
try {
145+
return resourceFile.toUri().toURL();
146+
} catch (MalformedURLException e) {
147+
// should not happen
148+
}
149+
}
150+
}
151+
152+
// 2. Project jars
153+
URL resource = jarClassLoader.findResource(name);
154+
if (resource != null) {
155+
return resource;
156+
}
157+
158+
// 3. Not found in this classloader
159+
return null;
160+
}
161+
162+
@Override
163+
protected Enumeration<URL> findResources(String name) throws IOException {
164+
List<URL> result = new ArrayList<>();
165+
166+
// 1. Project output directories
167+
for (Path dir : classesDirectories) {
168+
Path resourceFile = dir.resolve(name);
169+
if (Files.exists(resourceFile)) {
170+
result.add(resourceFile.toUri().toURL());
171+
}
172+
}
173+
174+
// 2. Project jars
175+
Enumeration<URL> jarResources = jarClassLoader.findResources(name);
176+
while (jarResources.hasMoreElements()) {
177+
result.add(jarResources.nextElement());
178+
}
179+
180+
// 3. Parent
181+
Enumeration<URL> parentResources = getParent().getResources(name);
182+
while (parentResources.hasMoreElements()) {
183+
result.add(parentResources.nextElement());
184+
}
185+
186+
return Collections.enumeration(result);
187+
}
188+
189+
/**
190+
* Clears cached classes (to call after project rebuild)
191+
*/
192+
public void clearProjectClassCache() {
193+
projectClassCache.clear();
194+
}
195+
196+
/**
197+
* Returns the URLs of jars used by this loader (useful for reflection or
198+
* converters)
199+
*/
200+
public URL[] getJarURLs() {
201+
if (jarClassLoader instanceof URLClassLoader) {
202+
return ((URLClassLoader) jarClassLoader).getURLs();
203+
}
204+
return new URL[0];
205+
}
144206
}

0 commit comments

Comments
 (0)