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    package ca.uhn.hl7v2.app;
028    
029    import java.util.HashMap;
030    import java.util.Map;
031    
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    import ca.uhn.hl7v2.HL7Exception;
036    import ca.uhn.hl7v2.model.Message;
037    import ca.uhn.hl7v2.util.Terser;
038    
039    /**
040     * Routes messages to various Applications based on message type and trigger
041     * event. The router is told which Application to which to route various
042     * messages by calling the method <code>registerApplication(...)</code>.
043     * 
044     * @author Bryan Tripp
045     */
046    public class MessageTypeRouter implements Application,
047                    ApplicationExceptionHandler {
048    
049            private static final Logger log = LoggerFactory
050                            .getLogger(MessageTypeRouter.class);
051            private Map<String, Application> apps;
052    
053            /** Creates a new instance of MessageTypeRouter */
054            public MessageTypeRouter() {
055                    apps = new HashMap<String, Application>(20);
056            }
057    
058            /**
059             * Returns true if at least one application has been registered to accept
060             * this type of message. Applications are registered using
061             * <code>registerApplication(...)</code>.
062             */
063            public boolean canProcess(Message in) {
064                    try {
065                            Application match = getMatchingApplication(in);
066                            return match != null ? match.canProcess(in) : false;
067                    } catch (Exception e) {
068                            return false;
069                    }
070            }
071    
072            /**
073             * Forwards the given message to any Applications that have been registered
074             * to accept messages of that type and trigger event.
075             * 
076             * @throws ApplicationException
077             *             if no such Applications are registered, or if the underlying
078             *             Application throws this exception during processing.
079             */
080            public Message processMessage(Message in) throws ApplicationException {
081                    Message out;
082                    try {
083                            Application matchingApp = this.getMatchingApplication(in);
084                            out = matchingApp.processMessage(in);
085                    } catch (HL7Exception e) {
086                            throw new ApplicationException("Error internally routing message: "
087                                            + e.toString(), e);
088                    }
089                    return out;
090            }
091    
092            /**
093             * Forwards the given exception to all Applications.
094             */
095            public String processException(String incomingMessage,
096                            String outgoingMessage, Exception e) throws HL7Exception {
097                    String outgoingMessageResult = outgoingMessage;
098                    for (Application app : apps.values()) {
099                            if (app instanceof ApplicationExceptionHandler) {
100                                    ApplicationExceptionHandler aeh = (ApplicationExceptionHandler) app;
101                                    outgoingMessageResult = aeh.processException(incomingMessage,
102                                                    outgoingMessageResult, e);
103                            }
104                    }
105                    return outgoingMessageResult;
106            }
107    
108            /**
109             * Registers the given application to handle messages corresponding to the
110             * given type and trigger event. Only one application can be registered for
111             * a given message type and trigger event combination. A repeated
112             * registration for a particular combination of type and trigger event
113             * over-writes the previous one. Use "*" as a wildcard (e.g.
114             * registerApplication("ADT", "*", myApp) would register your app for all
115             * ADT messages).
116             */
117            public synchronized void registerApplication(String messageType,
118                            String triggerEvent, Application handler) {
119                    this.apps.put(getKey(messageType, triggerEvent), handler);
120                    log.info("{} registered to handle {}^{} messages", new Object[] {
121                                    handler.getClass().getName(), messageType, triggerEvent });
122            }
123    
124            /**
125             * Returns the Applications that has been registered to handle messages of
126             * the type and trigger event of the given message, or null if there are
127             * none.
128             */
129            private Application getMatchingApplication(Message message)
130                            throws HL7Exception {
131                    Terser t = new Terser(message);
132                    String messageType = t.get("/MSH-9-1");
133                    String triggerEvent = t.get("/MSH-9-2");
134                    return this.getMatchingApplication(messageType, triggerEvent);
135            }
136    
137            /**
138             * Returns the Applications that has been registered to handle messages of
139             * the given type and trigger event, or null if there are none. If there is
140             * not an exact match, wildcards ("*") are tried as well.
141             */
142            private synchronized Application getMatchingApplication(String messageType,
143                            String triggerEvent) {
144                    Application matchingApp = null;
145                    Application o = this.apps.get(getKey(messageType, triggerEvent));
146                    if (o == null)
147                            o = this.apps.get(getKey(messageType, "*"));
148                    if (o == null)
149                            o = this.apps.get(getKey("*", triggerEvent));
150                    if (o == null)
151                            o = this.apps.get(getKey("*", "*"));
152                    if (o != null)
153                            matchingApp = o;
154                    return matchingApp;
155            }
156    
157            /**
158             * Creates reproducible hash key.
159             */
160            private String getKey(String messageType, String triggerEvent) {
161                    // create hash key string by concatenating type and trigger event
162                    return messageType + "|" + triggerEvent;
163            }
164    
165    }