001/** 002 * The contents of this file are subject to the Mozilla Public License Version 1.1 003 * (the "License"); you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at http://www.mozilla.org/MPL/ 005 * Software distributed under the License is distributed on an "AS IS" basis, 006 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 007 * specific language governing rights and limitations under the License. 008 * 009 * The Original Code is "Receiver.java". Description: 010 * "Listens for incoming messages on a certain input stream, and 011 * sends them to the appropriate location." 012 * 013 * The Initial Developer of the Original Code is University Health Network. Copyright (C) 014 * 2002. All Rights Reserved. 015 * 016 * Contributor(s): _____________. 017 * 018 * Alternatively, the contents of this file may be used under the terms of the 019 * GNU General Public License (the "GPL"), in which case the provisions of the GPL are 020 * applicable instead of those above. If you wish to allow use of your version of this 021 * file only under the terms of the GPL and not to allow others to use your version 022 * of this file under the MPL, indicate your decision by deleting the provisions above 023 * and replace them with the notice and other provisions required by the GPL License. 024 * If you do not delete the provisions above, a recipient may use your version of 025 * this file under either the MPL or the GPL. 026 */ 027 028package ca.uhn.hl7v2.app; 029 030import java.io.IOException; 031import java.net.SocketException; 032 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import ca.uhn.hl7v2.concurrent.Service; 037import ca.uhn.hl7v2.llp.HL7Reader; 038import ca.uhn.hl7v2.llp.LLPException; 039 040/** 041 * Listens for incoming messages on a certain input stream, and sends them to 042 * the appropriate location. 043 * 044 * @author Bryan Tripp 045 */ 046public class Receiver extends Service { 047 048 private static final Logger log = LoggerFactory.getLogger(Receiver.class); 049 050 private final ActiveConnection conn; 051 private final HL7Reader in; 052 private ReceiverParserExceptionHandler parserExeptionHandler; 053 054 /** Creates a new instance of Receiver, associated with the given Connection */ 055 public Receiver(ActiveConnection c, HL7Reader in) { 056 super("Receiver", c.getExecutorService()); 057 this.conn = c; 058 this.in = in; 059 } 060 061 public void setParserExeptionHandler(ReceiverParserExceptionHandler parserExeptionHandler) { 062 this.parserExeptionHandler = parserExeptionHandler; 063 } 064 065 @Override 066 protected void handle() { 067 try { 068 String message = in.getMessage(); 069 if (message == null) { 070 log.debug("Failed to read a message"); 071 } else { 072 processMessage(message); 073 } 074 } catch (LLPException e) { 075 //For any protocol exceptions on this particular connection notify the application about the same 076 //and close the connection 077 conn.close(); 078 log.info("LLPException: closing Connection from " + describeRemoteConnection() + ", will no longer read messages with this Receiver: " + e.getMessage()); 079 if(parserExeptionHandler!=null) { 080 parserExeptionHandler.handle(e); 081 } 082 } catch (SocketException e) { 083 // This probably means that the client closed the server connection normally 084 conn.close(); 085 log.info("SocketException: closing Connection from " + describeRemoteConnection() + ", will no longer read messages with this Receiver: " + e.getMessage()); 086 } catch (IOException e) { 087 conn.close(); 088 log.warn("IOException: closing Connection from " + describeRemoteConnection() + ", will no longer read messages with this Receiver. ", e); 089 } catch (Exception e) { 090 conn.close(); 091 log.error("Unexpected error, closing connection from " + describeRemoteConnection() + " - ", e); 092 } 093 094 } 095 096 097 private String describeRemoteConnection() { 098 return conn.getRemoteAddress().getHostAddress() + ":" + conn.getRemotePort(); 099 } 100 101 102 /** 103 * Processes a single incoming message by sending it to the appropriate 104 * internal location. If an incoming message contains an MSA-2 field, it is 105 * assumed that this message is meant as a reply to a message that has been 106 * sent earlier. In this case an attempt is give the message to the object 107 * that sent the corresponding outbound message. If the message contains an 108 * MSA-2 but there are no objects that appear to be waiting for it, it is 109 * discarded and an exception is logged. If the message does not contain an 110 * MSA-2 field, it is concluded that the message has arrived unsolicited. In 111 * this case it is sent to the Responder (in a new Thread). 112 */ 113 protected void processMessage(String message) { 114 String ackID = conn.getParser().getAckID(message); 115 if (ackID == null) { 116 log.debug("Unsolicited Message Received: {}", message); 117 getExecutorService().submit(new Grunt(conn, message)); 118 } else { 119 if ( conn.acceptAllMessages() ){ 120 getExecutorService().submit(new Grunt(conn, message)); 121 }else if (!conn.isRecipientWaiting(ackID, message)) { 122 log.info("Unexpected Message Received. This message appears to be an acknowledgement (MSA-2 has a value) so it will be ignored: {}", message); 123 } else { 124 log.debug("Response Message Received: {}", message); 125 } 126 } 127 } 128 129 /** Independent thread for processing a single message */ 130 private static class Grunt implements Runnable { 131 132 private final ActiveConnection conn; 133 private final String m; 134 135 public Grunt(ActiveConnection conn, String message) { 136 this.conn = conn; 137 this.m = message; 138 } 139 140 public void run() { 141 try { 142 String response = conn.getResponder().processMessage(m); 143 if (response != null) { 144 conn.getAckWriter().writeMessage(response); 145 } else { 146 log.debug("Not responding to incoming message"); 147 } 148 } catch (Exception e) { 149 log.error("Error while processing message: ", e); 150 } 151 } 152 } 153 154 /** 155 * Handle any protocol level parsing exceptions and pass them on to an exception handler 156 */ 157 public static interface ReceiverParserExceptionHandler { 158 void handle(Exception e); 159 } 160}