This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26124-jt400-pgm-npe in repository https://gitbox.apache.org/repos/asf/camel.git
commit f1239bc5002dfc6f75317ca2885494e7f1e00c72 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Nov 2 16:34:57 2026 +0100 CAMEL-26124: camel-jt400 - Fix program call without outputFieldsIdx and with a List body A program call without outputFieldsIdx failed with a NullPointerException, because isFieldIdxForOuput did a binary search on a null array. A missing outputFieldsIdx now means no output fields (all parameters are input). The producer cast the body to Object[], so a List body failed with a ClassCastException. It now uses getMandatoryBody(Object[].class): a List is converted, and other bodies fail with an InvalidPayloadException. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../apache/camel/catalog/docs/jt400-component.adoc | 6 +- .../camel-jt400/src/main/docs/jt400-component.adoc | 6 +- .../camel/component/jt400/Jt400Endpoint.java | 4 +- .../camel/component/jt400/Jt400PgmProducer.java | 10 ++- .../component/jt400/Jt400PgmProducerTest.java | 84 ++++++++++++++++++++++ 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jt400-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jt400-component.adoc index 047b2ba226e7..0ae32efb8cee 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jt400-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jt400-component.adoc @@ -93,12 +93,14 @@ on the endpoint. === Program call -This endpoint expects the input to be an `Object[]`, whose object types are +This endpoint expects the input to be an `Object[]` (a `List` is converted to an array), whose object types are `int`, `long`, `CharSequence` (such as `String`), or `byte[]`. All other data types in the input array will be converted to `String`. For character inputs, CCSID handling is performed through the native jt400 library mechanisms. A parameter can be _omitted_ by passing null as the value in -its position (the program has to support it). After the program execution, +its position (the program has to support it). The `outputFieldsIdx` option lists the +output parameters; when it is not set, all parameters are input-only. +After the program execution, the endpoint returns an `Object[]` in the message body. Depending on _format_, the returned array will be populated with `byte[]` or `String` objects representing the values as they were returned by the program. diff --git a/components/camel-jt400/src/main/docs/jt400-component.adoc b/components/camel-jt400/src/main/docs/jt400-component.adoc index 047b2ba226e7..0ae32efb8cee 100644 --- a/components/camel-jt400/src/main/docs/jt400-component.adoc +++ b/components/camel-jt400/src/main/docs/jt400-component.adoc @@ -93,12 +93,14 @@ on the endpoint. === Program call -This endpoint expects the input to be an `Object[]`, whose object types are +This endpoint expects the input to be an `Object[]` (a `List` is converted to an array), whose object types are `int`, `long`, `CharSequence` (such as `String`), or `byte[]`. All other data types in the input array will be converted to `String`. For character inputs, CCSID handling is performed through the native jt400 library mechanisms. A parameter can be _omitted_ by passing null as the value in -its position (the program has to support it). After the program execution, +its position (the program has to support it). The `outputFieldsIdx` option lists the +output parameters; when it is not set, all parameters are input-only. +After the program execution, the endpoint returns an `Object[]` in the message body. Depending on _format_, the returned array will be populated with `byte[]` or `String` objects representing the values as they were returned by the program. diff --git a/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400Endpoint.java b/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400Endpoint.java index 45a58baee4d2..d7d34c4d16e1 100644 --- a/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400Endpoint.java +++ b/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400Endpoint.java @@ -266,7 +266,9 @@ public class Jt400Endpoint extends ScheduledPollEndpoint implements MultipleCons } public boolean isFieldIdxForOuput(int idx) { - return Arrays.binarySearch(getOutputFieldsIdxArray(), idx) >= 0; + Integer[] outputFieldsIdxArray = getOutputFieldsIdxArray(); + // no outputFieldsIdx configured means no output fields + return outputFieldsIdxArray != null && Arrays.binarySearch(outputFieldsIdxArray, idx) >= 0; } public int getOutputFieldLength(int idx) { diff --git a/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400PgmProducer.java b/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400PgmProducer.java index bd9a02f888aa..cd300f26a7b5 100644 --- a/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400PgmProducer.java +++ b/components/camel-jt400/src/main/java/org/apache/camel/component/jt400/Jt400PgmProducer.java @@ -99,12 +99,11 @@ public class Jt400PgmProducer extends DefaultProducer { } } - private ProgramParameter[] getParameterList(Exchange exchange, AS400 iSeries) + ProgramParameter[] getParameterList(Exchange exchange, AS400 iSeries) throws InvalidPayloadException, PropertyVetoException { - Object body = exchange.getIn().getMandatoryBody(); - - Object[] params = (Object[]) body; + // the body must be an array of parameters (a List is converted to an array) + Object[] params = exchange.getIn().getMandatoryBody(Object[].class); ProgramParameter[] parameterList = new ProgramParameter[params.length]; for (int i = 0; i < params.length; i++) { @@ -178,8 +177,7 @@ public class Jt400PgmProducer extends DefaultProducer { private void handlePGMOutput(Exchange exchange, ProgramCall pgmCall, AS400 iSeries) throws InvalidPayloadException { - Object body = exchange.getIn().getMandatoryBody(); - Object[] params = (Object[]) body; + Object[] params = exchange.getIn().getMandatoryBody(Object[].class); List<Object> results = new ArrayList<>(); diff --git a/components/camel-jt400/src/test/java/org/apache/camel/component/jt400/Jt400PgmProducerTest.java b/components/camel-jt400/src/test/java/org/apache/camel/component/jt400/Jt400PgmProducerTest.java new file mode 100644 index 000000000000..8e96aca14d0d --- /dev/null +++ b/components/camel-jt400/src/test/java/org/apache/camel/component/jt400/Jt400PgmProducerTest.java @@ -0,0 +1,84 @@ +/* + * 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.camel.component.jt400; + +import java.util.List; + +import com.ibm.as400.access.AS400; +import com.ibm.as400.access.ProgramParameter; +import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.support.DefaultExchange; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Builds the program parameter list of {@link Jt400PgmProducer} without connecting to an IBM i system. + */ +public class Jt400PgmProducerTest extends Jt400TestSupport { + + private static final String PGM_URI = "jt400://USER:password@host/qsys.lib/library.lib/prog.pgm" + + "?connectionPool=#mockPool&fieldsLength=4,8"; + + private final AS400 iSeries = new AS400("host", "USER", "password"); + + @Test + public void testNoOutputFieldsIdxMeansInputOnly() throws Exception { + Jt400Endpoint endpoint = context.getEndpoint(PGM_URI, Jt400Endpoint.class); + assertNull(endpoint.getOutputFieldsIdxArray()); + assertFalse(endpoint.isFieldIdxForOuput(0)); + + ProgramParameter[] parameters = createParameterList(endpoint, new Object[] { 1, 2L }); + + assertEquals(2, parameters.length); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, parameters[0].getInputData()); + assertEquals(0, parameters[0].getOutputDataLength()); + assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 2 }, parameters[1].getInputData()); + assertEquals(0, parameters[1].getOutputDataLength()); + } + + @Test + public void testListBody() throws Exception { + Jt400Endpoint endpoint = context.getEndpoint(PGM_URI + "&outputFieldsIdx=1", Jt400Endpoint.class); + + ProgramParameter[] parameters = createParameterList(endpoint, List.of(1, new byte[0])); + + assertEquals(2, parameters.length); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, parameters[0].getInputData()); + assertEquals(0, parameters[0].getOutputDataLength()); + assertEquals(8, parameters[1].getOutputDataLength()); + } + + @Test + public void testInvalidBody() { + Jt400Endpoint endpoint = context.getEndpoint(PGM_URI, Jt400Endpoint.class); + + assertThrows(InvalidPayloadException.class, () -> createParameterList(endpoint, new Object())); + } + + private ProgramParameter[] createParameterList(Jt400Endpoint endpoint, Object body) throws Exception { + Exchange exchange = new DefaultExchange(context); + exchange.getIn().setBody(body); + return new Jt400PgmProducer(endpoint).getParameterList(exchange, iSeries); + } + +}
