diff --git a/component-api/src/main/java/org/talend/sdk/component/api/exception/ComponentException.java b/component-api/src/main/java/org/talend/sdk/component/api/exception/ComponentException.java index 3cf1bd5b4f6dd..d85358972b845 100644 --- a/component-api/src/main/java/org/talend/sdk/component/api/exception/ComponentException.java +++ b/component-api/src/main/java/org/talend/sdk/component/api/exception/ComponentException.java @@ -36,7 +36,8 @@ public static enum ErrorOrigin { public ComponentException(final ErrorOrigin errorOrigin, final String type, final String message, final StackTraceElement[] stackTrace, final Throwable cause) { - super((type != null ? "(" + type + ") " : "") + message, cause); + super((type != null ? "(" + type + ") " : "") + message); + this.initCause(toGenericThrowable(cause)); this.errorOrigin = errorOrigin; originalType = type; originalMessage = message; @@ -70,4 +71,35 @@ public ComponentException(final Throwable cause) { this(ErrorOrigin.UNKNOWN, cause.getMessage(), cause); } + /** + * Convert all exception stack to generic throwable stack to avoid unknown exception at deserialization time.. + * + * @param t + * @return An Throwable. + */ + protected Throwable toGenericThrowable(final Throwable t) { + if (t == null) { + return null; + } + if (t instanceof ComponentException) { + return t; + } + Throwable generic = new Throwable(String.format("(%s) : %s", t.getClass().getName(), t.getMessage())); + generic.setStackTrace(t.getStackTrace()); + + Throwable cause = t.getCause(); + Throwable genericCause = null; + if (cause != null) { + genericCause = toGenericThrowable(cause); + } else { + return generic; + } + + if (genericCause != null) { + generic = new Throwable(generic.getMessage(), genericCause); + } + + return generic; + } + } diff --git a/component-api/src/test/java/org/talend/sdk/component/api/exception/ComponentExceptionTest.java b/component-api/src/test/java/org/talend/sdk/component/api/exception/ComponentExceptionTest.java new file mode 100644 index 0000000000000..b7cfbfc1ad279 --- /dev/null +++ b/component-api/src/test/java/org/talend/sdk/component/api/exception/ComponentExceptionTest.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2006-2024 Talend Inc. - www.talend.com + * + * Licensed 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.talend.sdk.component.api.exception; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ComponentExceptionTest { + + @Test + void testTypedCauseExceptionTransformation() { + IndexOutOfBoundsException indexOutOfBoundsException = new IndexOutOfBoundsException("AAA"); + IllegalArgumentException illegalArgumentException = + new IllegalArgumentException("BBB", indexOutOfBoundsException); + IllegalStateException illegalStateException = new IllegalStateException("CCC", illegalArgumentException); + + ComponentException componentException = new ComponentException("DDD", illegalStateException); + + Throwable ccc = componentException.getCause(); + Assertions.assertEquals(Throwable.class, ccc.getClass()); + + Throwable bbb = ccc.getCause(); + Assertions.assertEquals(Throwable.class, bbb.getClass()); + + Throwable aaa = bbb.getCause(); + Assertions.assertEquals(Throwable.class, aaa.getClass()); + } + + @Test + void testConstructor() { + ComponentException componentException = new ComponentException("message"); + Assertions.assertNull(componentException.getCause()); + } + + @Test + void testParameters() { + ComponentException componentException = new ComponentException(new IndexOutOfBoundsException("cause")); + Assertions.assertNotNull(componentException.getCause()); + Assertions.assertEquals("java.lang.IndexOutOfBoundsException", componentException.getOriginalType()); + Assertions.assertEquals(ComponentException.ErrorOrigin.UNKNOWN, componentException.getErrorOrigin()); + + componentException = new ComponentException(ComponentException.ErrorOrigin.USER, "message"); + Assertions.assertEquals(ComponentException.ErrorOrigin.USER, componentException.getErrorOrigin()); + } + +} \ No newline at end of file diff --git a/sample-parent/sample-connector/pom.xml b/sample-parent/sample-connector/pom.xml index 7f7d7b811ffcd..cc0626b840eb6 100644 --- a/sample-parent/sample-connector/pom.xml +++ b/sample-parent/sample-connector/pom.xml @@ -55,6 +55,12 @@ ${project.version} provided + + org.talend.sdk.component + component-runtime-impl + 1.65.0M6-SNAPSHOT + compile + diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/config/InputConfig.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/config/InputConfig.java index 84542fbd48bdd..d6c69056c5859 100644 --- a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/config/InputConfig.java +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/config/InputConfig.java @@ -19,13 +19,18 @@ import java.time.ZonedDateTime; import org.talend.sdk.component.api.configuration.Option; +import org.talend.sdk.component.api.configuration.ui.DefaultValue; import org.talend.sdk.component.api.configuration.ui.layout.GridLayout; import org.talend.sdk.component.api.configuration.ui.widget.DateTime; import org.talend.sdk.component.api.meta.Documentation; +import lombok.Data; + +@Data @GridLayout({ @GridLayout.Row({ "date" }), - @GridLayout.Row({ "dataset" }) }) + @GridLayout.Row({ "dataset" }), + @GridLayout.Row({ "generateException" }) }) @GridLayout( names = GridLayout.FormType.ADVANCED, value = { @@ -43,4 +48,13 @@ public class InputConfig implements Serializable { @Documentation("Doc: default date documentation without Internationalization.") private ZonedDateTime date; + @Option + @Documentation("Doc: Use the generate exception service or not.") + @DefaultValue("false") + private Boolean generateException = false; + + @Option + @Documentation("Doc: Use the generate runtime exception service or not.") + @DefaultValue("false") + private Boolean generateRuntimeException = false; } diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheInput1.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheInput1.java index 2cbfacae7cbba..afea1fb3c7242 100644 --- a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheInput1.java +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheInput1.java @@ -26,6 +26,7 @@ import org.talend.sdk.component.api.input.Producer; import org.talend.sdk.component.api.meta.Documentation; import org.talend.sdk.component.test.connectors.config.InputConfig; +import org.talend.sdk.component.test.connectors.service.GenerateExceptionServices; @Icon(value = Icon.IconType.CUSTOM, custom = "input") @Documentation("Doc: default TheInput1 documentation without Internationalization.") @@ -41,12 +42,19 @@ public class TheInput1 implements Serializable { InputConfig config; - public TheInput1(final @Option("configin") InputConfig config) { + private final GenerateExceptionServices exceptionServices; + + public TheInput1(final @Option("configin") InputConfig config, + final GenerateExceptionServices exceptionServices) { this.config = config; + this.exceptionServices = exceptionServices; } @PostConstruct public void init() { + if (config.getGenerateException()) { + exceptionServices.generateException(); + } } @PreDestroy @@ -55,7 +63,9 @@ public void release() { @Producer public Object next() { - + if (config.getGenerateRuntimeException()) { + exceptionServices.generateRuntimeException(); + } return LocalDate.now(); } diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheMapper1.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheMapper1.java index 4c07ce0a573da..95ab2560f9de8 100644 --- a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheMapper1.java +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/input/TheMapper1.java @@ -30,6 +30,7 @@ import org.talend.sdk.component.api.meta.Documentation; import org.talend.sdk.component.test.connectors.config.InputConfig; import org.talend.sdk.component.test.connectors.migration.AbstractMigrationHandler; +import org.talend.sdk.component.test.connectors.service.GenerateExceptionServices; @Version(value = InputConfig.INPUT_CONFIG_VERSION, migrationHandler = AbstractMigrationHandler.InputMigrationHandler.class) @@ -47,9 +48,12 @@ public class TheMapper1 implements Serializable { private InputConfig config; - public TheMapper1(final @Option("configin") InputConfig config) { + private final GenerateExceptionServices exceptionServices; + public TheMapper1(final @Option("configin") InputConfig config, + final GenerateExceptionServices exceptionServices) { this.config = config; + this.exceptionServices = exceptionServices; } @Assessor @@ -66,7 +70,7 @@ public List split(@PartitionSize final int desiredNbSplits) { @Emitter public TheInput1 createSource() { - return new TheInput1(this.config); + return new TheInput1(this.config, this.exceptionServices); } } diff --git a/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/service/GenerateExceptionServices.java b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/service/GenerateExceptionServices.java new file mode 100644 index 0000000000000..99fe6d646757e --- /dev/null +++ b/sample-parent/sample-connector/src/main/java/org/talend/sdk/component/test/connectors/service/GenerateExceptionServices.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2006-2024 Talend Inc. - www.talend.com + * + * Licensed 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.talend.sdk.component.test.connectors.service; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; + +import org.talend.sdk.component.api.exception.ComponentException; +import org.talend.sdk.component.api.service.Service; +import org.talend.sdk.component.runtime.base.lang.exception.InvocationExceptionWrapper; + +@Service +public class GenerateExceptionServices implements Serializable { + + public void generateException() throws ComponentException { + IndexOutOfBoundsException indexOutOfBoundsException = + new IndexOutOfBoundsException("This is an index out of bound exception"); + IllegalArgumentException illegalArgumentException = + new IllegalArgumentException("This is an illegal argument exception", indexOutOfBoundsException); + IllegalStateException illegalStateException = + new IllegalStateException("This is an illegal state exception", illegalArgumentException); + throw new ComponentException("This is component exception", illegalStateException); + } + + public void generateRuntimeException() throws ComponentException { + throw InvocationExceptionWrapper + .toRuntimeException(new InvocationTargetException(new CustomException("custom for test"))); + } + + public static class CustomException extends RuntimeException { + + public CustomException(final String message) { + super(message, new AnotherException("other")); + } + } + + public static class AnotherException extends RuntimeException { + + public AnotherException(final String message) { + super(message); + } + } +} \ No newline at end of file diff --git a/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/config/Messages.properties b/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/config/Messages.properties index 51cdeeac32576..e32ded9bb01ba 100644 --- a/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/config/Messages.properties +++ b/sample-parent/sample-connector/src/main/resources/org/talend/sdk/component/test/connectors/config/Messages.properties @@ -58,6 +58,10 @@ InputConfig.dataset._displayName = Name: Dataset InputConfig.dataset._documentation = Doc: The dataset. InputConfig.date._displayName = Name: Date InputConfig.date._documentation = Doc: Retrieve only data after the given date. +InputConfig.generateException._displayName = Generate Exception +InputConfig.generateException._documentation = Doc: Use the generate exception service or not. +InputConfig.generateRuntimeException._displayName = Generate Runtime Exception +InputConfig.generateRuntimeException.._documentation = Doc: Use the generate runtime exception service or not. # Output OutputConfig._documentation = Doc: The output config for the connector.