Hi all,
Martin W. and myself were running into a rather weird issue in unit tests with
application composers (in per_jvm) mode only on Mac OSX 26.0.1 - the same tests
are working fine on Linux CI (Ubuntu).
## Context
In our test context we use a @Configeration properties and the application
resource definitions of TomEE [1] via a property like this:
---------
p.put(„myResourceDE", "new://Resource?class-name=my.fqdn.MyResource" +
"&constructor=locale, resourceName, useLowerCase");
p.put("myResourceDE.resourceName",
"classpath:/tlm-common-stopwords_de.properties");
p.put(„myResourceDE.locale", "de");
p.put("myResourceDE.useLowerCase", "true“);
---------
Note: MyResource has multiple ctors with varying arguments, so we have
signatures like (String, String, Boolean OR Locale, String, Boolean OR Locale,
Properties, Boolean)
As you can see, we rely on the „constructor“ argument of the resource syntax to
specify the constructor to be used for creating that resource.
## Problem
On OSX 26.0.1, we’re seeing an intermittent exception:
---------
org.apache.xbean.recipe.ConstructionException: Invalid and non-convertable
constructor parameter type: name=resourceName, index=1,
expected=java.util.Properties, actual=java.lang.String
at
org.apache.xbean.recipe.ObjectRecipe.extractConstructorArgs(ObjectRecipe.java:597)
at org.apache.xbean.recipe.ObjectRecipe.internalCreate(ObjectRecipe.java:278)
at org.apache.xbean.recipe.AbstractRecipe.create(AbstractRecipe.java:96)
at org.apache.xbean.recipe.AbstractRecipe.create(AbstractRecipe.java:61)
at
org.apache.openejb.assembler.classic.Assembler.doCreateResource(Assembler.java:3181)
at
org.apache.openejb.assembler.classic.Assembler.createResource(Assembler.java:3016)
at
org.apache.openejb.assembler.classic.Assembler.buildContainerSystem(Assembler.java:596)
at
org.apache.openejb.testing.ApplicationComposers.startContainer(ApplicationComposers.java:1481)
---------
So I went ahead and did a debugging session. On OSX, the test case sometime
passes and most of the time not (regardless of execution: Maven Surefire vs
IDEA).
The same test passes reliably on Ubuntu.
## Findings
After some debugging, I came up in XBeans ReflectionUtil.findConstructor(…) [2]
This method relies on
typeClass.getConstructors()
typeClass.getDeclaredConstructors()
However, according to the Javadoc [3], the order of constructors returned by
these methods is not deterministic.
As a result, the sorted list (ReflectionUtil#L618f ) can differ between JVMs or
OS environments.
Because TomEE doesn’t provide type arguments to the ObjectRecipe, the code
simply picks the first 3-arg constructor found — leading to the exception above
on OSX.
I wasn’t able to reproduce it in a standalone test case in XBeans or TomEE
(since it relies on the order in which ctors are returned).
## Expectation vs. Behavior
My expectation is that, since we specify parameter names via
constructor=locale,resourceName,useLowerCase,
those names should be used to select the appropriate constructor - even if the
parameter types aren’t specified.
However, in XBean, parameter names are only used if parameter types are also
provided.
And in TomEE, the Assembler doesn’t currently provide type arguments in
Assembler#prepareRecipe(final ServiceInfo info)[4], so we end up in this
situation.
## Question
How is the constructor syntax intended to work when a class provides several
constructors that all have the same number of parameters (and the given
parameter names aren’t used).
Should the syntax perhaps support specifying parameter types, for example:
---------
p.put("myResourceDE", "new://Resource?class-name=my.fqdn.MyResource" +
"&constructor=locale(java.lang.String),resourceName(java.lang.String),useLowerCase(java.lang.Boolean)");
---------
Any insights or background on how this constructor resolution is designed to
behave would be greatly appreciated.
Thanks and Gruß
Richard
[1] https://tomee.apache.org/latest/docs/application-resources.html
[2]
https://github.com/apache/geronimo-xbean/blob/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java#L583
[3]
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getDeclaredConstructors()
[4]
https://github.com/apache/tomee/blob/main/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java#L3739