001    package ca.uhn.hl7v2.app;
002    
003    import java.util.HashMap;
004    
005    import ca.uhn.hl7v2.model.Message;
006    import ca.uhn.hl7v2.HL7Exception;
007    import ca.uhn.hl7v2.util.Terser;
008    import ca.uhn.log.HapiLog;
009    import ca.uhn.log.HapiLogFactory;
010    
011    /**
012     * Routes messages to various Applications based on message type and trigger event.
013     * The router is told which Application to which to route various messages by calling
014     * the method <code>registerApplication(...)</code>.
015     * @author Bryan Tripp
016     */
017    public class MessageTypeRouter implements Application {
018        
019        private static final HapiLog log = HapiLogFactory.getHapiLog(MessageTypeRouter.class);
020        private HashMap apps;
021        
022        /** Creates a new instance of MessageTypeRouter */
023        public MessageTypeRouter() {
024            apps = new HashMap(20);
025        }
026        
027        /**
028         * Returns true if at least one application has been registered to accept this
029         * type of message.  Applications are registered using <code>registerApplication(...)</code>.
030         */
031        public boolean canProcess(Message in) {
032            boolean can = false;
033            try {
034                Application matches = this.getMatchingApplication(in);
035                if (matches != null) can = true;
036            } catch (HL7Exception e) { 
037                can = false;
038            }
039            return can;
040        }
041        
042        /**
043         * Forwards the given message to any Applications that have been registered to
044         * accept messages of that type and trigger event.
045         * @throws ApplicationException if no such Applications are registered, or if
046         *      the underlying Application throws this exception during processing.
047         */
048        public Message processMessage(Message in) throws ApplicationException {
049            Message out;
050            try {
051                Application matchingApp = this.getMatchingApplication(in);
052                out = matchingApp.processMessage(in);
053            } catch (HL7Exception e) {
054                throw new ApplicationException("Error internally routing message: " + e.toString(), e);
055            }
056            return out;
057        }
058        
059        /**
060         * Registers the given application to handle messages corresponding to the given type
061         * and trigger event.  Only one application can be registered for a given message type
062         * and trigger event combination.  A repeated registration for a particular combination 
063         * of type and trigger event over-writes the previous one.  Use "*" as a wildcard (e.g. 
064         * registerApplication("ADT", "*", myApp) would register your app for all ADT messages).
065         */
066        public synchronized void registerApplication(String messageType, String triggerEvent, Application handler) {
067            this.apps.put(getKey(messageType, triggerEvent), handler);
068    
069            //status message
070            StringBuffer buf = new StringBuffer();
071            buf.append(handler.getClass().getName());           
072            buf.append(" registered to handle ");
073            buf.append(messageType);
074            buf.append("^");
075            buf.append(triggerEvent);
076            buf.append(" messages");
077            log.info(buf.toString());
078        }
079    
080        /** 
081         * Returns the Applications that has been registered to handle 
082         * messages of the type and trigger event of the given message, or null if 
083         * there are none. 
084         */
085        private Application getMatchingApplication(Message message) throws HL7Exception {
086            Terser t = new Terser(message);
087            String messageType = t.get("/MSH-9-1");
088            String triggerEvent = t.get("/MSH-9-2");
089            return this.getMatchingApplication(messageType, triggerEvent);        
090        }
091        
092        /**
093         * Returns the Applications that has been registered to handle 
094         * messages of the given type and trigger event, or null if 
095         * there are none.  If there is not an exact match, wildcards 
096         * ("*") are tried as well.  
097         */
098        private synchronized Application getMatchingApplication(String messageType, String triggerEvent) {
099            Application matchingApp = null;
100            Object o = this.apps.get(getKey(messageType, triggerEvent));
101            if (o == null) o = this.apps.get(getKey(messageType, "*"));
102            if (o == null) o = this.apps.get(getKey("*", triggerEvent));
103            if (o == null) o = this.apps.get(getKey("*", "*"));        
104            if (o != null) matchingApp = (Application)o;
105            return matchingApp;
106        }
107        
108        /**
109         * Creates reproducible hash key. 
110         */
111        private String getKey(String messageType, String triggerEvent) {
112            //create hash key string by concatenating type and trigger event
113            return messageType + "|" + triggerEvent;
114        }
115    }