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 9b4e007867 New org.apache.juneau.commons.inject package
9b4e007867 is described below
commit 9b4e007867ced2339ed39595bc00ee39d0249282
Author: James Bognar <[email protected]>
AuthorDate: Tue Jan 6 16:07:11 2026 -0500
New org.apache.juneau.commons.inject package
---
.../juneau/commons/reflect/ParameterInfo.java | 8 +-
.../org/apache/juneau/commons/utils/BeanUtils.java | 391 ++++++++-
.../java/org/apache/juneau/parser/ParserSet.java | 1 -
.../apache/juneau/serializer/SerializerSet.java | 1 -
.../org/apache/juneau/svl/VarResolverSession.java | 1 -
.../juneau/rest/springboot/SpringBeanStore.java | 1 -
.../juneau/rest/springboot/SpringRestServlet.java | 1 -
.../java/org/apache/juneau/rest/RestChild.java | 1 -
.../java/org/apache/juneau/rest/RestChildren.java | 1 -
.../java/org/apache/juneau/rest/RestOpSession.java | 1 -
.../org/apache/juneau/rest/RestOperations.java | 1 -
.../java/org/apache/juneau/rest/RestSession.java | 1 -
.../juneau/rest/annotation/RestAnnotation.java | 1 -
.../org/apache/juneau/rest/arg/DefaultArg.java | 1 -
.../org/apache/juneau/rest/arg/RestOpArgList.java | 1 -
.../apache/juneau/rest/arg/RestOpSessionArgs.java | 1 -
.../apache/juneau/rest/config/DefaultConfig.java | 1 -
.../juneau/rest/debug/BasicDebugEnablement.java | 1 -
.../apache/juneau/rest/logger/BasicCallLogger.java | 1 -
.../rest/logger/BasicDisabledCallLogger.java | 1 -
.../juneau/rest/logger/BasicTestCallLogger.java | 1 -
.../rest/logger/BasicTestCaptureCallLogger.java | 1 -
.../org/apache/juneau/rest/logger/CallLogger.java | 1 -
.../apache/juneau/rest/logger/CallLoggerRule.java | 2 -
.../apache/juneau/rest/stats/MethodExecStats.java | 1 -
.../apache/juneau/rest/stats/MethodExecStore.java | 1 -
.../apache/juneau/rest/stats/MethodInvoker.java | 1 -
.../org/apache/juneau/rest/stats/ThrownStore.java | 1 -
.../juneau/commons/utils/BeanUtils_Test.java | 910 +++++++++++++++++++++
.../apache/juneau/mstat/MethodExecStore_Test.java | 1 -
.../org/apache/juneau/mstat/ThrownStore_Test.java | 1 -
.../juneau/rest/RestContext_Builder_Test.java | 1 -
.../java/org/apache/juneau/rest/Swagger_Test.java | 1 -
.../rest/annotation/RestAnnotation_Test.java | 1 -
34 files changed, 1282 insertions(+), 59 deletions(-)
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
index 526a9978d5..f3c29a995b 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/ParameterInfo.java
@@ -151,7 +151,7 @@ public class ParameterInfo extends ElementInfo implements
Annotatable {
private final ResettableSupplier<String> resolvedName =
memr(this::findNameInternal); // Resolved name from @Name annotation or
bytecode.
- private final ResettableSupplier<String> resolvedQualifier =
memr(this::findQualifierInternal); // Resolved qualifier from @Named
annotation.
+ private final ResettableSupplier<String> resolvedQualifier =
memr(this::findQualifierInternal); // Resolved qualifier from @Named or
@Qualifier annotation.
/**
* Constructor.
@@ -422,8 +422,8 @@ public class ParameterInfo extends ElementInfo implements
Annotatable {
* Finds the bean injection qualifier for this parameter.
*
* <p>
- * Searches for the {@link org.apache.juneau.annotation.Named @Named}
annotation value to determine
- * which named bean should be injected.
+ * Searches for the {@link org.apache.juneau.annotation.Named @Named}
or {@code javax.inject.Qualifier @Qualifier}
+ * annotation value to determine which named bean should be injected.
*
* <p>
* This method is used by the {@link org.apache.juneau.cp.BeanStore}
for bean injection.
@@ -432,7 +432,7 @@ public class ParameterInfo extends ElementInfo implements
Annotatable {
* <b>Note:</b> This is different from {@link #getResolvedName()} which
looks for {@link org.apache.juneau.annotation.Name @Name}
* annotations for bean property mapping.
*
- * @return The bean qualifier name if {@code @Named} annotation is
found, or <jk>null</jk> if not annotated.
+ * @return The bean qualifier name if {@code @Named} or {@code
@Qualifier} annotation is found, or <jk>null</jk> if not annotated.
* @see #getResolvedName()
*/
public String getResolvedQualifier() { return resolvedQualifier.get(); }
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/BeanUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/BeanUtils.java
index f826a8497e..216f9d592b 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/BeanUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/BeanUtils.java
@@ -20,32 +20,104 @@ import static java.util.stream.Collectors.*;
import static org.apache.juneau.commons.utils.CollectionUtils.*;
import static org.apache.juneau.commons.utils.Utils.*;
+import java.lang.reflect.ParameterizedType;
import java.util.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.reflect.*;
+/**
+ * Utility class for resolving method and constructor parameters from a bean
store.
+ *
+ * <p>
+ * This class provides functionality for dependency injection by analyzing
executable parameters (constructors and methods)
+ * and resolving their values from a {@link BeanStore}.
+ *
+ * <h5 class='section'>Supported Parameter Types:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>Single beans</b> - Parameters of type <c>T</c> are resolved by
looking up a bean of that type in the bean store.
+ * <li><b>Optional beans</b> - Parameters of type <c>Optional<T></c>
are resolved to <c>Optional.empty()</c> if no bean is found.
+ * <li><b>Arrays</b> - Parameters of type <c>T[]</c> are populated with
all beans of type <c>T</c> from the bean store.
+ * <li><b>Lists</b> - Parameters of type <c>List<T></c> are
populated with all beans of type <c>T</c> from the bean store.
+ * <li><b>Sets</b> - Parameters of type <c>Set<T></c> are populated
with all beans of type <c>T</c> from the bean store.
+ * <li><b>Maps</b> - Parameters of type <c>Map<String,T></c> are
populated with all beans of type <c>T</c> keyed by bean name.
+ * </ul>
+ *
+ * <h5 class='section'>Bean Qualifiers:</h5>
+ * <p>
+ * Parameters can be annotated with {@link org.apache.juneau.annotation.Named
@Named} or {@code javax.inject.Qualifier @Qualifier}
+ * to specify which named bean should be injected. For collections, arrays,
and maps, qualifiers are ignored and all beans
+ * of the element type are included.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ * <jc>// Bean store with multiple services</jc>
+ * BeanStore <jv>beanStore</jv> = BasicBeanStore.<jsm>create</jsm>()
+ * .addBean(MyService.<jk>class</jk>, <jk>new</jk> MyService1(),
<js>"service1"</js>)
+ * .addBean(MyService.<jk>class</jk>, <jk>new</jk> MyService2(),
<js>"service2"</js>)
+ * .build();
+ *
+ * <jc>// Constructor with various parameter types</jc>
+ * <jk>public</jk> MyClass(
+ * MyService <jv>primary</jv>, <jc>// Gets unnamed bean or first
found</jc>
+ * <ja>@Named</ja>(<js>"service1"</js>) MyService <jv>named</jv>,
<jc>// Gets bean named "service1"</jc>
+ * Optional<MyService> <jv>optional</jv>, <jc>// Gets
Optional.of(bean) or Optional.empty()</jc>
+ * List<MyService> <jv>allServices</jv>, <jc>// Gets all
MyService beans</jc>
+ * Map<String,MyService> <jv>servicesMap</jv> <jc>// Gets
all MyService beans keyed by name</jc>
+ * ) { ... }
+ *
+ * <jc>// Resolve parameters</jc>
+ * BeanUtils <jv>utils</jv> = <jk>new</jk> BeanUtils();
+ * Object[] <jv>params</jv> = <jv>utils</jv>.getParams(constructor,
<jv>beanStore</jv>, <jk>null</jk>);
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ * <li class='jc'>{@link BeanStore}
+ * <li class='jc'>{@link BasicBeanStore}
+ * </ul>
+ */
public class BeanUtils {
/**
- * Given an executable, returns a list of types that are missing from
this factory.
+ * Returns a comma-delimited list of parameter types that are missing
from the bean store.
+ *
+ * <p>
+ * Analyzes the parameters of the specified executable and checks if
all required beans are available in the bean store.
*
- * @param executable The constructor or method to get the params for.
- * @param outer The outer object to use when instantiating inner
classes. Can be <jk>null</jk>.
- * @return A comma-delimited list of types that are missing from this
factory, or <jk>null</jk> if none are missing.
+ * <p>
+ * The following parameter types are considered optional and are not
checked:
+ * <ul>
+ * <li>Parameters of type <c>Optional<T></c>
+ * <li>Parameters of type <c>T[]</c>, <c>List<T></c>,
<c>Set<T></c>, or <c>Map<String,T></c>
+ * <li>The first parameter if it matches the <c>bean</c> object
type (for non-static inner classes)
+ * </ul>
+ *
+ * <p>
+ * If a parameter has a {@link org.apache.juneau.annotation.Named
@Named} or {@code @Qualifier} annotation,
+ * the method checks for a bean with that specific name. Otherwise, it
checks for an unnamed bean of the parameter type.
+ *
+ * @param executable The constructor or method to analyze.
+ * @param beanStore The bean store to check for beans.
+ * @param bean The outer class instance for non-static inner class
constructors/methods.
+ * If the first parameter type matches this object's type, it is
used as the first parameter and not checked in the bean store.
+ * Can be <jk>null</jk> for regular classes or static inner
classes.
+ * @return A comma-delimited, sorted list of missing parameter types
(e.g., <js>"String,Integer"</js>),
+ * or <jk>null</jk> if all required parameters are available.
*/
- public String getMissingParams(ExecutableInfo executable, BeanStore
beanStore, Object outer) {
+ public String getMissingParams(ExecutableInfo executable, BeanStore
beanStore, Object bean) {
var params = executable.getParameters();
List<String> l = list();
loop: for (int i = 0; i < params.size(); i++) {
var pi = params.get(i);
var pt = pi.getParameterType();
- if (i == 0 && nn(outer) && pt.isInstance(outer))
+ if (i == 0 && nn(bean) && pt.isInstance(bean))
continue loop;
if (pt.is(Optional.class))
continue loop;
- var beanName = pi.getResolvedQualifier(); // Use
@Named for bean injection
- var ptc = pt.inner();
+ if (isCollectionType(pt))
+ continue loop; // Collections and arrays are
always satisfied (even if empty), so skip them
+ var beanName = pi.getResolvedQualifier(); // Use
@Named/@Qualified for bean injection
+ var ptc = pt.unwrap(Optional.class).inner();
if (beanName == null && ! beanStore.hasBean(ptc))
l.add(pt.getNameSimple());
if (nn(beanName) && ! beanStore.hasBean(ptc, beanName))
@@ -55,22 +127,55 @@ public class BeanUtils {
}
/**
- * Returns the corresponding beans in this factory for the specified
param types.
+ * Resolves and returns parameter values from the bean store for the
specified executable.
+ *
+ * <p>
+ * For each parameter in the executable, this method:
+ * <ul>
+ * <li>If the first parameter type matches the <c>bean</c> object
type, uses the <c>bean</c> object
+ * (for non-static inner class constructors/methods).
+ * <li>If the parameter is a collection/array/map type, collects
all beans of the element type.
+ * <li>Otherwise, looks up a single bean by type and optional
qualifier name.
+ * </ul>
+ *
+ * <h5 class='section'>Parameter Resolution:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>Single beans</b> - Resolved using {@link
BeanStore#getBean(Class)} or {@link BeanStore#getBean(Class, String)}.
+ * <li><b>Optional beans</b> - Wrapped in <c>Optional</c>, or
<c>Optional.empty()</c> if not found.
+ * <li><b>Arrays</b> - All beans of the element type are collected
into an array.
+ * <li><b>Lists</b> - All beans of the element type are collected
into a <c>List</c>.
+ * <li><b>Sets</b> - All beans of the element type are collected
into a <c>LinkedHashSet</c>.
+ * <li><b>Maps</b> - All beans of the value type are collected
into a <c>LinkedHashMap</c> keyed by bean name.
+ * </ul>
*
- * @param executable The constructor or method to get the params for.
- * @param outer The outer object to use when instantiating inner
classes. Can be <jk>null</jk>.
- * @return The corresponding beans in this factory for the specified
param types.
+ * @param executable The constructor or method to get parameters for.
+ * @param beanStore The bean store to resolve beans from.
+ * @param bean The outer class instance for non-static inner class
constructors/methods.
+ * If the first parameter type matches this object's type, it is
used as the first parameter value.
+ * Can be <jk>null</jk> for regular classes or static inner
classes.
+ * @return An array of parameter values in the same order as the
executable parameters.
+ * May contain <jk>null</jk> values if a required bean is not
found (for non-Optional parameters).
*/
- public Object[] getParams(ExecutableInfo executable, BeanStore
beanStore, Object outer) {
+ public Object[] getParams(ExecutableInfo executable, BeanStore
beanStore, Object bean) {
var o = new Object[executable.getParameterCount()];
for (var i = 0; i < executable.getParameterCount(); i++) {
var pi = executable.getParameter(i);
var pt = pi.getParameterType();
- if (i == 0 && nn(outer) && pt.isInstance(outer)) {
- o[i] = outer;
+ if (i == 0 && nn(bean) && pt.isInstance(bean)) {
+ o[i] = bean;
} else {
var beanQualifier = pi.getResolvedQualifier();
- var ptc = pt.unwrap(Optional.class).inner();
+ var ptUnwrapped = pt.unwrap(Optional.class);
+
+ // Handle collections and arrays
+ var collectionValue = getCollectionValue(pi,
ptUnwrapped, beanStore, beanQualifier);
+ if (nn(collectionValue)) {
+ o[i] = pt.is(Optional.class) ?
Optional.of(collectionValue) : collectionValue;
+ continue;
+ }
+
+ // Handle single bean
+ var ptc = ptUnwrapped.inner();
var o2 = beanQualifier == null ?
beanStore.getBean(ptc) : beanStore.getBean(ptc, beanQualifier);
o[i] = pt.is(Optional.class) ? o2 :
o2.orElse(null);
}
@@ -78,6 +183,33 @@ public class BeanUtils {
return o;
}
+ /**
+ * Resolves parameters from the bean store and invokes the specified
executable.
+ *
+ * <p>
+ * This is a convenience method that combines {@link
#getParams(ExecutableInfo, BeanStore, Object)} with
+ * {@link ConstructorInfo#newInstance(Object...)} or {@link
MethodInfo#invoke(Object, Object...)}.
+ *
+ * <p>
+ * Parameter usage:
+ * <ul>
+ * <li><b>For constructors</b> - The <c>bean</c> parameter is
passed as the <c>outer</c> parameter to
+ * {@link #getParams(ExecutableInfo, BeanStore, Object)}.
For non-static inner class constructors,
+ * this will be used as the first parameter (the outer
class instance). For regular constructors,
+ * this parameter is ignored if it doesn't match the first
parameter type.
+ * <li><b>For methods</b> - The <c>bean</c> parameter is the
object instance on which to invoke the method
+ * (or <jk>null</jk> for static methods). It is also
passed as the <c>outer</c> parameter to
+ * {@link #getParams(ExecutableInfo, BeanStore, Object)}
for inner class handling.
+ * </ul>
+ *
+ * @param <T> The return type of the executable.
+ * @param executable The constructor or method to invoke.
+ * @param beanStore The bean store to resolve parameters from.
+ * @param bean For constructors: the outer class instance for
non-static inner classes (can be <jk>null</jk>).
+ * For methods: the object instance on which to invoke the method
(or <jk>null</jk> for static methods).
+ * @return The result of invoking the executable.
+ * @throws ExecutableException If the executable cannot be invoked or
parameter resolution fails.
+ */
public <T> T invoke(ExecutableInfo executable, BeanStore beanStore,
Object bean) {
var params = getParams(executable, beanStore, bean);
if (executable instanceof ConstructorInfo ci)
@@ -86,25 +218,238 @@ public class BeanUtils {
}
/**
- * Given the list of param types, returns <jk>true</jk> if this factory
has all the parameters for the specified executable.
+ * Returns <jk>true</jk> if the bean store has all required parameters
for the specified executable.
+ *
+ * <p>
+ * This method performs the same checks as {@link
#getMissingParams(ExecutableInfo, BeanStore, Object)} but
+ * returns a boolean instead of a list of missing types.
+ *
+ * <p>
+ * The following parameter types are considered optional and are not
checked:
+ * <ul>
+ * <li>Parameters of type <c>Optional<T></c>
+ * <li>Parameters of type <c>T[]</c>, <c>List<T></c>,
<c>Set<T></c>, or <c>Map<String,T></c>
+ * <li>The first parameter if it matches the <c>bean</c> object
type (for non-static inner classes)
+ * </ul>
*
- * @param executable The constructor or method to get the params for.
- * @param outer The outer object to use when instantiating inner
classes. Can be <jk>null</jk>.
- * @return A comma-delimited list of types that are missing from this
factory.
+ * @param executable The constructor or method to check.
+ * @param beanStore The bean store to check for beans.
+ * @param bean The outer class instance for non-static inner class
constructors/methods.
+ * If the first parameter type matches this object's type, it is
used as the first parameter and not checked in the bean store.
+ * Can be <jk>null</jk> for regular classes or static inner
classes.
+ * @return <jk>true</jk> if all required parameters are available in
the bean store, <jk>false</jk> otherwise.
*/
- public boolean hasAllParams(ExecutableInfo executable, BeanStore
beanStore, Object outer) {
+ public boolean hasAllParams(ExecutableInfo executable, BeanStore
beanStore, Object bean) {
loop: for (int i = 0; i < executable.getParameterCount(); i++) {
var pi = executable.getParameter(i);
var pt = pi.getParameterType();
- if (i == 0 && nn(outer) && pt.isInstance(outer))
+ if (i == 0 && nn(bean) && pt.isInstance(bean))
continue loop;
if (pt.is(Optional.class))
continue loop;
+ if (isCollectionType(pt))
+ continue loop; // Collections and arrays are
always satisfied (even if empty)
var beanQualifier = pi.getResolvedQualifier();
- var ptc = pt.inner();
+ var ptc = pt.unwrap(Optional.class).inner();
if ((beanQualifier == null && ! beanStore.hasBean(ptc))
|| (nn(beanQualifier) && ! beanStore.hasBean(ptc, beanQualifier)))
return false;
}
return true;
}
+
+
//-----------------------------------------------------------------------------------------------------------------
+ // Helper methods
+
//-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Returns <jk>true</jk> if the specified type is a collection type
that should be populated with multiple beans.
+ *
+ * <p>
+ * Supported collection types:
+ * <ul>
+ * <li>Arrays (<c>T[]</c>)
+ * <li><c>List<T></c>
+ * <li><c>Set<T></c>
+ * <li><c>Map<String,T></c> (key type must be <c>String</c>)
+ * </ul>
+ *
+ * @param pt The parameter type to check.
+ * @return <jk>true</jk> if the type is a supported collection, array,
or map type.
+ */
+ @SuppressWarnings("static-method")
+ private boolean isCollectionType(ClassInfo pt) {
+ if (pt.isArray())
+ return true;
+ var inner = pt.inner();
+ if (nn(inner)) {
+ return List.class.isAssignableFrom(inner) ||
Set.class.isAssignableFrom(inner) || Map.class.isAssignableFrom(inner);
+ }
+ // Defensive check: inner() returns null for non-class types
(e.g., TypeVariable, WildcardType).
+ // In practice, this is unreachable for real parameter types
since Parameter.getType() always returns a Class.
+ // However, it's kept as a safety check for edge cases or
future use.
+ return false;
+ }
+
+ /**
+ * Extracts the element or value type from a collection, array, or map
parameter type.
+ *
+ * <p>
+ * For arrays, returns the component type. For <c>List<T></c> or
<c>Set<T></c>, returns <c>T</c>.
+ * For <c>Map<String,T></c>, returns <c>T</c> (the value type).
+ *
+ * @param pi The parameter info containing generic type information.
+ * @param pt The parameter type (must be a collection, array, or map).
+ * @return The element type (for arrays/collections) or value type (for
maps),
+ * or <jk>null</jk> if the type is not a supported collection type
or the element type cannot be determined.
+ */
+ @SuppressWarnings("static-method")
+ private Class<?> getElementType(ParameterInfo pi, ClassInfo pt) {
+ // Handle arrays
+ if (pt.isArray()) {
+ return pt.getComponentType().inner();
+ }
+
+ // Get the parameterized type and unwrap Optional if present
+ var parameterizedType = pi.getParameterizedType();
+ if (parameterizedType instanceof ParameterizedType pt2) {
+ var rawType = pt2.getRawType();
+ // If wrapped in Optional, unwrap it to get the nested
type
+ if (rawType instanceof Class<?> rawClass && rawClass ==
Optional.class) {
+ var typeArgs = pt2.getActualTypeArguments();
+ if (typeArgs.length > 0 && typeArgs[0]
instanceof ParameterizedType nestedPt) {
+ // Optional<List<T>> -> List<T>
+ parameterizedType = nestedPt;
+ } else {
+ // Optional<SomeClass> - not a
collection, return null
+ return null;
+ }
+ }
+ } else if (pt.innerType() instanceof ParameterizedType) {
+ // If pt is already a parameterized type (e.g.,
List<TestService> after unwrapping Optional),
+ // use pt.innerType() as the parameterizedType
+ parameterizedType = (ParameterizedType) pt.innerType();
+ }
+
+ // Handle List<T> or Set<T>
+ var inner = pt.inner();
+ if (nn(inner) && (List.class.isAssignableFrom(inner) ||
Set.class.isAssignableFrom(inner))) {
+ if (parameterizedType instanceof ParameterizedType pt2)
{
+ var typeArgs = pt2.getActualTypeArguments();
+ if (typeArgs.length > 0 && typeArgs[0]
instanceof Class<?> elementClass) {
+ return elementClass;
+ }
+ }
+ }
+
+ // Handle Map<String,T> - extract value type (second type
argument)
+ if (nn(inner) && Map.class.isAssignableFrom(inner)) {
+ if (parameterizedType instanceof ParameterizedType pt2)
{
+ var typeArgs = pt2.getActualTypeArguments();
+ // Verify key type is String and get value type
+ if (typeArgs.length >= 2 && typeArgs[0] ==
String.class && typeArgs[1] instanceof Class<?> valueClass) {
+ return valueClass;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Resolves and creates a collection, array, or map containing all
beans of the element type from the bean store.
+ *
+ * <p>
+ * Retrieves all beans of the element type using {@link
BeanStore#getBeansOfType(Class)} and converts them
+ * to the appropriate collection type:
+ * <ul>
+ * <li>Arrays - Creates an array of the element type
+ * <li><c>List<T></c> - Creates an <c>ArrayList</c>
+ * <li><c>Set<T></c> - Creates a <c>LinkedHashSet</c>
(preserves insertion order)
+ * <li><c>Map<String,T></c> - Creates a <c>LinkedHashMap</c>
keyed by bean name
+ * </ul>
+ *
+ * <p>
+ * The <c>beanQualifier</c> parameter is ignored for collections, as
all beans of the element type are included.
+ *
+ * @param pi The parameter info containing generic type information.
+ * @param pt The parameter type (unwrapped from <c>Optional</c>).
+ * @param beanStore The bean store to retrieve beans from.
+ * @param beanQualifier The bean qualifier (ignored for
collections/arrays/maps).
+ * @return The collection, array, or map containing all beans of the
element type,
+ * or <jk>null</jk> if the parameter type is not a supported
collection type.
+ */
+ private Object getCollectionValue(ParameterInfo pi, ClassInfo pt,
BeanStore beanStore, String beanQualifier) {
+ if (! isCollectionType(pt))
+ return null;
+
+ var elementType = getElementType(pi, pt);
+ if (elementType == null)
+ return null;
+
+ // Get all beans of the element type
+ var beans = beanStore.getBeansOfType(elementType);
+
+ // Handle Map<String,T> - getBeansOfType already returns
Map<String,T>
+ var inner = pt.inner();
+ if (nn(inner) && Map.class.isAssignableFrom(inner)) {
+ // Verify it's Map<String,T> and return the map directly
+ var parameterizedType = pi.getParameterizedType();
+ // Unwrap Optional if present
+ if (parameterizedType instanceof ParameterizedType pt2)
{
+ var rawType = pt2.getRawType();
+ if (rawType instanceof Class<?> rawClass &&
rawClass == Optional.class) {
+ var typeArgs =
pt2.getActualTypeArguments();
+ if (typeArgs.length > 0 && typeArgs[0]
instanceof ParameterizedType nestedPt) {
+ parameterizedType = nestedPt;
+ } else {
+ // Defensive check: if
Optional<SomeClass> where SomeClass is not a ParameterizedType.
+ // In practice, this is likely
unreachable because:
+ // - If pt is Map.class (raw),
getElementType returns null and we return early (line 383)
+ // - If pt is Map<String,T>
(parameterized), typeArgs[0] is a ParameterizedType
+ // However, kept as a safety
check for edge cases.
+ return null;
+ }
+ }
+ } else if (pt.innerType() instanceof ParameterizedType)
{
+ // If pt is already a parameterized type (e.g.,
Map<String,TestService> after unwrapping Optional),
+ // use pt.innerType() as the parameterizedType
+ parameterizedType = (ParameterizedType)
pt.innerType();
+ }
+ if (parameterizedType instanceof ParameterizedType pt2)
{
+ var typeArgs = pt2.getActualTypeArguments();
+ if (typeArgs.length >= 2 && typeArgs[0] ==
String.class) {
+ return new LinkedHashMap<>(beans);
+ }
+ }
+ return null;
+ }
+
+ var values = new ArrayList<>(beans.values());
+
+ // Convert to appropriate collection/array type
+ if (pt.isArray()) {
+ var array =
java.lang.reflect.Array.newInstance(elementType, values.size());
+ for (var i = 0; i < values.size(); i++)
+ java.lang.reflect.Array.set(array, i,
values.get(i));
+ return array;
+ }
+
+ if (nn(inner)) {
+ if (Set.class.isAssignableFrom(inner)) {
+ return new LinkedHashSet<>(values);
+ } else if (List.class.isAssignableFrom(inner)) {
+ return values;
+ }
+ }
+
+ // Defensive check: return null if inner is null or not a
supported collection type.
+ // In practice, this is likely unreachable because:
+ // - isCollectionType only returns true for List, Set, Map, or
array
+ // - List and Set are handled above (lines 430-434)
+ // - Map is handled earlier (lines 390-416)
+ // - Arrays are handled earlier (lines 422-426)
+ // - If inner() is null, isCollectionType returns false, so we
return early (line 379)
+ // However, kept as a safety check for edge cases.
+ return null;
+ }
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSet.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSet.java
index a923f7dd53..eeefc16351 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSet.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserSet.java
@@ -29,7 +29,6 @@ import java.util.stream.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Represents a group of {@link Parser Parsers} that can be looked up by media
type.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSet.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSet.java
index b5b34f1847..5788fc7723 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSet.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSet.java
@@ -29,7 +29,6 @@ import java.util.stream.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Represents a group of {@link Serializer Serializers} that can be looked up
by media type.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/svl/VarResolverSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/svl/VarResolverSession.java
index b0cf371561..c2f75d9587 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/svl/VarResolverSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/svl/VarResolverSession.java
@@ -30,7 +30,6 @@ import java.util.*;
import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.cp.*;
/**
* A var resolver session that combines a {@link VarResolver} with one or more
session objects.
diff --git
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
index b71a001f98..79036cbc75 100644
---
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
+++
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringBeanStore.java
@@ -21,7 +21,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import java.util.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.springframework.context.*;
/**
diff --git
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringRestServlet.java
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringRestServlet.java
index 5d51798fd4..9a11475d56 100644
---
a/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringRestServlet.java
+++
b/juneau-rest/juneau-rest-server-springboot/src/main/java/org/apache/juneau/rest/springboot/SpringRestServlet.java
@@ -19,7 +19,6 @@ package org.apache.juneau.rest.springboot;
import java.util.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.servlet.*;
import org.springframework.beans.factory.annotation.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChild.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChild.java
index f162d79734..542a2c6702 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChild.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChild.java
@@ -17,7 +17,6 @@
package org.apache.juneau.rest;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Represents a simple child REST resource / path mapping.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChildren.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChildren.java
index 390a58d2a9..ebc6ad8429 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChildren.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestChildren.java
@@ -23,7 +23,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.util.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpSession.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpSession.java
index 693b04f6c5..fce0b25531 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpSession.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpSession.java
@@ -24,7 +24,6 @@ import org.apache.http.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.collections.FluentMap;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.http.response.*;
import org.apache.juneau.rest.logger.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperations.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperations.java
index 4d95309caa..95c53993a1 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperations.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOperations.java
@@ -23,7 +23,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.http.response.*;
import org.apache.juneau.rest.annotation.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
index 65903d0fc8..f957d8f2df 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestSession.java
@@ -28,7 +28,6 @@ import org.apache.juneau.*;
import org.apache.juneau.commons.collections.FluentMap;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.http.response.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.logger.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
index 6bd0963a12..242863583f 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
@@ -22,7 +22,6 @@ import java.lang.annotation.*;
import java.nio.charset.*;
import org.apache.juneau.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.annotation.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/DefaultArg.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/DefaultArg.java
index bb5fb4d45a..4036beb09a 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/DefaultArg.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/DefaultArg.java
@@ -20,7 +20,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpArgList.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpArgList.java
index ef3c0bc760..f1e2dc9ebc 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpArgList.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpArgList.java
@@ -23,7 +23,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* A list of {@link RestOpArg} classes.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpSessionArgs.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpSessionArgs.java
index 464d13d4c2..13afbef8fd 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpSessionArgs.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/RestOpSessionArgs.java
@@ -16,7 +16,6 @@
*/
package org.apache.juneau.rest.arg;
-import org.apache.juneau.cp.*;
import org.apache.juneau.commons.function.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.reflect.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/DefaultConfig.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/DefaultConfig.java
index 953f10450c..568c237dff 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/DefaultConfig.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/DefaultConfig.java
@@ -18,7 +18,6 @@ package org.apache.juneau.rest.config;
import org.apache.juneau.annotation.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.rest.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
index 0b1172510d..d922de3a12 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
@@ -23,7 +23,6 @@ import static
org.apache.juneau.rest.annotation.RestOpAnnotation.*;
import java.util.*;
import org.apache.juneau.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.utils.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicCallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicCallLogger.java
index fba72427ff..4263394597 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicCallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicCallLogger.java
@@ -20,7 +20,6 @@ import static java.util.logging.Level.*;
import static org.apache.juneau.rest.logger.CallLoggingDetail.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.*;
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicDisabledCallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicDisabledCallLogger.java
index 3a24d92f5f..aae28e863a 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicDisabledCallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicDisabledCallLogger.java
@@ -17,7 +17,6 @@
package org.apache.juneau.rest.logger;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Default implementation of a {@link CallLogger} that never logs REST calls.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCallLogger.java
index bbd79096ca..60c0b3c6b0 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCallLogger.java
@@ -22,7 +22,6 @@ import static org.apache.juneau.commons.utils.Utils.*;
import static org.apache.juneau.rest.logger.CallLoggingDetail.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.*;
import jakarta.servlet.http.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCaptureCallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCaptureCallLogger.java
index 9957275a5d..8b65ee0e4c 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCaptureCallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/BasicTestCaptureCallLogger.java
@@ -24,7 +24,6 @@ import java.util.logging.*;
import org.apache.juneau.assertions.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
*
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
index 19b00cc84c..b82289cb34 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
@@ -33,7 +33,6 @@ import org.apache.juneau.*;
import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.utils.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.stats.*;
import org.apache.juneau.rest.util.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLoggerRule.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLoggerRule.java
index 26c7927d35..185ce203f8 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLoggerRule.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLoggerRule.java
@@ -25,8 +25,6 @@ import java.util.logging.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
-
import jakarta.servlet.http.*;
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStats.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStats.java
index 57e4277307..58c46b8a0b 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStats.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStats.java
@@ -26,7 +26,6 @@ import java.util.concurrent.atomic.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.collections.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Method execution statistics.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStore.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStore.java
index b1e1282c6d..fbb1f716ed 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStore.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodExecStore.java
@@ -25,7 +25,6 @@ import java.util.concurrent.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* Method execution statistics database.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodInvoker.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodInvoker.java
index dce49d2c8b..130992ccac 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodInvoker.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/MethodInvoker.java
@@ -22,7 +22,6 @@ import java.lang.reflect.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.reflect.*;
-import org.apache.juneau.cp.*;
/**
* A wrapper around a {@link Method#invoke(Object, Object...)} method that
allows for basic instrumentation.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStore.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStore.java
index 1d336c8813..02742b7db5 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStore.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/stats/ThrownStore.java
@@ -27,7 +27,6 @@ import java.util.concurrent.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
/**
* An in-memory cache of thrown exceptions.
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/BeanUtils_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/BeanUtils_Test.java
new file mode 100644
index 0000000000..a7b36bf300
--- /dev/null
+++
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/BeanUtils_Test.java
@@ -0,0 +1,910 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.commons.utils;
+
+import static org.apache.juneau.commons.utils.Utils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.Named;
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.commons.reflect.*;
+import org.junit.jupiter.api.*;
+
+class BeanUtils_Test extends TestBase {
+
+ // Test bean classes
+ static class TestService {
+ private final String name;
+ TestService(String name) { this.name = name; }
+ String getName() { return name; }
+ @Override public String toString() { return "TestService[" +
name + "]"; }
+ @Override public boolean equals(Object o) { return o instanceof
TestService o2 && eq(this, o2, (x,y) -> eq(x.name, y.name)); }
+ @Override public int hashCode() { return h(name); }
+ }
+
+ static class AnotherService {
+ private final int value;
+ AnotherService(int value) { this.value = value; }
+ int getValue() { return value; }
+ @Override public boolean equals(Object o) { return o instanceof
AnotherService o2 && eq(this, o2, (x,y) -> eq(x.value, y.value)); }
+ @Override public int hashCode() { return value; }
+ }
+
+ // Test classes with various constructor/method signatures
+ public static class TestClass1 {
+ public TestClass1(TestService service) {
+ // Single bean parameter
+ }
+ }
+
+ public static class TestClass2 {
+ public TestClass2(@Named("service1") TestService service) {
+ // Named bean parameter
+ }
+ }
+
+ public static class TestClass3 {
+ public TestClass3(Optional<TestService> service) {
+ // Optional parameter
+ }
+ }
+
+ public static class TestClass4 {
+ public TestClass4(TestService[] services) {
+ // Array parameter
+ }
+ }
+
+ public static class TestClass5 {
+ public TestClass5(List<TestService> services) {
+ // List parameter
+ }
+ }
+
+ public static class TestClass6 {
+ public TestClass6(Set<TestService> services) {
+ // Set parameter
+ }
+ }
+
+ public static class TestClass7 {
+ public TestClass7(Map<String, TestService> services) {
+ // Map parameter
+ }
+ }
+
+ public static class TestClass8 {
+ public TestClass8(TestService service1, AnotherService
service2) {
+ // Multiple parameters
+ }
+ }
+
+ public static class TestClass9 {
+ public TestClass9(TestService service, Optional<AnotherService>
optional, List<TestService> list) {
+ // Mixed parameter types
+ }
+ }
+
+ // Inner class for testing outer parameter
+ // Note: This is a non-static inner class, so it requires an outer
instance
+ static class OuterClass {
+ public OuterClass() {}
+
+ // Non-static inner class
+ class InnerClass {
+ public InnerClass(OuterClass outer, TestService
service) {
+ // First parameter is outer class instance
+ }
+ }
+ }
+
+ // Test method classes
+ public static class TestMethodClass {
+ public void method1(TestService service) {}
+ public void method2(@Named("service1") TestService service) {}
+ public void method3(Optional<TestService> service) {}
+ public void method4(TestService[] services) {}
+ public void method5(List<TestService> services) {}
+ public void method6(Set<TestService> services) {}
+ public void method7(Map<String, TestService> services) {}
+ public static void staticMethod(TestService service) {}
+ }
+
+ private BeanUtils utils;
+ private BasicBeanStore beanStore;
+
+ @BeforeEach
+ void setUp() {
+ utils = new BeanUtils();
+ beanStore = new BasicBeanStore(null);
+ }
+
+
//====================================================================================================
+ // getMissingParams
+
//====================================================================================================
+
+ @Test
+ void a01_getMissingParams_allAvailable() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result);
+ }
+
+ @Test
+ void a02_getMissingParams_singleMissing() throws Exception {
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertEquals("TestService", result);
+ }
+
+ @Test
+ void a03_getMissingParams_namedBeanFound() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"),
"service1");
+ var constructor =
ClassInfo.of(TestClass2.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result);
+ }
+
+ @Test
+ void a04_getMissingParams_namedBeanMissing() throws Exception {
+ var constructor =
ClassInfo.of(TestClass2.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertEquals("TestService@service1", result);
+ }
+
+ @Test
+ void a05_getMissingParams_optionalSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass3.class).getPublicConstructor(x ->
x.hasParameterTypes(Optional.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result); // Optional parameters are skipped
+ }
+
+ @Test
+ void a06_getMissingParams_arraySkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass4.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService[].class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result); // Arrays are skipped
+ }
+
+ @Test
+ void a07_getMissingParams_listSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass5.class).getPublicConstructor(x ->
x.hasParameterTypes(List.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result); // Lists are skipped
+ }
+
+ @Test
+ void a08_getMissingParams_setSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass6.class).getPublicConstructor(x ->
x.hasParameterTypes(Set.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result); // Sets are skipped
+ }
+
+ @Test
+ void a09_getMissingParams_mapSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass7.class).getPublicConstructor(x ->
x.hasParameterTypes(Map.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertNull(result); // Maps are skipped
+ }
+
+ @Test
+ void a10_getMissingParams_multipleMissing() throws Exception {
+ var constructor =
ClassInfo.of(TestClass8.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class, AnotherService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertTrue(result.contains("AnotherService"));
+ assertTrue(result.contains("TestService"));
+ // Should be sorted
+ assertTrue(result.indexOf("AnotherService") <
result.indexOf("TestService"));
+ }
+
+ @Test
+ void a11_getMissingParams_innerClassOuterSkipped() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ beanStore.addBean(OuterClass.class, new OuterClass()); // Add
outer class bean so explicit parameter is available
+ var outer = new OuterClass();
+ // Use the class literal directly instead of getting it from an
instance
+ // For non-static inner classes, the constructor has 3
parameters: (implicit outer, explicit outer, TestService)
+ // The first parameter (implicit outer) is skipped, but the
second (explicit outer) is checked
+ var classInfo = ClassInfo.of(OuterClass.InnerClass.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 3 &&
x.getParameter(2).isType(TestService.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available constructors: " + constructors);
+ var constructor = constructorOpt.get();
+ var result = utils.getMissingParams(constructor, beanStore,
outer);
+ assertNull(result); // All parameters should be available
(implicit outer skipped, explicit outer and TestService in store)
+ }
+
+
//====================================================================================================
+ // getParams
+
//====================================================================================================
+
+ @Test
+ void b01_getParams_singleBean() throws Exception {
+ var service = new TestService("test1");
+ beanStore.addBean(TestService.class, service);
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertSame(service, params[0]);
+ }
+
+ @Test
+ void b02_getParams_singleBeanNotFound() throws Exception {
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertNull(params[0]);
+ }
+
+ @Test
+ void b03_getParams_namedBean() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1, "service1");
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass2.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertSame(service1, params[0]);
+ }
+
+ @Test
+ void b04_getParams_optionalBeanFound() throws Exception {
+ var service = new TestService("test1");
+ beanStore.addBean(TestService.class, service);
+ var constructor =
ClassInfo.of(TestClass3.class).getPublicConstructor(x ->
x.hasParameterTypes(Optional.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof Optional);
+ var opt = (Optional<TestService>) params[0];
+ assertTrue(opt.isPresent());
+ assertSame(service, opt.get());
+ }
+
+ @Test
+ void b05_getParams_optionalBeanNotFound() throws Exception {
+ var constructor =
ClassInfo.of(TestClass3.class).getPublicConstructor(x ->
x.hasParameterTypes(Optional.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof Optional);
+ var opt = (Optional<TestService>) params[0];
+ assertFalse(opt.isPresent());
+ }
+
+ @Test
+ void b06_getParams_array() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass4.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService[].class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof TestService[]);
+ var array = (TestService[]) params[0];
+ assertEquals(2, array.length);
+ assertTrue(array[0].equals(service1) ||
array[0].equals(service2));
+ assertTrue(array[1].equals(service1) ||
array[1].equals(service2));
+ }
+
+ @Test
+ void b07_getParams_arrayEmpty() throws Exception {
+ var constructor =
ClassInfo.of(TestClass4.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService[].class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof TestService[]);
+ var array = (TestService[]) params[0];
+ assertEquals(0, array.length);
+ }
+
+ @Test
+ void b08_getParams_list() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass5.class).getPublicConstructor(x ->
x.hasParameterTypes(List.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof List);
+ var list = (List<TestService>) params[0];
+ assertEquals(2, list.size());
+ assertTrue(list.contains(service1));
+ assertTrue(list.contains(service2));
+ }
+
+ @Test
+ void b09_getParams_set() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass6.class).getPublicConstructor(x ->
x.hasParameterTypes(Set.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof Set);
+ var set = (Set<TestService>) params[0];
+ assertEquals(2, set.size());
+ assertTrue(set.contains(service1));
+ assertTrue(set.contains(service2));
+ }
+
+ @Test
+ void b10_getParams_map() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1, "service1");
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass7.class).getPublicConstructor(x ->
x.hasParameterTypes(Map.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof Map);
+ var map = (Map<String, TestService>) params[0];
+ assertEquals(2, map.size());
+ assertSame(service1, map.get("service1"));
+ assertSame(service2, map.get("service2"));
+ }
+
+ @Test
+ void b11_getParams_mapWithUnnamedBean() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1); // Unnamed
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass7.class).getPublicConstructor(x ->
x.hasParameterTypes(Map.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(1, params.length);
+ assertTrue(params[0] instanceof Map);
+ var map = (Map<String, TestService>) params[0];
+ assertEquals(2, map.size());
+ assertSame(service1, map.get("")); // Unnamed beans use empty
string as key
+ assertSame(service2, map.get("service2"));
+ }
+
+ @Test
+ void b12_getParams_mixedTypes() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ var another = new AnotherService(42);
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ beanStore.addBean(AnotherService.class, another);
+ var constructor =
ClassInfo.of(TestClass9.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class, Optional.class, List.class)).get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(3, params.length);
+ assertSame(service1, params[0]); // Single bean
+ assertTrue(params[1] instanceof Optional); // Optional
+ var opt = (Optional<AnotherService>) params[1];
+ assertTrue(opt.isPresent());
+ assertSame(another, opt.get());
+ assertTrue(params[2] instanceof List); // List
+ var list = (List<TestService>) params[2];
+ assertEquals(2, list.size());
+ }
+
+ @Test
+ void b13_getParams_innerClassOuter() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ beanStore.addBean(OuterClass.class, new OuterClass()); // Add
outer class bean for explicit parameter
+ var outer = new OuterClass();
+ // Use the class literal directly instead of getting it from an
instance
+ // For non-static inner classes, the constructor has 3
parameters: (implicit outer, explicit outer, TestService)
+ // getParams only uses the bean for index 0, so index 1 will be
resolved from bean store
+ var classInfo = ClassInfo.of(OuterClass.InnerClass.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 3 &&
x.getParameter(2).isType(TestService.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found");
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, outer);
+ assertEquals(3, params.length);
+ assertSame(outer, params[0]); // Implicit outer instance (from
bean parameter)
+ assertNotNull(params[1]); // Explicit outer parameter (from
bean store)
+ assertNotNull(params[2]); // Service from bean store
+ }
+
+
//====================================================================================================
+ // hasAllParams
+
//====================================================================================================
+
+ @Test
+ void c01_hasAllParams_allAvailable() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertTrue(utils.hasAllParams(constructor, beanStore, null));
+ }
+
+ @Test
+ void c02_hasAllParams_missing() throws Exception {
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertFalse(utils.hasAllParams(constructor, beanStore, null));
+ }
+
+ @Test
+ void c03_hasAllParams_optionalSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass3.class).getPublicConstructor(x ->
x.hasParameterTypes(Optional.class)).get();
+ assertTrue(utils.hasAllParams(constructor, beanStore, null));
// Optional is skipped
+ }
+
+ @Test
+ void c04_hasAllParams_collectionsSkipped() throws Exception {
+ var constructor =
ClassInfo.of(TestClass5.class).getPublicConstructor(x ->
x.hasParameterTypes(List.class)).get();
+ assertTrue(utils.hasAllParams(constructor, beanStore, null));
// Collections are skipped
+ }
+
+ @Test
+ void c05_hasAllParams_namedBeanFound() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"),
"service1");
+ var constructor =
ClassInfo.of(TestClass2.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertTrue(utils.hasAllParams(constructor, beanStore, null));
+ }
+
+ @Test
+ void c06_hasAllParams_namedBeanMissing() throws Exception {
+ var constructor =
ClassInfo.of(TestClass2.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertFalse(utils.hasAllParams(constructor, beanStore, null));
+ }
+
+
//====================================================================================================
+ // invoke
+
//====================================================================================================
+
+ @Test
+ void d01_invoke_constructor() throws Exception {
+ var service = new TestService("test1");
+ beanStore.addBean(TestService.class, service);
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.invoke(constructor, beanStore, null);
+ assertNotNull(result);
+ assertTrue(result instanceof TestClass1);
+ }
+
+ @Test
+ void d02_invoke_method() throws Exception {
+ var service = new TestService("test1");
+ beanStore.addBean(TestService.class, service);
+ var instance = new TestMethodClass();
+ var method =
ClassInfo.of(TestMethodClass.class).getPublicMethod(x -> x.hasName("method1")
&& x.hasParameterTypes(TestService.class)).get();
+ utils.invoke(method, beanStore, instance);
+ // Method should execute without exception
+ }
+
+ @Test
+ void d03_invoke_staticMethod() throws Exception {
+ var service = new TestService("test1");
+ beanStore.addBean(TestService.class, service);
+ var method =
ClassInfo.of(TestMethodClass.class).getPublicMethod(x ->
x.hasName("staticMethod") && x.hasParameterTypes(TestService.class)).get();
+ utils.invoke(method, beanStore, null); // null for static
methods
+ // Method should execute without exception
+ }
+
+ @Test
+ void d04_invoke_constructorWithCollections() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ var constructor =
ClassInfo.of(TestClass5.class).getPublicConstructor(x ->
x.hasParameterTypes(List.class)).get();
+ var result = utils.invoke(constructor, beanStore, null);
+ assertNotNull(result);
+ assertTrue(result instanceof TestClass5);
+ }
+
+ @Test
+ void d05_invoke_innerClassConstructor() throws Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ beanStore.addBean(OuterClass.class, new OuterClass()); // Add
outer class bean for explicit parameter
+ var outer = new OuterClass();
+ // Use the class literal directly instead of getting it from an
instance
+ // For non-static inner classes, the constructor has 3
parameters: (implicit outer, explicit outer, TestService)
+ var classInfo = ClassInfo.of(OuterClass.InnerClass.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 3 &&
x.getParameter(2).isType(TestService.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found");
+ var constructor = constructorOpt.get();
+ constructor.accessible(); // Make constructor accessible
+ var result = utils.invoke(constructor, beanStore, outer);
+ assertNotNull(result);
+ assertTrue(result.getClass().getName().contains("InnerClass"));
+ }
+
+
//====================================================================================================
+ // Additional coverage tests
+
//====================================================================================================
+
+ // Test line 113: getMissingParams - when bean is null (covers nn(bean)
== false branch)
+ @Test
+ void e01_getMissingParams_beanNull() throws Exception {
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
null);
+ assertEquals("TestService", result); // Should check bean store
since bean is null
+ }
+
+ // Test line 113: getMissingParams - when first parameter doesn't match
bean type
+ @Test
+ void e02_getMissingParams_firstParamDoesNotMatchBean() throws Exception
{
+ var wrongBean = new AnotherService(42);
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
wrongBean);
+ assertEquals("TestService", result); // Should check bean store
since types don't match
+ }
+
+ // Test line 173: getParams - Optional collection type
+ @Test
+ void e03_getParams_optionalCollection() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ // Create a class with Optional<List<TestService>> parameter
+ class TestClassWithOptionalList {
+ @SuppressWarnings("unused")
+ public
TestClassWithOptionalList(Optional<List<TestService>> services) {}
+ }
+ // Find constructor by checking parameter count and that second
param (index 1) is Optional
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithOptionalList.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().is(Optional.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Optional
parameter
+ assertTrue(params[1] instanceof Optional);
+ var opt = (Optional<List<TestService>>) params[1];
+ assertTrue(opt.isPresent());
+ var list = opt.get();
+ assertEquals(2, list.size());
+ }
+
+ // Test line 247: hasAllParams - when bean is null
+ @Test
+ void e04_hasAllParams_beanNull() throws Exception {
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertFalse(utils.hasAllParams(constructor, beanStore, null));
// Should check bean store
+ }
+
+ // Test line 247: hasAllParams - when first parameter doesn't match
bean type
+ @Test
+ void e05_hasAllParams_firstParamDoesNotMatchBean() throws Exception {
+ var wrongBean = new AnotherService(42);
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertFalse(utils.hasAllParams(constructor, beanStore,
wrongBean)); // Should check bean store
+ }
+
+ // Test line 247: hasAllParams - when first parameter matches bean
(covers continue loop)
+ @Test
+ void e07_hasAllParams_firstParamMatchesBean() throws Exception {
+ var bean = new TestService("test1");
+ // Constructor with TestService as first parameter - should use
bean, not check store
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ assertTrue(utils.hasAllParams(constructor, beanStore, bean));
// First param satisfied by bean, no store check needed
+ }
+
+ // Test line 247: hasAllParams - when first parameter matches bean but
second parameter is missing
+ @Test
+ void e08_hasAllParams_firstParamMatchesBeanButSecondMissing() throws
Exception {
+ var bean = new TestService("test1");
+ // Constructor with TestService (first) and AnotherService
(second) - first satisfied by bean, second missing
+ var constructor =
ClassInfo.of(TestClass8.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class, AnotherService.class)).get();
+ assertFalse(utils.hasAllParams(constructor, beanStore, bean));
// First param satisfied by bean, but second is missing
+ }
+
+
+ // Test line 113: getMissingParams - when i == 0 but bean doesn't match
type (covers pt.isInstance(bean) == false when i == 0)
+ @Test
+ void e06_getMissingParams_firstParamWithWrongBeanType() throws
Exception {
+ var wrongBean = new AnotherService(42);
+ // Use a constructor where first param is TestService but we
pass AnotherService
+ var constructor =
ClassInfo.of(TestClass1.class).getPublicConstructor(x ->
x.hasParameterTypes(TestService.class)).get();
+ var result = utils.getMissingParams(constructor, beanStore,
wrongBean);
+ assertEquals("TestService", result); // Should check bean store
since types don't match
+ }
+
+ // Test line 173: getParams - Optional collection (ensure the
Optional.of branch is taken)
+ @Test
+ void e07_getParams_optionalArray() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1);
+ beanStore.addBean(TestService.class, service2, "service2");
+ // Create a class with Optional<TestService[]> parameter
+ class TestClassWithOptionalArray {
+ @SuppressWarnings("unused")
+ public
TestClassWithOptionalArray(Optional<TestService[]> services) {}
+ }
+ // Find constructor by checking parameter count and that second
param (index 1) is Optional
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithOptionalArray.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().is(Optional.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Optional
parameter
+ assertTrue(params[1] instanceof Optional);
+ var opt = (Optional<TestService[]>) params[1];
+ assertTrue(opt.isPresent());
+ var array = opt.get();
+ assertEquals(2, array.length);
+ }
+
+ // Test line 311, 313, 315: getElementType - List with raw type (no
generic parameters)
+ // This tests when parameterizedType is not a ParameterizedType, or
when typeArgs is empty
+ @Test
+ void e08_getParams_rawList() throws Exception {
+ // Raw List type (no generic parameters) - this will cause
getElementType to return null
+ // which means getCollectionValue will return null, so it will
fall back to single bean lookup
+ class TestClassWithRawList {
+ @SuppressWarnings("unused")
+ public
TestClassWithRawList(@SuppressWarnings("rawtypes") List services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithRawList.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(List.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + List parameter
+ // Since getElementType returns null for raw List, it won't be
treated as a collection
+ // and will try to look up a bean of type List, which won't be
found
+ assertNull(params[1]);
+ }
+
+ // Test line 322, 324, 327: getElementType - Map with wrong key type or
non-Class value type
+ @Test
+ void e09_getParams_mapWithWrongKeyType() throws Exception {
+ // Map with Integer key instead of String - should return null
from getElementType
+ class TestClassWithWrongMap {
+ @SuppressWarnings("unused")
+ public TestClassWithWrongMap(Map<Integer, TestService>
services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithWrongMap.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(Map.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Map parameter
+ // Since key type is not String, getElementType returns null
+ assertNull(params[1]);
+ }
+
+ // Test line 322, 324, 327: getElementType - Map with non-Class value
type
+ @Test
+ void e10_getParams_mapWithNonClassValueType() throws Exception {
+ // Map with generic value type (not a Class) - should return
null from getElementType
+ class TestClassWithGenericMap {
+ @SuppressWarnings("unused")
+ public TestClassWithGenericMap(Map<String,
List<TestService>> services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithGenericMap.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(Map.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Map parameter
+ // Since value type is List<TestService> (ParameterizedType,
not Class), getElementType returns null
+ assertNull(params[1]);
+ }
+
+ // Test line 324: getElementType - Map with raw type (no generic
parameters)
+ @Test
+ void e11_getParams_rawMap() throws Exception {
+ // Raw Map type (no generic parameters) - this will cause
getElementType to return null
+ class TestClassWithRawMap {
+ @SuppressWarnings("unused")
+ public
TestClassWithRawMap(@SuppressWarnings("rawtypes") Map services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithRawMap.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(Map.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Map parameter
+ // Since getElementType returns null for raw Map, it won't be
treated as a collection
+ // and will try to look up a bean of type Map, which won't be
found
+ assertNull(params[1]);
+ }
+
+ // Test line 333: getElementType - return null for unsupported types
+ @Test
+ void e13_getParams_unsupportedCollectionType() throws Exception {
+ // Use a type that's not List, Set, Map, or array - should
return null
+ // This tests the fallback return null at line 333
+ class TestClassWithCollection {
+ @SuppressWarnings("unused")
+ public TestClassWithCollection(Collection<TestService>
services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithCollection.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(java.util.Collection.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Collection
parameter
+ // Collection is not List/Set/Map, so isCollectionType returns
false, so getCollectionValue returns null
+ // Then it tries to look up a bean of type Collection, which
won't be found
+ assertNull(params[1]);
+ }
+
+ // Test line 324: getElementType - Optional<SomeClass> where SomeClass
is not a ParameterizedType
+ // This tests the case where we have Optional<List> (raw type) or
Optional<SomeClass> (not a collection)
+ // but pt is a collection type. This is a rare edge case.
+ @Test
+ void e09_getElementType_optionalWithNonParameterizedType() throws
Exception {
+ beanStore.addBean(TestService.class, new TestService("test1"));
+ // Create a class with Optional<List> (raw type, not
ParameterizedType)
+ class TestClassWithOptionalRawList {
+ @SuppressWarnings({"unused", "rawtypes"})
+ public TestClassWithOptionalRawList(Optional<List>
services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo =
ClassInfo.of(TestClassWithOptionalRawList.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2) // Outer class
+ Optional<List> parameter
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Optional<List>
parameter
+ // Optional<List> (raw) - after unwrapping Optional, we get
List (raw)
+ // isCollectionType(List.class) returns true, so
getCollectionValue is called
+ // getElementType is called with pt=List.class and
parameterizedType=Optional<List> (raw)
+ // When unwrapping Optional, typeArgs[0] is List (raw), which
is a Class, not ParameterizedType
+ // So line 324 is executed: return null
+ // Then getCollectionValue returns null because elementType is
null
+ // Then it tries to look up a bean of type List, which won't be
found
+ // Since the parameter is Optional<List>, it wraps the result
in Optional.empty()
+ assertTrue(params[1] instanceof Optional);
+ var opt = (Optional<?>) params[1];
+ assertFalse(opt.isPresent()); // Should be Optional.empty()
+ }
+
+ // Test Optional<Map<String, TestService>> - covers Optional unwrapping
in getCollectionValue (lines 394-403)
+ // Note: Line 401 (return null when Optional<SomeClass> where SomeClass
is not ParameterizedType)
+ // appears to be unreachable in practice because:
+ // - If pt is Map.class (raw), getElementType returns null and we
return early (line 383)
+ // - If pt is Map<String,T> (parameterized), typeArgs[0] is a
ParameterizedType, so line 399 executes, not 401
+ // However, the code is kept as a defensive check. This test verifies
the normal Optional<Map<String,T>> case.
+ @Test
+ void e10_getCollectionValue_optionalMap() throws Exception {
+ var service1 = new TestService("test1");
+ var service2 = new TestService("test2");
+ beanStore.addBean(TestService.class, service1, "service1");
+ beanStore.addBean(TestService.class, service2, "service2");
+ class TestClassWithOptionalMap {
+ @SuppressWarnings("unused")
+ public TestClassWithOptionalMap(Optional<Map<String,
TestService>> services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithOptionalMap.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2) // Outer class
+ Optional<Map<String, TestService>> parameter
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Optional<Map>
parameter
+ // This should work normally: Optional unwrapping happens, then
Map is populated
+ assertTrue(params[1] instanceof Optional);
+ var opt = (Optional<Map<String, TestService>>) params[1];
+ assertTrue(opt.isPresent());
+ var map = opt.get();
+ assertEquals(2, map.size());
+ assertSame(service1, map.get("service1"));
+ assertSame(service2, map.get("service2"));
+ }
+
+ // Test line 416: getCollectionValue - Map where parameterizedType is
not ParameterizedType after Optional unwrapping
+ // Note: Line 416 appears to be unreachable in practice because:
+ // - If getElementType returns null (e.g., Map<Integer,T> or raw Map),
we return early (line 383)
+ // - If getElementType returns non-null (e.g., Map<String,T>), then
parameterizedType is a ParameterizedType
+ // and typeArgs[0] is String.class, so line 413 executes, not 416
+ // However, kept as a defensive check. This test verifies that
Map<Integer,T> returns null early.
+ @Test
+ void e11_getCollectionValue_mapWithNonStringKey() throws Exception {
+ var service1 = new TestService("test1");
+ beanStore.addBean(TestService.class, service1);
+ // Create a class with Map<Integer, TestService> - key is not
String
+ class TestClassWithWrongKeyMap {
+ @SuppressWarnings("unused")
+ public TestClassWithWrongKeyMap(Map<Integer,
TestService> services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithWrongKeyMap.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2) // Outer class
+ Map<Integer, TestService> parameter
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Map parameter
+ // Map<Integer, TestService> - getElementType returns null (key
is not String)
+ // So getCollectionValue returns null early (line 383), never
reaching line 416
+ assertNull(params[1]);
+ }
+
+ // Test line 437: getCollectionValue - fallback return null
+ // Note: Line 437 appears to be unreachable in practice because:
+ // - isCollectionType only returns true for List, Set, Map, or array
+ // - List and Set are handled above (lines 430-434)
+ // - Map is handled earlier (lines 390-416)
+ // - Arrays are handled earlier (lines 422-426)
+ // - If inner() is null, isCollectionType returns false, so we return
early (line 379)
+ // However, kept as defensive code. This test verifies that Collection
types return null early.
+ @Test
+ void e12_getCollectionValue_unsupportedCollectionType() throws
Exception {
+ var service1 = new TestService("test1");
+ beanStore.addBean(TestService.class, service1);
+ // Collection is not List/Set/Map, so isCollectionType returns
false
+ // So getCollectionValue returns null early (line 379), never
reaching line 437
+ // Line 437 appears to be unreachable defensive code
+ // This test verifies that Collection types are not handled
(they return null early)
+ class TestClassWithCollection {
+ @SuppressWarnings("unused")
+ public TestClassWithCollection(Collection<TestService>
services) {}
+ }
+ // Local classes in instance methods are non-static inner
classes, so they have outer class as first param
+ var classInfo = ClassInfo.of(TestClassWithCollection.class);
+ var constructors = classInfo.getDeclaredConstructors();
+ var constructorOpt = constructors.stream()
+ .filter(x -> x.getParameterCount() == 2 &&
x.getParameter(1).getParameterType().inner().equals(java.util.Collection.class))
+ .findFirst();
+ assertTrue(constructorOpt.isPresent(), "Constructor should be
found. Available: " + constructors);
+ var constructor = constructorOpt.get();
+ var params = utils.getParams(constructor, beanStore, null);
+ assertEquals(2, params.length); // Outer class + Collection
parameter
+ // Collection is not List/Set/Map, so isCollectionType returns
false, so getCollectionValue returns null early
+ // Then it tries to look up a bean of type Collection, which
won't be found
+ assertNull(params[1]);
+ }
+}
+
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/mstat/MethodExecStore_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/mstat/MethodExecStore_Test.java
index 10f4df219d..b677d4b0e8 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/mstat/MethodExecStore_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/mstat/MethodExecStore_Test.java
@@ -24,7 +24,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.stats.*;
import org.junit.jupiter.api.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/mstat/ThrownStore_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/mstat/ThrownStore_Test.java
index 9e120d324c..0d95412437 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/mstat/ThrownStore_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/mstat/ThrownStore_Test.java
@@ -25,7 +25,6 @@ import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.stats.*;
import org.junit.jupiter.api.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_Builder_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_Builder_Test.java
index 8f68e29cd7..adbae4da2a 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_Builder_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestContext_Builder_Test.java
@@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.Named;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.client.*;
import org.apache.juneau.rest.config.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
index 2af79e28f0..a42401aab8 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
@@ -27,7 +27,6 @@ import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.http.annotation.Tag;
import org.apache.juneau.json.*;
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
index 2121201d4a..643aa20a75 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
@@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.*;
import org.apache.juneau.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.cp.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.parser.*;