Skip to content

Commit e8bfdaa

Browse files
author
Patrick Twohig
committed
Permit java.sql for Elements and clarify classloader denial errors
Adds JavaSqlPermittedPackages so java.sql types (e.g. Timestamp) are visible to Elements the same way java.base types already are, registered via the existing PermittedPackages SPI convention. Also gives PermittedTypesClassLoader and ElementImplementationClassLoader denial paths descriptive exception messages naming which mechanism denied the class and how to permit it, instead of a bare class name, so a NoClassDefFoundError several frames removed from these loaders is diagnosable without already knowing about this visibility gate. Fixes #75
1 parent a102043 commit e8bfdaa

4 files changed

Lines changed: 58 additions & 9 deletions

File tree

sdk-spi/src/main/java/dev/getelements/elements/sdk/spi/ElementImplementationClassLoader.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import dev.getelements.elements.sdk.PermittedPackages;
44
import dev.getelements.elements.sdk.PermittedTypes;
55
import dev.getelements.elements.sdk.annotation.ElementLocal;
6+
import dev.getelements.elements.sdk.annotation.ElementPackageRequest;
67
import dev.getelements.elements.sdk.annotation.ElementPrivate;
78
import dev.getelements.elements.sdk.annotation.ElementPublic;
9+
import dev.getelements.elements.sdk.annotation.ElementTypeRequest;
810
import dev.getelements.elements.sdk.exception.SdkException;
911
import dev.getelements.elements.sdk.record.ElementRecord;
1012
import dev.getelements.elements.sdk.record.ElementServiceRecord;
@@ -276,7 +278,24 @@ protected Class<?> loadClass(final String name, final boolean resolve) throws Cl
276278
}
277279

278280
// Step 9: Default Deny
279-
throw new ClassNotFoundException(name);
281+
final var message = (
282+
"%s was denied by %s: it is not a registered service export, and no %s or %s in this " +
283+
"Element's package-info.java permits type or package %s. To permit this type, add " +
284+
"@%s(\"%s\") or @%s to the Element's package-info.java, or export it as a service."
285+
).formatted(
286+
name,
287+
ElementImplementationClassLoader.class.getSimpleName(),
288+
ElementTypeRequest.class.getSimpleName(),
289+
ElementPackageRequest.class.getSimpleName(),
290+
packageName,
291+
ElementPackageRequest.class.getSimpleName(),
292+
packageName,
293+
ElementTypeRequest.class.getSimpleName()
294+
);
295+
296+
logger.trace(message);
297+
298+
throw new ClassNotFoundException(message);
280299

281300
}
282301

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package dev.getelements.elements.sdk;
2+
3+
public class JavaSqlPermittedPackages implements PermittedPackages {
4+
5+
@Override
6+
public boolean test(final Package aPackage) {
7+
final var name = aPackage.getName();
8+
return name.equals("java.sql") || name.startsWith("java.sql.");
9+
}
10+
11+
@Override
12+
public String getDescription() {
13+
return "Permits the usage of java.sql types (e.g. java.sql.Timestamp, java.sql.Date). Unlike java.base, " +
14+
"the java.sql platform module is not loaded by the bootstrap class loader, so its types are not " +
15+
"otherwise visible to Elements without this rule.";
16+
}
17+
18+
}

sdk/src/main/java/dev/getelements/elements/sdk/PermittedTypesClassLoader.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.getelements.elements.sdk;
22

3-
import dev.getelements.elements.sdk.annotation.ElementDefinition;
43
import dev.getelements.elements.sdk.annotation.ElementPrivate;
54
import dev.getelements.elements.sdk.annotation.ElementPublic;
65
import org.slf4j.Logger;
@@ -203,26 +202,38 @@ private Class<?> processVisibilityAnnotations(final Class<?> aClass) throws Clas
203202
return aClass;
204203
}
205204

206-
logger.trace(
207-
"{} or {}'s package ({}) must have @{} annotation, be exposed via @{}, or one of [{}] or [{}]",
208-
aClass.getSimpleName(),
209-
aClass.getSimpleName(),
205+
final var message = (
206+
"%s was denied by %s: its package (%s) is not permitted by any registered %s or %s, and " +
207+
"neither the type nor its package is annotated with @%s. Registered %s: [%s]. Registered %s: [%s]. " +
208+
"To permit this type, register a %s/%s service (see META-INF/services), or annotate the type or " +
209+
"its package with @%s."
210+
).formatted(
211+
aClass.getName(),
212+
PermittedTypesClassLoader.class.getSimpleName(),
210213
aClass.getPackage(),
214+
PermittedTypes.class.getSimpleName(),
215+
PermittedPackages.class.getSimpleName(),
211216
ElementPublic.class.getSimpleName(),
212-
ElementDefinition.class.getSimpleName(),
217+
PermittedTypes.class.getSimpleName(),
213218
permittedTypes
214219
.stream()
215220
.map(Object::getClass)
216221
.map(Objects::toString)
217222
.collect(Collectors.joining(",")),
223+
PermittedPackages.class.getSimpleName(),
218224
permittedPackages
219225
.stream()
220226
.map(Object::getClass)
221227
.map(Objects::toString)
222-
.collect(Collectors.joining(","))
228+
.collect(Collectors.joining(",")),
229+
PermittedTypes.class.getSimpleName(),
230+
PermittedPackages.class.getSimpleName(),
231+
ElementPublic.class.getSimpleName()
223232
);
224233

225-
throw new ClassNotFoundException(aClass.getName());
234+
logger.trace(message);
235+
236+
throw new ClassNotFoundException(message);
226237

227238
}
228239

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
dev.getelements.elements.sdk.LoggingPermittedPackages
22
dev.getelements.elements.sdk.JakartaInjectPermittedPackages
3+
dev.getelements.elements.sdk.JavaSqlPermittedPackages

0 commit comments

Comments
 (0)