/// <param name="message">a parsed message to validate (note that MSH-9-1 and MSH-9-2 must be valued) /// </param> /// <returns> true if the message is OK /// </returns> /// <throws> HL7Exception if there is at least one error and this validator is set to fail on errors </throws> public virtual bool validate(Message message) { Terser t = new Terser(message); NuGenMessageRule[] rules = myContext.getMessageRules(message.Version, t.get_Renamed("MSH-9-1"), t.get_Renamed("MSH-9-2")); NuGenValidationException toThrow = null; bool result = true; for (int i = 0; i < rules.Length; i++) { NuGenValidationException[] ex = rules[i].test(message); for (int j = 0; j < ex.Length; j++) { result = false; if (failOnError && toThrow == null) { toThrow = ex[j]; } } } if (toThrow != null) { throw new NuGenHL7Exception("Invalid message", toThrow); } return result; }
/// <seealso cref="Genetibase.NuGenHL7.protocol.Initiator.sendAndReceive(Genetibase.NuGenHL7.model.Message)"> /// </seealso> public virtual Message sendAndReceive(Message theMessage) { Terser t = new Terser(theMessage); System.String appAckNeeded = t.get_Renamed("/MSH-16"); System.String msgId = t.get_Renamed("/MSH-10"); System.String messageText = Parser.encode(theMessage); System.Collections.IDictionary metadata = getMetadata(theMessage); NuGenTransportable out_Renamed = new NuGenTransportableImpl(messageText, metadata); if (needAck(appAckNeeded)) { myProcessor.reserve(msgId, ReceiveTimeout); } myProcessor.send(out_Renamed, MaxRetries, RetryInterval); Message in_Renamed = null; if (needAck(appAckNeeded)) { NuGenTransportable received = myProcessor.receive(msgId, ReceiveTimeout); if (received != null && received.Message != null) { in_Renamed = Parser.parse(received.Message); } } return(in_Renamed); }
/// <summary> Returns the Applications that has been registered to handle /// messages of the type and trigger event of the given message, or null if /// there are none. /// </summary> private NuGenApplication getMatchingApplication(Message message) { Terser t = new Terser(message); System.String messageType = t.get_Renamed("/MSH-9-1"); System.String triggerEvent = t.get_Renamed("/MSH-9-2"); return(this.getMatchingApplication(messageType, triggerEvent)); }
public virtual void Run() { try { //get message ID System.String ID = MessageIDGenerator.Instance.NewID; Message out_Renamed = parser.parse(outText); Terser tOut = new Terser(out_Renamed); tOut.set_Renamed("/MSH-10", ID); //send, get response Message in_Renamed = initiator.sendAndReceive(out_Renamed); //get ACK ID Terser tIn = new Terser(in_Renamed); System.String ackID = tIn.get_Renamed("/MSA-2"); if (ID.Equals(ackID)) { System.Console.Out.WriteLine("OK - ack ID matches"); } else { throw new System.SystemException("Ack ID for message " + ID + " is " + ackID); } } catch (System.Exception e) { SupportClass.WriteStackTrace(e, Console.Error); } }
/// <param name="message">a parsed message to validate (note that MSH-9-1 and MSH-9-2 must be valued) /// </param> /// <returns> true if the message is OK /// </returns> /// <throws> HL7Exception if there is at least one error and this validator is set to fail on errors </throws> public virtual bool validate(Message message) { Terser t = new Terser(message); NuGenMessageRule[] rules = myContext.getMessageRules(message.Version, t.get_Renamed("MSH-9-1"), t.get_Renamed("MSH-9-2")); NuGenValidationException toThrow = null; bool result = true; for (int i = 0; i < rules.Length; i++) { NuGenValidationException[] ex = rules[i].test(message); for (int j = 0; j < ex.Length; j++) { result = false; if (failOnError && toThrow == null) { toThrow = ex[j]; } } } if (toThrow != null) { throw new NuGenHL7Exception("Invalid message", toThrow); } return(result); }
/// <summary> Formats a Message object into an HL7 message string using this parser's /// default encoding ("VB"). /// </summary> /// <throws> HL7Exception if the data fields in the message do not permit encoding </throws> /// <summary> (e.g. required fields are null) /// </summary> protected internal override System.String doEncode(Message source) { //get encoding characters ... Segment msh = (Segment)source.get_Renamed("MSH"); System.String fieldSepString = Terser.get_Renamed(msh, 1, 0, 1, 1); if (fieldSepString == null) { throw new NuGenHL7Exception("Can't encode message: MSH-1 (field separator) is missing"); } char fieldSep = '|'; if (fieldSepString != null && fieldSepString.Length > 0) { fieldSep = fieldSepString[0]; } System.String encCharString = Terser.get_Renamed(msh, 2, 0, 1, 1); if (encCharString == null) { throw new NuGenHL7Exception("Can't encode message: MSH-2 (encoding characters) is missing"); } if (encCharString.Length != 4) { throw new NuGenHL7Exception("Encoding characters '" + encCharString + "' invalid -- must be 4 characters", NuGenHL7Exception.DATA_TYPE_ERROR); } NuGenEncodingCharacters en = new NuGenEncodingCharacters(fieldSep, encCharString); //pass down to group encoding method which will operate recursively on children ... return(encode((Group)source, en)); }
private System.String[] getDeclaredProfileIDs(Message theMessage) { Terser t = new Terser(theMessage); bool noMore = false; int c = 0; System.Collections.ArrayList declaredProfiles = new System.Collections.ArrayList(8); while (!noMore) { System.String path = "MSH-21(" + c++ + ")"; System.String idRep = t.get_Renamed(path); //FIXME fails if empty rep precedes full rep ... should add getAll() to Terser and use that if (idRep == null || idRep.Equals("")) { noMore = true; } else { declaredProfiles.Add(idRep); } } String[] retVal = new String[declaredProfiles.Count]; declaredProfiles.CopyTo(retVal); return(retVal); }
/// <summary> Sends a message to a responder system, receives the reply, and /// returns the reply as a Message object. This method is thread-safe - multiple /// threads can share an Initiator and call this method. Responses are returned to /// the calling thread on the basis of message ID. /// </summary> public virtual Message sendAndReceive(Message out_Renamed) { if (out_Renamed == null) { throw new NuGenHL7Exception("Can't encode null message", NuGenHL7Exception.REQUIRED_FIELD_MISSING); } //register message with response Receiver(s) (by message ID) Terser t = new Terser(out_Renamed); System.String messID = t.get_Renamed("/MSH-10"); NuGenMessageReceipt mr = this.conn.reserveResponse(messID); //log and send message System.String outbound = conn.Parser.encode(out_Renamed); try { this.conn.SendWriter.writeMessage(outbound); } catch (System.IO.IOException e) { conn.close(); throw e; } //wait for response bool done = false; Message response = null; long startTime = (System.DateTime.Now.Ticks - 621355968000000000) / 10000; while (!done) { lock (mr) { try { System.Threading.Monitor.Wait(mr, TimeSpan.FromMilliseconds(500)); //if it comes, notifyAll() will be called } catch (System.Threading.ThreadInterruptedException) { } if (mr.Message != null) { //get message from receipt System.String inbound = mr.Message; //parse message response = conn.Parser.parse(inbound); done = true; } //if ((System.DateTime.Now.Ticks - 621355968000000000) / 10000 > startTime + timeoutMillis) //throw new HL7Exception("Timeout waiting for response to message with control ID '" + messID); } } return(response); }
/// <summary> Logs the given exception and creates an error message to send to the /// remote system. /// /// </summary> /// <param name="encoding">The encoding for the error message. If <code>null</code>, uses default encoding /// </param> public static System.String logAndMakeErrorMessage(System.Exception e, Segment inHeader, Parser p, System.String encoding) { // create error message ... System.String errorMessage = null; try { Message out_Renamed = NuGenDefaultApplication.makeACK(inHeader); Terser t = new Terser(out_Renamed); //copy required data from incoming message ... try { t.set_Renamed("/MSH-10", MessageIDGenerator.Instance.NewID); } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("Problem creating error message ID: " + ioe.Message); } //populate MSA ... t.set_Renamed("/MSA-1", "AE"); //should this come from HL7Exception constructor? t.set_Renamed("/MSA-2", Terser.get_Renamed(inHeader, 10, 0, 1, 1)); System.String excepMessage = e.Message; if (excepMessage != null) { t.set_Renamed("/MSA-3", excepMessage.Substring(0, (System.Math.Min(80, excepMessage.Length)) - (0))); } /* Some earlier ACKs don't have ERRs, but I think we'll change this within HAPI * so that there is a single ACK for each version (with an ERR). */ //see if it's an HL7Exception (so we can get specific information) ... if (e.GetType().Equals(typeof(NuGenHL7Exception))) { Segment err = (Segment)out_Renamed.get_Renamed("ERR"); ((NuGenHL7Exception)e).populate(err); } else { t.set_Renamed("/ERR-1-4-1", "207"); t.set_Renamed("/ERR-1-4-2", "Application Internal Error"); t.set_Renamed("/ERR-1-4-3", "HL70357"); } if (encoding != null) { errorMessage = p.encode(out_Renamed, encoding); } else { errorMessage = p.encode(out_Renamed); } } catch (System.IO.IOException ioe) { throw new NuGenHL7Exception("IOException creating error response message: " + ioe.Message, NuGenHL7Exception.APPLICATION_INTERNAL_ERROR); } return(errorMessage); }
/// <param name="theMessage">a message from which to extract fields /// </param> /// <param name="theTerserPaths">a list of paths to desired fields, in the /// form required by <code>Terser</code>. /// </param> /// <returns> a Map from Terser paths to field values /// </returns> public static System.Collections.IDictionary getFields(Message theMessage, System.Collections.IList theTerserPaths) { System.Collections.IDictionary fields = new System.Collections.Hashtable(); Terser terser = new Terser(theMessage); for (int i = 0; i < theTerserPaths.Count; i++) { System.String path = (System.String) theTerserPaths[i]; System.String fieldValue = terser.get_Renamed(path); fields[path] = fieldValue; } return fields; }
/// <param name="theMessage">a message from which to extract fields /// </param> /// <param name="theTerserPaths">a list of paths to desired fields, in the /// form required by <code>Terser</code>. /// </param> /// <returns> a Map from Terser paths to field values /// </returns> public static System.Collections.IDictionary getFields(Message theMessage, System.Collections.IList theTerserPaths) { System.Collections.IDictionary fields = new System.Collections.Hashtable(); Terser terser = new Terser(theMessage); for (int i = 0; i < theTerserPaths.Count; i++) { System.String path = (System.String)theTerserPaths[i]; System.String fieldValue = terser.get_Renamed(path); fields[path] = fieldValue; } return(fields); }
/// <summary> Returns the first Application that has been bound to messages of this type. </summary> private NuGenReceivingApplication findApplication(Message theMessage) { Terser t = new Terser(theMessage); AppRoutingData msgData = new NuGenAppRoutingDataImpl(t.get_Renamed("/MSH-9-1"), t.get_Renamed("/MSH-9-2"), t.get_Renamed("/MSH-11-1"), t.get_Renamed("/MSH-12")); NuGenReceivingApplication app = findDestination(msgData); //have to send back an application reject if no apps available to process if (app == null) { app = new NuGenAppWrapper(new DefaultApplication()); } return(app); }
/// <summary> Creates an ACK message with the minimum required information from an inbound message. /// Optional fields can be filled in afterwards, before the message is returned. Pleaase /// note that MSH-10, the outbound message control ID, is also set using the class /// <code>Genetibase.NuGenHL7.util.MessageIDGenerator</code>. Also note that the ACK messages returned /// is the same version as the version stated in the inbound MSH if there is a generic ACK for that /// version, otherwise a version 2.4 ACK is returned. MSA-1 is set to AA by default. /// /// </summary> /// <param name="inboundHeader">the MSH segment if the inbound message /// </param> /// <throws> IOException if there is a problem reading or writing the message ID file </throws> /// <throws> DataTypeException if there is a problem setting ACK values </throws> public static Message makeACK(Segment inboundHeader) { if (!inboundHeader.getName().Equals("MSH")) { throw new NuGenHL7Exception("Need an MSH segment to create a response ACK (got " + inboundHeader.getName() + ")"); } //make ACK of correct version System.String version = null; try { version = Terser.get_Renamed(inboundHeader, 12, 0, 1, 1); } catch (NuGenHL7Exception) { /* proceed with null */ } if (version == null) { version = "2.4"; } System.String ackClassName = SourceGenerator.getVersionPackageName(version) + "message.ACK"; Message out_Renamed = null; try { System.Type ackClass = System.Type.GetType(ackClassName); out_Renamed = (Message)System.Activator.CreateInstance(ackClass); } catch (System.Exception e) { throw new NuGenHL7Exception("Can't instantiate ACK of class " + ackClassName + ": " + e.GetType().FullName); } Terser terser = new Terser(out_Renamed); //populate outbound MSH using data from inbound message ... Segment outHeader = (Segment)out_Renamed.get_Renamed("MSH"); fillResponseHeader(inboundHeader, outHeader); terser.set_Renamed("/MSH-9", "ACK"); terser.set_Renamed("/MSH-12", version); terser.set_Renamed("/MSA-1", "AA"); terser.set_Renamed("/MSA-2", Genetibase.NuGenHL7.util.NuGenTerser.get_Renamed(inboundHeader, 10, 0, 1, 1)); return(out_Renamed); }
/// <seealso cref="Validator.validate"> /// </seealso> public virtual NuGenHL7Exception[] validate(Message message, StaticDef profile) { System.Collections.ArrayList exList = new System.Collections.ArrayList(20); Terser t = new Terser(message); //check msg type, event type, msg struct ID System.String msgType = t.get_Renamed("/MSH-9-1"); if (!msgType.Equals(profile.MsgType)) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Message type " + msgType + " doesn't match profile type of " + profile.MsgType); exList.Add(e); } System.String evType = t.get_Renamed("/MSH-9-2"); if (!evType.Equals(profile.EventType) && !profile.EventType.ToUpper().Equals("ALL".ToUpper())) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Event type " + evType + " doesn't match profile type of " + profile.EventType); exList.Add(e); } System.String msgStruct = t.get_Renamed("/MSH-9-3"); if (msgStruct == null || !msgStruct.Equals(profile.MsgStructID)) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Message structure " + msgStruct + " doesn't match profile type of " + profile.MsgStructID); exList.Add(e); } System.Exception[] childExceptions; childExceptions = testGroup(message, profile, profile.Identifier); for (int i = 0; i < childExceptions.Length; i++) { exList.Add(childExceptions[i]); } return toArray(exList); }
/// <seealso cref="Validator.validate"> /// </seealso> public virtual NuGenHL7Exception[] validate(Message message, StaticDef profile) { System.Collections.ArrayList exList = new System.Collections.ArrayList(20); Terser t = new Terser(message); //check msg type, event type, msg struct ID System.String msgType = t.get_Renamed("/MSH-9-1"); if (!msgType.Equals(profile.MsgType)) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Message type " + msgType + " doesn't match profile type of " + profile.MsgType); exList.Add(e); } System.String evType = t.get_Renamed("/MSH-9-2"); if (!evType.Equals(profile.EventType) && !profile.EventType.ToUpper().Equals("ALL".ToUpper())) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Event type " + evType + " doesn't match profile type of " + profile.EventType); exList.Add(e); } System.String msgStruct = t.get_Renamed("/MSH-9-3"); if (msgStruct == null || !msgStruct.Equals(profile.MsgStructID)) { NuGenHL7Exception e = new NuGenProfileNotFollowedException("Message structure " + msgStruct + " doesn't match profile type of " + profile.MsgStructID); exList.Add(e); } System.Exception[] childExceptions; childExceptions = testGroup(message, profile, profile.Identifier); for (int i = 0; i < childExceptions.Length; i++) { exList.Add(childExceptions[i]); } return(toArray(exList)); }
private System.Collections.IDictionary getMetadata(Message theMessage) { System.Collections.IDictionary md = new System.Collections.Hashtable(); Terser t = new Terser(theMessage); //snapshot so concurrent changes won't break our iteration Object[] fields = new Object[MetadataFields.Count]; MetadataFields.CopyTo(fields, 0); for (int i = 0; i < fields.Length; i++) { System.String field = fields[i].ToString(); System.String val = t.get_Renamed(field); md[field] = val; } return(md); }
/// <summary> Processes an incoming message string and returns the response message string. /// Message processing consists of parsing the message, finding an appropriate /// Application and processing the message with it, and encoding the response. /// Applications are chosen from among those registered using /// <code>bindApplication</code>. /// /// </summary> /// <returns> {text, charset} /// </returns> private System.String[] processMessage(System.String incomingMessageString, System.Collections.IDictionary theMetadata) { ; Message incomingMessageObject = null; System.String outgoingMessageString = null; System.String outgoingMessageCharset = null; try { incomingMessageObject = myParser.parse(incomingMessageString); } catch (NuGenHL7Exception e) { outgoingMessageString = Responder.logAndMakeErrorMessage(e, myParser.getCriticalResponseData(incomingMessageString), myParser, myParser.getEncoding(incomingMessageString)); } if (outgoingMessageString == null) { try { //message validation (in terms of optionality, cardinality) would go here *** NuGenReceivingApplication app = findApplication(incomingMessageObject); theMetadata[RAW_MESSAGE_KEY] = incomingMessageString; Message response = app.processMessage(incomingMessageObject, theMetadata); //Here we explicitly use the same encoding as that of the inbound message - this is important with GenericParser, which might use a different encoding by default outgoingMessageString = myParser.encode(response, myParser.getEncoding(incomingMessageString)); Terser t = new Terser(response); outgoingMessageCharset = t.get_Renamed("MSH-18"); } catch (System.Exception e) { outgoingMessageString = Responder.logAndMakeErrorMessage(e, (Segment)incomingMessageObject.get_Renamed("MSH"), myParser, myParser.getEncoding(incomingMessageString)); } } return(new System.String[] { outgoingMessageString, outgoingMessageCharset }); }
/// <summary> Populates certain required fields in a response message header, using /// information from the corresponding inbound message. The current time is /// used for the message time field, and <code>MessageIDGenerator</code> is /// used to create a unique message ID. Version and message type fields are /// not populated. /// </summary> public static void fillResponseHeader(Segment inbound, Segment outbound) { if (!inbound.getName().Equals("MSH") || !outbound.getName().Equals("MSH")) { throw new NuGenHL7Exception("Need MSH segments. Got " + inbound.getName() + " and " + outbound.getName()); } //get MSH data from incoming message ... System.String encChars = Terser.get_Renamed(inbound, 2, 0, 1, 1); System.String fieldSep = Terser.get_Renamed(inbound, 1, 0, 1, 1); System.String procID = Terser.get_Renamed(inbound, 11, 0, 1, 1); //populate outbound MSH using data from inbound message ... Terser.set_Renamed(outbound, 2, 0, 1, 1, encChars); Terser.set_Renamed(outbound, 1, 0, 1, 1, fieldSep); System.Globalization.GregorianCalendar now = new System.Globalization.GregorianCalendar(); SupportClass.CalendarManager.manager.SetDateTime(now, System.DateTime.Now); Terser.set_Renamed(outbound, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now)); Terser.set_Renamed(outbound, 10, 0, 1, 1, MessageIDGenerator.Instance.NewID); Terser.set_Renamed(outbound, 11, 0, 1, 1, procID); }
/// <summary> Returns the first Application that has been bound to messages of this type. </summary> private NuGenReceivingApplication findApplication(Message theMessage) { Terser t = new Terser(theMessage); AppRoutingData msgData = new NuGenAppRoutingDataImpl(t.get_Renamed("/MSH-9-1"), t.get_Renamed("/MSH-9-2"), t.get_Renamed("/MSH-11-1"), t.get_Renamed("/MSH-12")); NuGenReceivingApplication app = findDestination(msgData); //have to send back an application reject if no apps available to process if (app == null) app = new NuGenAppWrapper(new DefaultApplication()); return app; }
/// <summary> Sends a message to a responder system, receives the reply, and /// returns the reply as a Message object. This method is thread-safe - multiple /// threads can share an Initiator and call this method. Responses are returned to /// the calling thread on the basis of message ID. /// </summary> public virtual Message sendAndReceive(Message out_Renamed) { if (out_Renamed == null) throw new NuGenHL7Exception("Can't encode null message", NuGenHL7Exception.REQUIRED_FIELD_MISSING); //register message with response Receiver(s) (by message ID) Terser t = new Terser(out_Renamed); System.String messID = t.get_Renamed("/MSH-10"); NuGenMessageReceipt mr = this.conn.reserveResponse(messID); //log and send message System.String outbound = conn.Parser.encode(out_Renamed); try { this.conn.SendWriter.writeMessage(outbound); } catch (System.IO.IOException e) { conn.close(); throw e; } //wait for response bool done = false; Message response = null; long startTime = (System.DateTime.Now.Ticks - 621355968000000000) / 10000; while (!done) { lock (mr) { try { System.Threading.Monitor.Wait(mr, TimeSpan.FromMilliseconds(500)); //if it comes, notifyAll() will be called } catch (System.Threading.ThreadInterruptedException) { } if (mr.Message != null) { //get message from receipt System.String inbound = mr.Message; //parse message response = conn.Parser.parse(inbound); done = true; } //if ((System.DateTime.Now.Ticks - 621355968000000000) / 10000 > startTime + timeoutMillis) //throw new HL7Exception("Timeout waiting for response to message with control ID '" + messID); } } return response; }
/// <summary> Processes an incoming message string and returns the response message string. /// Message processing consists of parsing the message, finding an appropriate /// Application and processing the message with it, and encoding the response. /// Applications are chosen from among those registered using /// <code>bindApplication</code>. /// /// </summary> /// <returns> {text, charset} /// </returns> private System.String[] processMessage(System.String incomingMessageString, System.Collections.IDictionary theMetadata) {; Message incomingMessageObject = null; System.String outgoingMessageString = null; System.String outgoingMessageCharset = null; try { incomingMessageObject = myParser.parse(incomingMessageString); } catch (NuGenHL7Exception e) { outgoingMessageString = Responder.logAndMakeErrorMessage(e, myParser.getCriticalResponseData(incomingMessageString), myParser, myParser.getEncoding(incomingMessageString)); } if (outgoingMessageString == null) { try { //message validation (in terms of optionality, cardinality) would go here *** NuGenReceivingApplication app = findApplication(incomingMessageObject); theMetadata[RAW_MESSAGE_KEY] = incomingMessageString; Message response = app.processMessage(incomingMessageObject, theMetadata); //Here we explicitly use the same encoding as that of the inbound message - this is important with GenericParser, which might use a different encoding by default outgoingMessageString = myParser.encode(response, myParser.getEncoding(incomingMessageString)); Terser t = new Terser(response); outgoingMessageCharset = t.get_Renamed("MSH-18"); } catch (System.Exception e) { outgoingMessageString = Responder.logAndMakeErrorMessage(e, (Segment) incomingMessageObject.get_Renamed("MSH"), myParser, myParser.getEncoding(incomingMessageString)); } } return new System.String[]{outgoingMessageString, outgoingMessageCharset}; }
/// <summary> Returns the Applications that has been registered to handle /// messages of the type and trigger event of the given message, or null if /// there are none. /// </summary> private NuGenApplication getMatchingApplication(Message message) { Terser t = new Terser(message); System.String messageType = t.get_Renamed("/MSH-9-1"); System.String triggerEvent = t.get_Renamed("/MSH-9-2"); return this.getMatchingApplication(messageType, triggerEvent); }
private System.String[] getDeclaredProfileIDs(Message theMessage) { Terser t = new Terser(theMessage); bool noMore = false; int c = 0; System.Collections.ArrayList declaredProfiles = new System.Collections.ArrayList(8); while (!noMore) { System.String path = "MSH-21(" + c++ + ")"; System.String idRep = t.get_Renamed(path); //FIXME fails if empty rep precedes full rep ... should add getAll() to Terser and use that if (idRep == null || idRep.Equals("")) { noMore = true; } else { declaredProfiles.Add(idRep); } } String[] retVal = new String[declaredProfiles.Count]; declaredProfiles.CopyTo(retVal); return retVal; }