You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@camel.apache.org by Bob Paulin <bo...@bobpaulin.com> on 2021/01/29 14:50:08 UTC
[Discuss] How to handle UnmarshalType defined as an Array Class in
Java DSL
Hi,
I've submitted a issue [1].
in prior versions you could define an array type as the unmarshalType in
a dataformat in the Java DSL as follows:
from("direct:beginArray").unmarshal().json(JsonLibrary.Jackson,
String[].class).to("mock:endArray");
This now throws an exception see [1].
It appears is due to the code in JsonDataFormatReifier [2] that converts
the unmarshalType into unmarshalTypeName and then the data format (in my
case JacksonDataFormat) tries to resolve the class via the camel
ClassResolver
camelContext.getClassResolver().resolveClass(unmarshalType);
Given this delegates to a classloader loadClass method see [3].
Classloaders can't load array types this way. Also it fails the check
on the original unmarshalType because the original unmarshalType in the
definition is not copied to the created DataFormat object so the check
if (unmarshalTypeName != null && (unmarshalType == null || unmarshalType
== Object.class)) {
does not help here.
If arrays are to be supported I see a couple of options.
1) org.apache.camel.util.ObjectHelper could have a fall back to use
something like the Apache Commons ClassUtil.getClass(). This would also
allow folks using unmarshalTypeName to specify java.lang.String[] and
get a result. However it does make the ClassResolver act less like a
classloader which may be undesirable.
2) Detect that the unmarshalTypeName is an array and resolve the base
class then convert to an array. Array.newInstance(..).getClass() may be
replaced with class.arrayType() in Java 12+. Might be a little ugly but
leaves ObjectHelper alone.
Ex
@Override
protected void doInit() throws Exception {
if (unmarshalTypeName != null && (unmarshalType == null ||
unmarshalType == Object.class)) {
String baseUnmarshalType = unmarshalTypeName;
if (baseUnmarshalType.startsWith("[L")) {
baseUnmarshalType = baseUnmarshalType.substring(2,
baseUnmarshalType.length() - 1);
Class<?> baseUnmarshalTypeClass =
camelContext.getClassResolver().resolveClass(baseUnmarshalType);
unmarshalType =
Array.newInstance(baseUnmarshalTypeClass, 0).getClass();
} else {
unmarshalType =
camelContext.getClassResolver().resolveClass(baseUnmarshalType);
}
......
}
3) Allow the unmarshalType to be passed directly to the newly created
DataFormat. I know these fields are transient currently (and have been
for a long time) but if it were pass the check to do the class
resolution would not need to run and it could just use the unmarshalType
directly.
4) Don't allow array types and throw an exception. Seems a bit
opinionated but better than the null error.
Other options?
Sincerely,
Bob
[1] https://issues.apache.org/jira/browse/CAMEL-16114
[2]
https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/JsonDataFormatReifier.java#L46
[3]
https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java#L474
Re: [Discuss] How to handle UnmarshalType defined as an Array Class
in Java DSL
Posted by Bob Paulin <bo...@bobpaulin.com>.
Hi,
Zoran thanks for the reply. I saw Claus applied a patch over the
weekend that fixed my test but it turns out my test may have been too
simple since String[] has some custom code to help it in the
ObjectHelper. I've attached a case to CAMEL-16114 that uses a POJO that
still fails with the new code. I would also mention with #1 we do not
need to add a dependency if we use similar behavior like Class.forName
to load the array type. But the issue of this not being ClassLoader
like behavior remains so I'm still not sure which path forward makes the
most sense. Happy to help, please let me know if it makes sense to
re-open CAMEL-16114 or if it would would be better to submit a new issue.
- Bob
On 2/1/2021 3:48 AM, Zoran Regvart wrote:
> Hi Bob, Cameleers
> just to note, this got marked as spam in my mail client (for whatever
> reason), so you might have missed this also. Some comments inline:
>
> On Fri, Jan 29, 2021 at 3:50 PM Bob Paulin <bo...@bobpaulin.com> wrote:
>> Hi,
>>
>> I've submitted a issue [1].
>>
>> in prior versions you could define an array type as the unmarshalType in
>> a dataformat in the Java DSL as follows:
>>
>> from("direct:beginArray").unmarshal().json(JsonLibrary.Jackson,
>> String[].class).to("mock:endArray");
>>
>> This now throws an exception see [1].
>>
>>
>> It appears is due to the code in JsonDataFormatReifier [2] that converts
>> the unmarshalType into unmarshalTypeName and then the data format (in my
>> case JacksonDataFormat) tries to resolve the class via the camel
>> ClassResolver
>>
>> camelContext.getClassResolver().resolveClass(unmarshalType);
>>
>> Given this delegates to a classloader loadClass method see [3].
>> Classloaders can't load array types this way. Also it fails the check
>> on the original unmarshalType because the original unmarshalType in the
>> definition is not copied to the created DataFormat object so the check
>>
>> if (unmarshalTypeName != null && (unmarshalType == null || unmarshalType
>> == Object.class)) {
>>
>> does not help here.
>>
>>
>> If arrays are to be supported I see a couple of options.
>>
>> 1) org.apache.camel.util.ObjectHelper could have a fall back to use
>> something like the Apache Commons ClassUtil.getClass(). This would also
>> allow folks using unmarshalTypeName to specify java.lang.String[] and
>> get a result. However it does make the ClassResolver act less like a
>> classloader which may be undesirable.
> In addition to that, with the efforts on making Camel core lighter, I
> don't think we want to add a new dependency to `camel-util`.
>
>> 2) Detect that the unmarshalTypeName is an array and resolve the base
>> class then convert to an array. Array.newInstance(..).getClass() may be
>> replaced with class.arrayType() in Java 12+. Might be a little ugly but
>> leaves ObjectHelper alone.
>>
>> Ex
>> @Override
>> protected void doInit() throws Exception {
>> if (unmarshalTypeName != null && (unmarshalType == null ||
>> unmarshalType == Object.class)) {
>>
>> String baseUnmarshalType = unmarshalTypeName;
>> if (baseUnmarshalType.startsWith("[L")) {
>> baseUnmarshalType = baseUnmarshalType.substring(2,
>> baseUnmarshalType.length() - 1);
>> Class<?> baseUnmarshalTypeClass =
>> camelContext.getClassResolver().resolveClass(baseUnmarshalType);
>> unmarshalType =
>> Array.newInstance(baseUnmarshalTypeClass, 0).getClass();
>> } else {
>> unmarshalType =
>> camelContext.getClassResolver().resolveClass(baseUnmarshalType);
>> }
>>
>> ......
>> }
>>
>> 3) Allow the unmarshalType to be passed directly to the newly created
>> DataFormat. I know these fields are transient currently (and have been
>> for a long time) but if it were pass the check to do the class
>> resolution would not need to run and it could just use the unmarshalType
>> directly.
> This looks like the best option to me, we should not be loosing type
> information.
>
>> 4) Don't allow array types and throw an exception. Seems a bit
>> opinionated but better than the null error.
>>
>>
>> Other options?
>>
>> Sincerely,
>>
>> Bob
>>
>> [1] https://issues.apache.org/jira/browse/CAMEL-16114
>> [2]
>> https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/JsonDataFormatReifier.java#L46
>> [3]
>> https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java#L474
>
>
Re: [Discuss] How to handle UnmarshalType defined as an Array Class
in Java DSL
Posted by Zoran Regvart <zo...@regvart.com>.
Hi Bob, Cameleers
just to note, this got marked as spam in my mail client (for whatever
reason), so you might have missed this also. Some comments inline:
On Fri, Jan 29, 2021 at 3:50 PM Bob Paulin <bo...@bobpaulin.com> wrote:
>
> Hi,
>
> I've submitted a issue [1].
>
> in prior versions you could define an array type as the unmarshalType in
> a dataformat in the Java DSL as follows:
>
> from("direct:beginArray").unmarshal().json(JsonLibrary.Jackson,
> String[].class).to("mock:endArray");
>
> This now throws an exception see [1].
>
>
> It appears is due to the code in JsonDataFormatReifier [2] that converts
> the unmarshalType into unmarshalTypeName and then the data format (in my
> case JacksonDataFormat) tries to resolve the class via the camel
> ClassResolver
>
> camelContext.getClassResolver().resolveClass(unmarshalType);
>
> Given this delegates to a classloader loadClass method see [3].
> Classloaders can't load array types this way. Also it fails the check
> on the original unmarshalType because the original unmarshalType in the
> definition is not copied to the created DataFormat object so the check
>
> if (unmarshalTypeName != null && (unmarshalType == null || unmarshalType
> == Object.class)) {
>
> does not help here.
>
>
> If arrays are to be supported I see a couple of options.
>
> 1) org.apache.camel.util.ObjectHelper could have a fall back to use
> something like the Apache Commons ClassUtil.getClass(). This would also
> allow folks using unmarshalTypeName to specify java.lang.String[] and
> get a result. However it does make the ClassResolver act less like a
> classloader which may be undesirable.
In addition to that, with the efforts on making Camel core lighter, I
don't think we want to add a new dependency to `camel-util`.
> 2) Detect that the unmarshalTypeName is an array and resolve the base
> class then convert to an array. Array.newInstance(..).getClass() may be
> replaced with class.arrayType() in Java 12+. Might be a little ugly but
> leaves ObjectHelper alone.
>
> Ex
> @Override
> protected void doInit() throws Exception {
> if (unmarshalTypeName != null && (unmarshalType == null ||
> unmarshalType == Object.class)) {
>
> String baseUnmarshalType = unmarshalTypeName;
> if (baseUnmarshalType.startsWith("[L")) {
> baseUnmarshalType = baseUnmarshalType.substring(2,
> baseUnmarshalType.length() - 1);
> Class<?> baseUnmarshalTypeClass =
> camelContext.getClassResolver().resolveClass(baseUnmarshalType);
> unmarshalType =
> Array.newInstance(baseUnmarshalTypeClass, 0).getClass();
> } else {
> unmarshalType =
> camelContext.getClassResolver().resolveClass(baseUnmarshalType);
> }
>
> ......
> }
>
> 3) Allow the unmarshalType to be passed directly to the newly created
> DataFormat. I know these fields are transient currently (and have been
> for a long time) but if it were pass the check to do the class
> resolution would not need to run and it could just use the unmarshalType
> directly.
This looks like the best option to me, we should not be loosing type
information.
>
> 4) Don't allow array types and throw an exception. Seems a bit
> opinionated but better than the null error.
>
>
> Other options?
>
> Sincerely,
>
> Bob
>
> [1] https://issues.apache.org/jira/browse/CAMEL-16114
> [2]
> https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/JsonDataFormatReifier.java#L46
> [3]
> https://github.com/apache/camel/blob/7a58c2b0f9612c205ddbafbe30a8487f46021d14/core/camel-util/src/main/java/org/apache/camel/util/ObjectHelper.java#L474
--
Zoran Regvart