001package ca.uhn.hl7v2.hoh.relay.sender;
002
003import java.io.IOException;
004import java.net.ConnectException;
005import java.util.Map;
006
007import org.springframework.beans.factory.BeanNameAware;
008import org.springframework.beans.factory.InitializingBean;
009
010import ca.uhn.hl7v2.HL7Exception;
011import ca.uhn.hl7v2.hoh.api.DecodeException;
012import ca.uhn.hl7v2.hoh.api.EncodeException;
013import ca.uhn.hl7v2.hoh.api.IReceivable;
014import ca.uhn.hl7v2.hoh.hapi.api.MessageSendable;
015import ca.uhn.hl7v2.hoh.hapi.client.HohClientMultithreaded;
016import ca.uhn.hl7v2.hoh.relay.Binder;
017import ca.uhn.hl7v2.hoh.util.Validate;
018import ca.uhn.hl7v2.model.Message;
019import ca.uhn.hl7v2.protocol.ApplicationRouter;
020import ca.uhn.hl7v2.protocol.MetadataKeys;
021import ca.uhn.hl7v2.protocol.ReceivingApplicationException;
022import ca.uhn.hl7v2.util.Terser;
023
024public class RelayHttpSender extends HohClientMultithreaded implements IRelaySender<Message>, BeanNameAware, InitializingBean {
025
026        private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RelayHttpSender.class);
027        private String myBeanName;
028        private int myIoRetries = 0;
029        
030        /**
031         * {@inheritDoc}
032         */
033        public void afterPropertiesSet() throws Exception {
034                Validate.propertySet(getUrl(), "Url");
035                ourLog.info("Sender [{}] will transmit by HL7 over HTTP to {}", myBeanName, getUrl().toExternalForm());
036        }
037
038        /**
039         * Returns true
040         * 
041         * {@inheritDoc}
042         */
043        public boolean canProcess(Message theMessage) {
044                return true;
045        }
046
047        /**
048         * {@inheritDoc}
049         */
050        public String getBeanName() {
051                return myBeanName;
052        }
053
054        /**
055         * @see #setIoRetries(int)
056         */
057        public int getIoRetries() {
058                return myIoRetries;
059        }
060
061        /**
062         * {@inheritDoc}
063         */
064        public Message processMessage(Message theMessage, Map<String, Object> theMetadata) throws ReceivingApplicationException, HL7Exception {
065                String sendingIp = (String) theMetadata.get(ApplicationRouter.METADATA_KEY_SENDING_IP);
066                Object sendingPort = theMetadata.get(ApplicationRouter.METADATA_KEY_SENDING_PORT);
067                String controlId = (String) theMetadata.get(ApplicationRouter.METADATA_KEY_MESSAGE_CONTROL_ID);
068                String rawMessage = (String) theMetadata.get(MetadataKeys.IN_RAW_MESSAGE);
069
070                ourLog.info("Relaying message ({} bytes) with ID {} from {}:{} to URL {}", new Object[] {rawMessage.length(), controlId, sendingIp, sendingPort, getUrl()});
071                
072                IReceivable<Message> response;
073                long delay = System.currentTimeMillis();
074                int attempt = -1;
075                while(true) {
076                        attempt++;
077                        
078                        if (attempt > 0) {
079                                ourLog.info("This is attempt {}", attempt);
080                        }
081                        
082                        try {
083                                response = sendAndReceiveMessage(new MessageSendable(theMessage));
084                                delay = System.currentTimeMillis() - delay;
085                        } catch (DecodeException e) {
086                                ourLog.error("Failed to process HL7 over HTTP response from URL \"" + getUrl().toExternalForm() + "\"", e);
087                                throw new HL7Exception(Binder.getProductname() + " - Failed to process HL7 over HTTP response from URL \"" + getUrl().toExternalForm() + "\" - Error was: " + e.getMessage());
088                        } catch (ConnectException e) {
089                                ourLog.info("Failed to connect to URL \"" + getUrl().toExternalForm() + "\" - Error was: " + e.getMessage());
090                                throw new HL7Exception(Binder.getProductname() + " - Failed to connect to URL \"" + getUrl().toExternalForm() + "\" - Error was: " + e.getMessage());
091                        } catch (IOException e) {
092                                if (attempt < myIoRetries) {
093                                        ourLog.warn("Got an IOException, going to retry transmission: " + e.toString());
094                                        continue;
095                                }
096                                ourLog.error("IO Exception communicating with URL \"" + getUrl().toExternalForm() + "\"", e);
097                                throw new HL7Exception(Binder.getProductname() + " - IO Exception communicating with URL URL \"" + getUrl().toExternalForm() + "\" - Error was: " + e.getMessage());
098                        } catch (EncodeException e) {
099                                ourLog.error("Failed to create HTTP request", e);
100                                throw new HL7Exception(Binder.getProductname() + " - Failed to create HTTP request - Error was: " + e.getMessage());
101                        }
102                
103                        break;
104                }
105                
106                String responseControlId = new Terser(response.getMessage()).get("/MSH-10");
107                ourLog.info("Received response to ID {} with ID {} in {} ms", new Object[] {controlId, responseControlId, delay});
108                
109                return response.getMessage();
110        }
111
112        /**
113         * <i>Automatically called by the container</i>
114         */
115        public void setBeanName(String theBeanName) {
116                myBeanName = theBeanName;
117        }
118
119        /**
120         * If set to a positive integer, the relay will attempt to redeliver a message up to the given
121         * number of times before giving up, if the transmission fails due to an IO exception. 
122         */
123        public void setIoRetries(int theIoRetries) {
124                myIoRetries = theIoRetries;
125        }
126
127
128}