Merge pull request #8983 from smatvienko-tb/feature/onRateLimit-TbMsgPackCallback-skipAllRateLimited
Rule engine: ack all rate limited failures
This commit is contained in:
		
						commit
						a8d0968cc3
					
				@ -232,7 +232,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
 | 
			
		||||
        } catch (RuleNodeException rne) {
 | 
			
		||||
            msg.getCallback().onFailure(rne);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            msg.getCallback().onFailure(new RuleEngineException(e.getMessage()));
 | 
			
		||||
            msg.getCallback().onFailure(new RuleEngineException(e.getMessage(), e));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -335,7 +335,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
 | 
			
		||||
            msg.getCallback().onFailure(rne);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.warn("[" + tenantId + "]" + "[" + entityId + "]" + "[" + msg.getId() + "]" + " onTellNext failure", e);
 | 
			
		||||
            msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage()));
 | 
			
		||||
            msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage(), e));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -349,7 +349,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
 | 
			
		||||
                callback.onSuccess();
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            callback.onFailure(new RuleEngineException(e.getMessage()));
 | 
			
		||||
            callback.onFailure(new RuleEngineException(e.getMessage(), e));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,11 +17,14 @@ package org.thingsboard.server.service.queue;
 | 
			
		||||
 | 
			
		||||
import io.micrometer.core.instrument.Timer;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.common.util.ExceptionUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.AbstractRateLimitException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.RuleNodeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.RuleEngineException;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
 | 
			
		||||
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
@ -57,8 +60,23 @@ public class TbMsgPackCallback implements TbMsgCallback {
 | 
			
		||||
        ctx.onSuccess(id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onRateLimit(RuleEngineException e) {
 | 
			
		||||
        log.debug("[{}] ON RATE LIMIT", id, e);
 | 
			
		||||
        //TODO notify tenant on rate limit
 | 
			
		||||
        if (failedMsgTimer != null) {
 | 
			
		||||
            failedMsgTimer.record(System.currentTimeMillis() - startMsgProcessing, TimeUnit.MILLISECONDS);
 | 
			
		||||
        }
 | 
			
		||||
        ctx.onSuccess(id);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFailure(RuleEngineException e) {
 | 
			
		||||
        if (ExceptionUtil.lookupExceptionInCause(e, AbstractRateLimitException.class) != null) {
 | 
			
		||||
            onRateLimit(e);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        log.trace("[{}] ON FAILURE", id, e);
 | 
			
		||||
        if (failedMsgTimer != null) {
 | 
			
		||||
            failedMsgTimer.record(System.currentTimeMillis() - startMsgProcessing, TimeUnit.MILLISECONDS);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,98 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.thingsboard.server.service.queue;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.params.ParameterizedTest;
 | 
			
		||||
import org.junit.jupiter.params.provider.Arguments;
 | 
			
		||||
import org.junit.jupiter.params.provider.MethodSource;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleNode;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.RuleEngineException;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.RuleNodeException;
 | 
			
		||||
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
class TbMsgPackCallbackTest {
 | 
			
		||||
 | 
			
		||||
    TenantId tenantId;
 | 
			
		||||
    UUID msgId;
 | 
			
		||||
    TbMsgPackProcessingContext ctx;
 | 
			
		||||
    TbMsgPackCallback callback;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    void setUp() {
 | 
			
		||||
        tenantId = TenantId.fromUUID(UUID.randomUUID());
 | 
			
		||||
        msgId = UUID.randomUUID();
 | 
			
		||||
        ctx = mock(TbMsgPackProcessingContext.class);
 | 
			
		||||
        callback = spy(new TbMsgPackCallback(msgId, tenantId, ctx));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Stream<Arguments> testOnFailure_NotRateLimitException() {
 | 
			
		||||
        return Stream.of(
 | 
			
		||||
                Arguments.of(new RuleEngineException("rule engine no cause")),
 | 
			
		||||
                Arguments.of(new RuleEngineException("rule engine caused 1 lvl", new RuntimeException())),
 | 
			
		||||
                Arguments.of(new RuleEngineException("rule engine caused 2 lvl", new RuntimeException(new Exception()))),
 | 
			
		||||
                Arguments.of(new RuleEngineException("rule engine caused 2 lvl Throwable", new RuntimeException(new Throwable()))),
 | 
			
		||||
                Arguments.of(new RuleNodeException("rule node no cause", "RuleChain", new RuleNode()))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ParameterizedTest
 | 
			
		||||
    @MethodSource
 | 
			
		||||
    void testOnFailure_NotRateLimitException(RuleEngineException ree) {
 | 
			
		||||
        callback.onFailure(ree);
 | 
			
		||||
 | 
			
		||||
        verify(callback, never()).onRateLimit(any());
 | 
			
		||||
        verify(callback, never()).onSuccess();
 | 
			
		||||
        verify(ctx, never()).onSuccess(any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Stream<Arguments> testOnFailure_RateLimitException() {
 | 
			
		||||
        return Stream.of(
 | 
			
		||||
                Arguments.of(new RuleEngineException("caused lvl 1", new TbRateLimitsException(EntityType.ASSET))),
 | 
			
		||||
                Arguments.of(new RuleEngineException("caused lvl 2", new RuntimeException(new TbRateLimitsException(EntityType.ASSET)))),
 | 
			
		||||
                Arguments.of(
 | 
			
		||||
                        new RuleEngineException("caused lvl 3",
 | 
			
		||||
                                new RuntimeException(
 | 
			
		||||
                                        new Exception(
 | 
			
		||||
                                                new TbRateLimitsException(EntityType.ASSET)))))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ParameterizedTest
 | 
			
		||||
    @MethodSource
 | 
			
		||||
    void testOnFailure_RateLimitException(RuleEngineException ree) {
 | 
			
		||||
        callback.onFailure(ree);
 | 
			
		||||
 | 
			
		||||
        verify(callback).onRateLimit(any());
 | 
			
		||||
        verify(callback).onFailure(any());
 | 
			
		||||
        verify(callback, never()).onSuccess();
 | 
			
		||||
        verify(ctx).onSuccess(msgId);
 | 
			
		||||
        verify(ctx).onSuccess(any());
 | 
			
		||||
        verify(ctx, never()).onFailure(any(), any(), any());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,41 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.thingsboard.server.common.data.exception;
 | 
			
		||||
 | 
			
		||||
public abstract class AbstractRateLimitException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
    public AbstractRateLimitException() {
 | 
			
		||||
        super();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AbstractRateLimitException(String message) {
 | 
			
		||||
        super(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AbstractRateLimitException(String message, Throwable cause) {
 | 
			
		||||
        super(message, cause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public AbstractRateLimitException(Throwable cause) {
 | 
			
		||||
        super(cause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected AbstractRateLimitException(String message, Throwable cause,
 | 
			
		||||
                                         boolean enableSuppression,
 | 
			
		||||
                                         boolean writableStackTrace) {
 | 
			
		||||
        super(message, cause, enableSuppression, writableStackTrace);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.common.data.exception;
 | 
			
		||||
 | 
			
		||||
public class ApiUsageLimitsExceededException extends RuntimeException {
 | 
			
		||||
public class ApiUsageLimitsExceededException extends AbstractRateLimitException {
 | 
			
		||||
    public ApiUsageLimitsExceededException(String message) {
 | 
			
		||||
        super(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,11 @@ public class RuleEngineException extends Exception {
 | 
			
		||||
        ts = System.currentTimeMillis();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public RuleEngineException(String message, Throwable t) {
 | 
			
		||||
        super(message != null ? message : "Unknown", t);
 | 
			
		||||
        ts = System.currentTimeMillis();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String toJsonString() {
 | 
			
		||||
        try {
 | 
			
		||||
            return mapper.writeValueAsString(mapper.createObjectNode().put("message", getMessage()));
 | 
			
		||||
 | 
			
		||||
@ -39,6 +39,10 @@ public interface TbMsgCallback {
 | 
			
		||||
 | 
			
		||||
    void onFailure(RuleEngineException e);
 | 
			
		||||
 | 
			
		||||
    default void onRateLimit(RuleEngineException e) {
 | 
			
		||||
        onFailure(e);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns 'true' if rule engine is expecting the message to be processed, 'false' otherwise.
 | 
			
		||||
     * message may no longer be valid, if the message pack is already expired/canceled/failed.
 | 
			
		||||
 | 
			
		||||
@ -17,11 +17,12 @@ package org.thingsboard.server.common.msg.tools;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.AbstractRateLimitException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by ashvayka on 22.10.18.
 | 
			
		||||
 */
 | 
			
		||||
public class TbRateLimitsException extends RuntimeException {
 | 
			
		||||
public class TbRateLimitsException extends AbstractRateLimitException {
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final EntityType entityType;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,6 @@ public class MultipleTbQueueCallbackWrapper implements TbQueueCallback {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFailure(Throwable t) {
 | 
			
		||||
        callback.onFailure(new RuleEngineException(t.getMessage()));
 | 
			
		||||
        callback.onFailure(new RuleEngineException(t.getMessage(), t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,6 @@ public class MultipleTbQueueTbMsgCallbackWrapper implements TbQueueCallback {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFailure(Throwable t) {
 | 
			
		||||
        tbMsgCallback.onFailure(new RuleEngineException(t.getMessage()));
 | 
			
		||||
        tbMsgCallback.onFailure(new RuleEngineException(t.getMessage(), t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,6 @@ public class TbQueueTbMsgCallbackWrapper implements TbQueueCallback {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFailure(Throwable t) {
 | 
			
		||||
        tbMsgCallback.onFailure(new RuleEngineException(t.getMessage()));
 | 
			
		||||
        tbMsgCallback.onFailure(new RuleEngineException(t.getMessage(), t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,67 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.thingsboard.common.util;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonParseException;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
 | 
			
		||||
import javax.script.ScriptException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.StringWriter;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class ExceptionUtil {
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("unchecked")
 | 
			
		||||
    public static <T extends Exception> T lookupException(Throwable source, Class<T> clazz) {
 | 
			
		||||
        Exception e = lookupExceptionInCause(source, clazz);
 | 
			
		||||
        if (e != null) {
 | 
			
		||||
            return (T) e;
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Exception lookupExceptionInCause(Throwable source, Class<? extends Exception>... clazzes) {
 | 
			
		||||
        while (source != null) {
 | 
			
		||||
            for (Class<? extends Exception> clazz : clazzes) {
 | 
			
		||||
                if (clazz.isAssignableFrom(source.getClass())) {
 | 
			
		||||
                    return (Exception) source;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            source = source.getCause();
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String toString(Exception e, EntityId componentId, boolean stackTraceEnabled) {
 | 
			
		||||
        Exception exception = lookupExceptionInCause(e, ScriptException.class, JsonParseException.class);
 | 
			
		||||
        if (exception != null && StringUtils.isNotEmpty(exception.getMessage())) {
 | 
			
		||||
            return exception.getMessage();
 | 
			
		||||
        } else {
 | 
			
		||||
            if (stackTraceEnabled) {
 | 
			
		||||
                StringWriter sw = new StringWriter();
 | 
			
		||||
                e.printStackTrace(new PrintWriter(sw));
 | 
			
		||||
                return sw.toString();
 | 
			
		||||
            } else {
 | 
			
		||||
                log.debug("[{}] Unknown error during message processing", componentId, e);
 | 
			
		||||
                return "Please contact system administrator";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,74 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2023 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * 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.thingsboard.common.util;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
class ExceptionUtilTest {
 | 
			
		||||
 | 
			
		||||
    final Exception cause = new RuntimeException();
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenRootCause_whenLookupExceptionInCause_thenReturnRootCauseAndNoStackOverflow() {
 | 
			
		||||
        Exception e = cause;
 | 
			
		||||
        for (int i = 0; i <= 16384; i++) {
 | 
			
		||||
            e = new Exception(e);
 | 
			
		||||
        }
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(e, RuntimeException.class)).isSameAs(cause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenCause_whenLookupExceptionInCause_thenReturnCause() {
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(cause), RuntimeException.class)).isSameAs(cause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenNoCauseAndExceptionIsWantedCauseClass_whenLookupExceptionInCause_thenReturnSelf() {
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(cause, RuntimeException.class)).isSameAs(cause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenNoCause_whenLookupExceptionInCause_thenReturnNull() {
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(), RuntimeException.class)).isNull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenNotWantedCause_whenLookupExceptionInCause_thenReturnNull() {
 | 
			
		||||
        final Exception cause = new IOException();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(cause), RuntimeException.class)).isNull();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    void givenCause_whenLookupExceptionInCauseByMany_thenReturnFirstCause() {
 | 
			
		||||
        final Exception causeIAE = new IllegalAccessException();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE))).isNull();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IOException.class, NoSuchFieldException.class)).isNull();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IllegalAccessException.class, IOException.class, NoSuchFieldException.class)).isSameAs(causeIAE);
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IOException.class, NoSuchFieldException.class, IllegalAccessException.class)).isSameAs(causeIAE);
 | 
			
		||||
 | 
			
		||||
        final Exception causeIOE = new IOException(causeIAE);
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE))).isNull();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), ClassNotFoundException.class, NoSuchFieldException.class)).isNull();
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IOException.class, NoSuchFieldException.class)).isSameAs(causeIOE);
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IllegalAccessException.class, IOException.class, NoSuchFieldException.class)).isSameAs(causeIOE);
 | 
			
		||||
        assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IOException.class, NoSuchFieldException.class, IllegalAccessException.class)).isSameAs(causeIOE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -39,7 +39,7 @@ public class MultipleTbMsgsCallbackWrapper implements TbMsgCallbackWrapper {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFailure(Throwable t) {
 | 
			
		||||
        callback.onFailure(new RuleEngineException(t.getMessage()));
 | 
			
		||||
        callback.onFailure(new RuleEngineException(t.getMessage(), t));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user