0
votes

I have a requirement which is as follows:

  • Accept HTTP POST requests containing XML to a certain URL.
  • Perform pre-requisite actions such as saving the request XML to a file.
  • Validate the incoming XML matches the corresponding schema.
  • If the schema validation fails, synchronously respond with a HTTP 400 response code.
  • If the schema validation passes, synchronously respond with a HTTP 200 response code.
  • Pass the XML message on for further processing.
  • When this further processing completes, asynchronously respond to the caller with a HTTP 200 response code.

This is currently how I have the route configured:

        onException(IOException.class)
            .log(LoggingLevel.INFO, "Schema validation error on incoming message: ${id}")
            .handled(true)
            .maximumRedeliveries(0)
            .process(schemaValidationErrorProcessor);

        from("restlet:http://localhost:" + portNum + "/api/XX/XXX?restletMethod=POST")
            .log(LoggingLevel.INFO, "Received message")
            .convertBodyTo(String.class)
            .multicast()
            .parallelProcessing()
                .to(SAVE_REQUEST_TO_FILE_QUEUE, PROCESS_PROVISIONING_REQUEST_QUEUE);

        from(SAVE_REQUEST_TO_FILE_QUEUE)
            .log(LoggingLevel.INFO, "Storing message: ${id}")
            .to("file://" + requestLogFolder);

        from(PROCESS_PROVISIONING_REQUEST_QUEUE)
            .log(LoggingLevel.INFO, "Processing provisioning request: ${id}")
            .process(requestGate)
            .choice()
                .when(header(SYSTEM_STATUS_HEADER).isEqualTo(true))
                    .unmarshal(xmlParser)
                    .inOnly("bean:requestHandler?method=handle")
                .when(header(SYSTEM_STATUS_HEADER).isEqualTo(false))
                    .log(LoggingLevel.INFO, "Intentially dropping message")
            .endChoice();

The schema validation part is achieved via the .unmarshal(xmlParser) line (I have a JaxbDataFormat object configured elsewhere with the schema set in that). When schema validation fails, an IOException is thrown and this is handled by my schemaValidationErrorProcessor which adds the HTTP 400 to the response.

That is all working fine.

The problem I am having is passing the XML message on for further processing. Basically, I need this to be done asynchronously because when the schema validation passes I need to synchronously respond with a 200 response. The processing that I need to do is in the .inOnly("bean:requestHandler?method=handle") line.

I naively thought that setting the routing to my bean to inOnly would set this to be asynchronous and that main route would not wait for a response. However, this is not the case as when the requestHandler.handle method throws an exception, this is thrown back to the caller of the REST endpoint. I don't want this to happen as I want all of this processing to be done in 'the background' as the consumer will have already received a 200 response.

So, my question is, how would I go about achieving such behaviour? I have thought about using queues etc but ideally would like to avoid such components if possible.

1
You're saying that you respond twice, once synchronously and once asynchronously, which is at best strange, at worst - impossible. What you could do is use something like Websockets or an abstraction like Atmosphere to keep pushing messages to the client, but then all messages are async (which is a good thing in your case, probably). To get the async behavior in Camel, see this: camel.apache.org/async.htmlkaqqao
They are the business requirements we have. The customer wanted a synchronous response to indicate we had received their message successfully and they wanted an async callback when we had successfully processed the message.chrishern

1 Answers

0
votes

Use Camel Websocket component for asynchronously respond to the caller.

From the Camel documentation:

from("activemq:topic:newsTopic")
    .routeId("fromJMStoWebSocket")
    .to("websocket://localhost:8443/newsTopic?sendToAll=true&staticResources=classpath:webapp");