This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new f3f91065bf Marshall module improvements
f3f91065bf is described below
commit f3f91065bf854aa1b51ca89543166b6ae20bbac8
Author: James Bognar <[email protected]>
AuthorDate: Mon Dec 8 12:26:02 2025 -0500
Marshall module improvements
---
TODO.md | 3 +-
.../src/main/java/org/apache/juneau/ClassMeta.java | 48 ++++++++++++----------
2 files changed, 29 insertions(+), 22 deletions(-)
diff --git a/TODO.md b/TODO.md
index 547b30d4d7..a928b0298f 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,6 +1,6 @@
# TODO List
-**Last generated TODO number: TODO-93**
+**Last generated TODO number: TODO-94**
This file tracks pending tasks for the Apache Juneau project. For completed
items, see [TODO-completed.md](TODO-completed.md).
@@ -28,6 +28,7 @@ This file tracks pending tasks for the Apache Juneau project.
For completed item
- [ ] TODO-21 Thrown NotFound causes - javax.servlet.ServletException: Invalid
method response: 200
- [x] TODO-89 Add ClassInfoTyped
- [ ] TODO-91 Security: LogsResource returns HTTP 500 instead of 404 for
malformed query parameters (CWE-74). When accessing log file URLs with encoded
special characters in query parameters (e.g., `?method=VIEW%5C%5C%5C%22`), the
system returns HTTP 500 "Invalid method response: 200" instead of HTTP 404. The
error suggests it's incorrectly trying to find a Java method matching a
malformed path. Should return 404 for invalid/malformed requests.
+- [ ] TODO-94 Add a "cloaked" mode to IRS to always return 404s in place of
40x/50x responses.
## HTTP Response/Exception Improvements
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
index f8f04078f7..3c3cb9b435 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ClassMeta.java
@@ -146,7 +146,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
private final List<ObjectSwap<?,?>> childSwaps;
// Any ObjectSwaps where the normal type is a subclass of this class.
private final Cache<Class<?>,ObjectSwap<?,?>> childUnswapMap;
// Maps swap subclasses to ObjectSwaps.
private final Supplier<String> dictionaryName;
// The dictionary name of this class if it has one.
- private final ClassMeta<?> elementType;
// If ARRAY or COLLECTION, the element class type.
+ private final Supplier<ClassMeta<?>> elementType;
// If ARRAY or COLLECTION, the element class type.
private final OptionalSupplier<String> example;
// Example JSON.
private final OptionalSupplier<FieldInfo> exampleField;
// The @Example-annotated field (if it has one).
private final OptionalSupplier<MethodInfo> exampleMethod;
// The example() or @Example-annotated method (if it has one).
@@ -258,20 +258,8 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
example = memoize(()->findExample());
implClass = memoize(()->findImplClass());
- var _elementType = (ClassMeta<?>)null;
- if (cat.is(ARRAY)) {
- _elementType =
beanContext.getClassMeta(inner().getComponentType(), false);
- } else if (cat.is(COLLECTION) || is(Optional.class)) {
- // If this is a COLLECTION, see if it's
parameterized (e.g. AddressBook extends LinkedList<Person>)
- var parameters =
beanContext.findParameters(inner(), inner());
- if (nn(parameters) && parameters.length == 1) {
- _elementType = parameters[0];
- } else {
- _elementType =
beanContext.getClassMeta(Object.class);
- }
- }
- this.elementType = _elementType;
this.keyValueTypes = memoize(()->findKeyValueTypes());
+ this.elementType = memoize(()->findElementType());
this.beanMeta = memoize(()->findBeanMeta());
this.enumValues = memoize(()->findEnumValues());
@@ -334,7 +322,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.childUnswapMap = null;
this.cat = new Categories().set(ARGS);
this.beanContext = null;
- this.elementType = null;
+ this.elementType = memoize(()->findElementType());
this.keyValueTypes = memoize(()->findKeyValueTypes());
this.proxyInvocationHandler = null;
this.beanMeta = memoize(()->findBeanMeta());
@@ -370,7 +358,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
this.cat = mainType.cat;
this.fromStringMethod = mainType.fromStringMethod;
this.beanContext = mainType.beanContext;
- this.elementType = elementType;
+ this.elementType = elementType != null ?
memoize(()->elementType) : mainType.elementType;
this.keyValueTypes = (keyType != null || valueType != null) ?
memoize(()->Tuple2.of(keyType, valueType)) : mainType.keyValueTypes;
this.proxyInvocationHandler = mainType.proxyInvocationHandler;
this.beanMeta = mainType.beanMeta;
@@ -419,7 +407,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
public boolean canCreateNewInstance() {
if (isMemberClass() && isNotStatic())
return false;
- if (noArgConstructor.isPresent() ||
proxyInvocationHandler.isPresent() || (isArray() &&
elementType.canCreateNewInstance()))
+ if (noArgConstructor.isPresent() ||
proxyInvocationHandler.isPresent() || (isArray() &&
elementType.get().canCreateNewInstance()))
return true;
return false;
}
@@ -564,7 +552,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
*
* @return The element class type, or <jk>null</jk> if this class is
not an array or Collection.
*/
- public ClassMeta<?> getElementType() { return elementType; }
+ public ClassMeta<?> getElementType() { return elementType.get(); }
/**
* Returns the example of this class.
@@ -1388,6 +1376,22 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
return Tuple2.of(null,null);
}
+ private ClassMeta<?> findElementType() {
+ if (beanContext == null)
+ return null;
+ if (cat.is(ARRAY)) {
+ return
beanContext.getClassMeta(inner().getComponentType(), false);
+ } else if (cat.is(COLLECTION) || is(Optional.class)) {
+ // If this is a COLLECTION, see if it's parameterized
(e.g. AddressBook extends LinkedList<Person>)
+ var parameters = beanContext.findParameters(inner(),
inner());
+ if (nn(parameters) && parameters.length == 1) {
+ return parameters[0];
+ }
+ return beanContext.getClassMeta(Object.class);
+ }
+ return null;
+ }
+
@SuppressWarnings("unchecked")
private BuilderSwap<T,?> findBuilderSwap() {
var bc = beanContext;
@@ -1743,7 +1747,7 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
n = n.substring(i == -1 ? 0 : i + 1).replace('$', '.');
}
if (cat.is(ARRAY))
- return elementType.toString(sb,
simple).append('[').append(']');
+ return elementType.get().toString(sb,
simple).append('[').append(']');
if (cat.is(BEANMAP))
return
sb.append(cn(BeanMap.class)).append('<').append(n).append('>');
if (cat.is(MAP)) {
@@ -1754,8 +1758,10 @@ public class ClassMeta<T> extends ClassInfoTyped<T> {
return sb.append(n);
return sb.append(n).append('<').append(kt == null ? "?"
: kt.toString(simple)).append(',').append(vt == null ? "?" :
vt.toString(simple)).append('>');
}
- if (cat.is(COLLECTION) || is(Optional.class))
- return sb.append(n).append(elementType.isObject() ? ""
: "<" + elementType.toString(simple) + ">");
+ if (cat.is(COLLECTION) || is(Optional.class)) {
+ var et = elementType.get();
+ return sb.append(n).append(et != null && et.isObject()
? "" : "<" + (et == null ? "?" : et.toString(simple)) + ">");
+ }
return sb.append(n);
}