All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.citrusframework.camel.actions.CamelVerifyIntegrationAction Maven / Gradle / Ivy

The newest version!
/*
 * Copyright the original author or 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.citrusframework.camel.actions;

import java.util.Map;

import org.citrusframework.camel.CamelSettings;
import org.citrusframework.context.TestContext;
import org.citrusframework.exceptions.ActionTimeoutException;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.jbang.ProcessAndOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Verifies Camel integration via Camel JBang. Checks route for running/stopped state and optionally waits for a log message to be present.
 * Raises errors when either the Camel integration is not in expected state or the log message is not available.
 * Both operations are automatically retried for a given amount of attempts.
 */
public class CamelVerifyIntegrationAction extends AbstractCamelJBangAction {

    private static final Logger INTEGRATION_STATUS_LOG = LoggerFactory.getLogger("INTEGRATION_STATUS");
    private static final Logger INTEGRATION_LOG = LoggerFactory.getLogger("INTEGRATION_LOGS");

    /** Logger */
    private static final Logger logger = LoggerFactory.getLogger(CamelVerifyIntegrationAction.class);

    private final String integrationName;

    private final String logMessage;
    private final int maxAttempts;
    private final long delayBetweenAttempts;

    private final String phase;
    private final boolean printLogs;

    private final boolean stopOnErrorStatus;

    /**
     * Default constructor.
     */
    public CamelVerifyIntegrationAction(Builder builder) {
        super("verify-integration", builder);

        this.integrationName = builder.integrationName;
        this.phase = builder.phase;
        this.logMessage = builder.logMessage;
        this.maxAttempts = builder.maxAttempts;
        this.delayBetweenAttempts = builder.delayBetweenAttempts;
        this.printLogs = builder.printLogs;
        this.stopOnErrorStatus = builder.stopOnErrorStatus;
    }

    @Override
    public void doExecute(TestContext context) {
        String name = context.replaceDynamicContentInString(integrationName);

        logger.info("Verify Camel integration '%s' ...".formatted(name));

        Long pid = verifyRouteStatus(name, context.replaceDynamicContentInString(phase), context);

        if (logMessage != null) {
            verifyRouteLogs(pid, name, context.replaceDynamicContentInString(logMessage.trim()), context);
        }

        logger.info("Successfully verified Camel integration '%s'".formatted(name));
    }

    private void verifyRouteLogs(Long pid, String name, String message, TestContext context) {
        if (printLogs) {
            INTEGRATION_LOG.info(String.format("Waiting for Camel integration '%s' to log message", name));
        }

        String log;
        int offset = 0;

        ProcessAndOutput pao = context.getVariable(name + ":process:" + pid, ProcessAndOutput.class);
        for (int i = 0; i < maxAttempts; i++) {
            log = pao.getOutput();

            if (printLogs && (offset < log.length())) {
                INTEGRATION_LOG.info(log.substring(offset));
                offset = log.length();
            }

            if (log.contains(message)) {
                logger.info("Verified Camel integration logs - All values OK!");
                return;
            }

            if (!printLogs) {
                logger.warn(String.format("Waiting for Camel integration '%s' to log message - retry in %s ms", name, delayBetweenAttempts));
            }

            try {
                Thread.sleep(delayBetweenAttempts);
            } catch (InterruptedException e) {
                logger.warn("Interrupted while waiting for Camel integration logs", e);
            }
        }

        throw new ActionTimeoutException((maxAttempts * delayBetweenAttempts),
                new CitrusRuntimeException(String.format("Failed to verify Camel integration '%s' - " +
                        "has not printed message '%s' after %d attempts", name, message, maxAttempts)));
    }

    private Long verifyRouteStatus(String name, String phase, TestContext context) {
        INTEGRATION_STATUS_LOG.info(String.format("Waiting for Camel integration '%s' to be in state '%s'", name, phase));

        for (int i = 0; i < maxAttempts; i++) {
            if (context.getVariables().containsKey(name + ":pid")) {
                Long pid = context.getVariable(name + ":pid", Long.class);
                Map properties = camelJBang().get(pid);
                if ((phase.equals("Stopped") && properties.isEmpty()) || (!properties.isEmpty() && properties.get("STATUS").equals(phase))) {
                    logger.info(String.format("Verified Camel integration '%s' state '%s' - All values OK!", name, phase));
                    return pid;
                } else if (phase.equals("Error")) {
                    logger.info(String.format("Camel integration '%s' is in state 'Error'", name));
                    if (stopOnErrorStatus) {
                        throw new CitrusRuntimeException(String.format("Failed to verify Camel integration '%s' - is in state 'Error'", name));
                    }
                }

                if (context.getVariables().containsKey(name + ":process:" + pid)) {
                    // check if process is still alive
                    ProcessAndOutput pao = context.getVariable(name + ":process:" + pid, ProcessAndOutput.class);
                    if (!pao.getProcess().isAlive()) {
                        logger.info("Failed to verify Camel integration '%s' - exit code %s".formatted(name, pao.getProcess().exitValue()));
                        logger.info(pao.getOutput());

                        throw new CitrusRuntimeException(String.format("Failed to verify Camel integration '%s' - exit code %s", name, pao.getProcess().exitValue()));
                    }
                }
            }

            logger.info(System.lineSeparator() + camelJBang().ps());
            logger.info(String.format("Waiting for Camel integration '%s' to be in state '%s'- retry in %s ms", name, phase, delayBetweenAttempts));
            try {
                Thread.sleep(delayBetweenAttempts);
            } catch (InterruptedException e) {
                logger.warn("Interrupted while waiting for Camel integration state", e);
            }
        }

        throw new ActionTimeoutException((maxAttempts * delayBetweenAttempts),
                new CitrusRuntimeException(String.format("Failed to verify Camel integration '%s' - " +
                        "is not in state '%s' after %d attempts", name, phase, maxAttempts)));

    }

    public String getIntegrationName() {
        return integrationName;
    }

    /**
     * Action builder.
     */
    public static final class Builder extends AbstractCamelJBangAction.Builder {

        private String integrationName = "route";

        private String logMessage;

        private int maxAttempts = CamelSettings.getMaxAttempts();
        private long delayBetweenAttempts = CamelSettings.getDelayBetweenAttempts();

        private String phase = "Running";
        private boolean printLogs = CamelSettings.isPrintLogs();

        private boolean stopOnErrorStatus = true;

        /**
         * Identify Camel JBang process for this route.
         * @param name
         * @return
         */
        public Builder integration(String name) {
            this.integrationName = name;
            return this;
        }

        /**
         * Sets the integration name.
         * @param name
         * @return
         */
        public Builder integrationName(String name) {
            this.integrationName = name;
            return this;
        }

        public Builder isRunning() {
            this.phase = "Running";
            return this;
        }

        public Builder isStopped() {
            this.phase = "Stopped";
            return this;
        }

        public Builder isInPhase(String phase) {
            this.phase = phase;
            return this;
        }

        public Builder printLogs(boolean printLogs) {
            this.printLogs = printLogs;
            return this;
        }

        public Builder waitForLogMessage(String logMessage) {
            this.logMessage = logMessage;
            return this;
        }

        public Builder maxAttempts(int maxAttempts) {
            this.maxAttempts = maxAttempts;
            return this;
        }

        public Builder delayBetweenAttempts(long delayBetweenAttempts) {
            this.delayBetweenAttempts = delayBetweenAttempts;
            return this;
        }

        public Builder stopOnErrorStatus(boolean stopOnErrorStatus) {
            this.stopOnErrorStatus = stopOnErrorStatus;
            return this;
        }

        @Override
        public CamelVerifyIntegrationAction build() {
            return new CamelVerifyIntegrationAction(this);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy