Failsafe Apache Camel route

First of all I need to warn about the limitations. I'm going to tell how to prevent particular type of exceptions related to unreachable host:port. The approach allows for application to start up in case of this exception without the problematic route(s). Without the described below approach the entire application fails. It's up to you to decide if the failure in a route should stop the application.

So, the trick is to check if the host:port, which are to be passed into route's endpoint, is reachable. Sample route (please note the call of CustomSocketUtils.isReachable() which short short circuits the creation of route if the crucial host:port is unreachable):

package ru.outofrange.camel.as2.send;
 
import ru.outofrange.camel.certloader.MendelsonCertLoader;
import ru.outofrange.model.test.ArtemisTestMessage;
import ru.outofrange.util.CustomSocketUtils;
import com.sun.istack.ByteArrayDataSource;
import org.apache.camel.*;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.as2.api.*;
import org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity;
import org.apache.camel.component.jackson.JacksonDataFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.mail.BodyPart;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
 
@Component
public class MendelsonAs2ClientRouteBuilder extends RouteBuilder {
 
    private static final Logger LOG = LoggerFactory.getLogger(MendelsonAs2ClientRouteBuilder.class);
 
    private static final boolean NEED_SIGNED_ENCRYPTED = true;
 
    @Autowired
    CamelContext camelContext;
 
    @Autowired
    private MendelsonCertLoader mendelsonCertLoader;
 
    @Value("${as2.version}")
    private String as2Version;
 
    private String remoteHost = "testas2.mendelson-e-c.com";
    private int remotePort = 8080;
 
    private static org.apache.http.entity.ContentType contentTypeEdifact =
            org.apache.http.entity.ContentType.create("application/edifact", (Charset) null);
 
    @Override
    public void configure() throws Exception {
 
        // if the remote host:port is unreachable, then exit early
        if (!CustomSocketUtils.isReachable(remoteHost, remotePort)) {
            LOG.warn("MendelsonAs2ClientRouteBuilder wasn't instantiated - remote "
                    + remoteHost + ":" + remotePort + " unreachable");
            return;
        }
 
        // expected format {"edi":"message_dk2k"}
        from("jetty:http://localhost:3400/link")
                .routeId("as2ClientMendelson")
                .unmarshal(new JacksonDataFormat(ArtemisTestMessage.class))
                .process(new Processor() {
 
                    @Override
                    public void process(Exchange exchange) throws Exception {
                        if (exchange.getIn() != null) {
                            ArtemisTestMessage messageIn = exchange.getIn().getBody(ArtemisTestMessage.class);
                            LOG.debug("Request Body Message: " + messageIn);
                            if (messageIn != null) {
                                exchange.getIn().setBody(messageIn.getEdi());
                            } else {
                                exchange.getIn().setBody("_error");
                            }
                        }
 
                        // enriching
                        exchange.getIn().setHeader("CamelAS2.as2To", "mendelsontestAS2");
                        exchange.getIn().setHeader("CamelAS2.as2From", "mycompanyAS2");
                        exchange.getIn().setHeader("CamelAS2.as2Version", "1.1");
                        exchange.getIn().setHeader("CamelAS2.ediMessageContentType", contentTypeEdifact);
                        exchange.getIn().setHeader("CamelAS2.server", "DKAS2Client");
                        exchange.getIn().setHeader("CamelAS2.subject", "testDK");
                        exchange.getIn().setHeader("CamelAS2.from", "DKEdi");
                        exchange.getIn().setHeader("CamelAS2.dispositionNotificationTo", "dk2k@mail.ru");
                        exchange.getIn().setHeader("CamelAS2.requestUri", "/as2/HttpReceiver");
 
                        if (NEED_SIGNED_ENCRYPTED) {
                            exchange.getIn().setHeader("CamelAS2.as2MessageStructure", AS2MessageStructure.SIGNED_ENCRYPTED);
                            exchange.getIn().setHeader("CamelAS2.signingAlgorithm", AS2SignatureAlgorithm.SHA1WITHRSA);
                            exchange.getIn().setHeader("CamelAS2.encryptingAlgorithm", AS2EncryptionAlgorithm.DES_EDE3_CBC);
 
                            exchange.getIn().setHeader("CamelAS2.signingCertificateChain", mendelsonCertLoader.getChain());
                            exchange.getIn().setHeader("CamelAS2.signingPrivateKey", mendelsonCertLoader.getPrivateKey());
                            exchange.getIn().setHeader("CamelAS2.encryptingCertificateChain", mendelsonCertLoader.getChain());
                        } else {
                            exchange.getIn().setHeader("CamelAS2.as2MessageStructure", AS2MessageStructure.PLAIN);
                        }
 
                    }
                })
                .to("as2://client/send?targetHostName=" +
                        remoteHost +
                        "&targetPortNumber=" + remotePort + // http
                        "&inBody=ediMessage" +
                        "&requestUri=/as2/HttpReceiver"
                )
                .id("DKAS2sender")
                .process(new Processor() {
                    @Override
                    public void process(Exchange exchange) throws Exception {
                        String messageIn = exchange.getIn().getBody(String.class);
                        LOG.debug("MDN Message: " + messageIn);
                        processMultipartMessage(exchange.getIn());
                    }
                });
        LOG.debug("Mendelson route created");
    }
 
    private void processMultipartMessage(Message message) {
        //org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity
        if (message.getBody() instanceof DispositionNotificationMultipartReportEntity) {
            DispositionNotificationMultipartReportEntity dispositionNotificationMultipartReportEntity =
                    (DispositionNotificationMultipartReportEntity) message.getBody();
            try {
                InputStream inputStream = dispositionNotificationMultipartReportEntity.getContent();
 
                ByteArrayDataSource datasource = new ByteArrayDataSource(inputStream.readAllBytes(), "multipart/report");
                MimeMultipart multipart = new MimeMultipart(datasource);
 
                int count = multipart.getCount();
                LOG.debug("count " + count);
                for (int i = 0; i < count; i++) {
                    BodyPart bodyPart = multipart.getBodyPart(i);
                    if (bodyPart.isMimeType("text/plain")) {
                        LOG.info("text/plain");
                        LOG.debug(bodyPart.getContent().getClass().getName());
                        Enumeration<Header> headerEnumeration = bodyPart.getAllHeaders();
                        while (headerEnumeration.hasMoreElements()) {
                            Header header = headerEnumeration.nextElement();
                            LOG.debug(header.getName() + ": " + header.getValue());
                        }
                        LOG.debug("----");
                        LOG.debug(bodyPart.getContent().toString());
                        LOG.debug("----");
                        //processTextData(bodyPart.getContent());
                    } else if (bodyPart.isMimeType("application/octet-stream")) {
                        LOG.info("application/octet-stream");
                        LOG.debug(bodyPart.getContent().getClass().getName());
                        //processBinaryData(bodyPart.getInputStream());
                    } else if (bodyPart.isMimeType("message/disposition-notification")) {
                        // MDN!
                        LOG.info("message/disposition-notification");
                        Enumeration<Header> headerEnumeration = bodyPart.getAllHeaders();
                        while (headerEnumeration.hasMoreElements()) {
                            Header header = headerEnumeration.nextElement();
                            LOG.debug(header.getName() + ": " + header.getValue());
                        }
 
                        if (bodyPart.getContent() instanceof ByteArrayInputStream) {
                            ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) bodyPart.getContent();
                            int n = byteArrayInputStream.available();
                            byte[] bytes = new byte[n];
                            byteArrayInputStream.read(bytes, 0, n);
                            String s = new String(bytes, StandardCharsets.UTF_8);
                            LOG.debug("----");
                            LOG.debug(new String(bytes));
                            LOG.debug("----");
                        }
                    } else {
                        LOG.debug(bodyPart.getContent().getClass().getName());
                        LOG.warn("default " + bodyPart.getContentType());
                    }
                }
            } catch (IOException | MessagingException e) {
                e.printStackTrace();
            }
        }
    }
}

Class CustomSocketUtils:
package ru.outofrange.util;
 
import java.io.IOException;
import java.net.Socket;
 
public class CustomSocketUtils {
 
    // checks if remote host:port is reachable
    public static boolean isReachable(String host, int port) {
        try (Socket ss = new Socket(host, port)) {
            return true;
        } catch (IOException e) {
        } finally {
        }
 
        return false;
    }
}

You can leave a response, or trackback from your own site.

Leave a Reply