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

uk.nhs.ciao.docs.parser.route.InProgressFolderManagerRoute Maven / Gradle / Ivy

The newest version!
package uk.nhs.ciao.docs.parser.route;

import static org.apache.camel.builder.ExpressionBuilder.append;
import static org.apache.camel.builder.PredicateBuilder.*;
import static uk.nhs.ciao.logging.CiaoCamelLogMessage.camelLogMsg;

import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import uk.nhs.ciao.camel.BaseRouteBuilder;
import uk.nhs.ciao.logging.CiaoCamelLogger;
import uk.nhs.ciao.util.Clock;
import uk.nhs.ciao.util.SystemClock;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

/**
 * Provides methods to manage the in-progress folders associated with a document upload.
 * 

* The in-progress folder has the following structure: *

*

 * source/
 *   ${documentName} - original document content
 * control/
 *   error-folder - single line file specifying location of error folder
 *   completed-folder - single line file specifying location of completed folder
 *   wants-bus-ack - empty file, present if an ITK business ack has been requested
 *   wants-inf-ack - empty file, present if an ITK infrastructure ack has been requested
 * state/
 *   ${timestamp}-${messageType}-${eventType} - file contains the message associated with the event
 * 
*

* State event types: *

  • preparation-failed - a message/document has failed preparation *
  • sending - a message is being sent *
  • sent - a message was successfully sent *
  • send-failed - a message failed to send *
  • received - a message was received *

    * State message types: *

      *
    • document - file related to the original source document *
    • bus-message - file containing an ITK business message *
    • inf-ack - file containing an ITK infrastructure ack *
    • bus-ack - file containing an ITK business ack *
    • inf-nack - file containing an ITK infrastructure nack *
    • bus-nack - file containing an ITK business nack *
    *

    * The timestamp format is yyyyMMdd-HHmmssSSS (in UTC) */ public class InProgressFolderManagerRoute extends BaseRouteBuilder { private static final CiaoCamelLogger LOGGER = CiaoCamelLogger.getLogger(InProgressFolderManagerRoute.class); private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormat.forPattern("yyyyMMdd-HHmmssSSS").withZoneUTC(); /** * Enumeration of header names supported by the manager */ public static final class Header { public static final String ACTION = "ciao.inProgressFolder.action"; public static final String FILE_TYPE = "ciao.inProgressFolder.fileType"; public static final String EVENT_TYPE = "ciao.inProgressFolder.eventType"; // Internal (private) headers private static final String MESSAGE_TYPE = "ciao.inProgressFolder.messageType"; private Header() { // Suppress default constructor } } /** * Enumeration of action header values supported by the manager * * @see Header#ACTION */ public static final class Action { public static final String STORE = "store"; private Action() { // Suppress default constructor } } /** * Enumeration of file type header values supported by the manager * * @see Header#FILE_TYPE */ public static final class FileType { public static final String CONTROL = "control"; public static final String EVENT = "event"; private FileType() { // Suppress default constructor } } /** * Enumeration of event type header values supported by the manager * * @see Header#EVENT_TYPE */ public static final class EventType { public static final String MESSAGE_PREPARATION_FAILED = "preparation-failed"; public static final String MESSAGE_SENDING = "sending"; public static final String MESSAGE_SENT = "sent"; public static final String MESSAGE_SEND_FAILED = "send-failed"; public static final String MESSAGE_RECEIVED = "received"; private EventType() { // Suppress default constructor } } /** * Enumeration of known message types *

    * Values are supplied via the {@link Exchange#FILE_NAME} header */ public static final class MessageType { public static final String DOCUMENT = "document"; public static final String BUSINESS_MESSAGE = "bus-message"; public static final String INFRASTRUCTURE_ACK = "inf-ack"; public static final String INFRASTRUCTURE_NACK = "inf-nack"; public static final String BUSINESS_ACK = "bus-ack"; public static final String BUSINESS_NACK = "bus-nack"; private MessageType() { // Suppress default constructor } } private String inProgressFolderManagerUri; private String inProgressFolderRootUri; private Clock clock = SystemClock.getInstance(); public void setInProgressFolderManagerUri(final String inProgressFolderManagerUri) { this.inProgressFolderManagerUri = inProgressFolderManagerUri; } public void setInProgressFolderRootUri(final String inProgressFolderRootUri) { this.inProgressFolderRootUri = inProgressFolderRootUri; } public void setClock(final Clock clock) { this.clock = Preconditions.checkNotNull(clock); } private String getStoreEventFileUri() { return internalDirectUri("store-event-file"); } private String getStoreEventFileHandlerUri() { return internalDirectUri("store-event-file-handler"); } private String getStoreControlFileUri() { return internalDirectUri("store-control-file"); } @Override public void configure() throws Exception { configureRouter(); configureStoreControlFileRoute(); configureStoreEventFileRoute(); configureStoreEventFileHandlerRoute(); } private void configureRouter() throws Exception { from(inProgressFolderManagerUri) .choice() .when(isEqualTo(header(Header.ACTION), constant(Action.STORE))) .pipeline() .choice() .when(isEqualTo(header(Header.FILE_TYPE), constant(FileType.CONTROL))) .to(getStoreControlFileUri()) .endChoice() .when(isEqualTo(header(Header.FILE_TYPE), constant(FileType.EVENT))) .to(getStoreEventFileUri()) .endChoice() .end() .end() .endChoice() .end() .end(); } private void configureStoreControlFileRoute() throws Exception { from(getStoreControlFileUri()) .bean(new ControlFileNameCalculator()) .to(inProgressFolderRootUri + "?fileExist=Override") .process(LOGGER.info(camelLogMsg("Stored control file in in-progress folder") .documentId(header(Exchange.CORRELATION_ID)) .fileName(header(Exchange.FILE_NAME)))) .end(); } private void configureStoreEventFileRoute() throws Exception { from(getStoreEventFileUri()) .errorHandler(defaultErrorHandler() .maximumRedeliveries(10) .redeliveryDelay(1)) .to(getStoreEventFileHandlerUri()) .process(LOGGER.info(camelLogMsg("Stored event file in in-progress folder") .documentId(header(Exchange.CORRELATION_ID)) .eventName(append(append(header(Header.MESSAGE_TYPE), constant("-")), header(Header.EVENT_TYPE))) .fileName(header(Exchange.FILE_NAME)))) .end(); } private void configureStoreEventFileHandlerRoute() throws Exception { from(getStoreEventFileHandlerUri()) .errorHandler(noErrorHandler()) .bean(new EventFileNameCalculator()) .to(inProgressFolderRootUri + "?fileExist=Fail") .end(); } // Processor / bean methods // The methods can't live in the route builder - it causes havoc with the debug/tracer logging public class ControlFileNameCalculator { public void calculateFileName(final Message message) throws Exception { final String originalName = Strings.nullToEmpty(message.getHeader(Exchange.FILE_NAME, String.class)); final String id = message.getHeader(Exchange.CORRELATION_ID, String.class); if (Strings.isNullOrEmpty(id)) { throw new Exception("Missing header " + Exchange.CORRELATION_ID); } final String fileName = id + "/control/" + originalName; message.setHeader(Exchange.FILE_NAME, fileName); } } public class EventFileNameCalculator { public void calculateFileName(final Message message) throws Exception { final String messageType = Strings.nullToEmpty(message.getHeader(Exchange.FILE_NAME, String.class)); message.setHeader(Header.MESSAGE_TYPE, messageType); final String id = message.getHeader(Exchange.CORRELATION_ID, String.class); if (Strings.isNullOrEmpty(id)) { throw new Exception("Missing header " + Exchange.CORRELATION_ID); } final String eventType = message.getHeader(Header.EVENT_TYPE, String.class); if (Strings.isNullOrEmpty(eventType)) { throw new Exception("Missing header " + Header.EVENT_TYPE); } final String timestamp = TIMESTAMP_FORMAT.print(clock.getMillis()); final String fileName = id + "/events/" + timestamp + "-" + messageType + "-" + eventType; message.setHeader(Exchange.FILE_NAME, fileName); } } }





  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy