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 "JMSTransport.java".  Description: 
010    "A TransportLayer that exchanges messages through JMS destinations." 
011    
012    The Initial Developer of the Original Code is University Health Network. Copyright (C) 
013    2004.  All Rights Reserved. 
014    
015    Contributor(s): ______________________________________. 
016    
017    Alternatively, the contents of this file may be used under the terms of the 
018    GNU General Public License (the  ?GPL?), in which case the provisions of the GPL are 
019    applicable instead of those above.  If you wish to allow use of your version of this 
020    file only under the terms of the GPL and not to allow others to use your version 
021    of this file under the MPL, indicate your decision by deleting  the provisions above 
022    and replace  them with the notice and other provisions required by the GPL License.  
023    If you do not delete the provisions above, a recipient may use your version of 
024    this file under either the MPL or the GPL. 
025    
026    */
027    package ca.uhn.hl7v2.protocol.impl;
028    
029    import java.util.HashMap;
030    import java.util.Iterator;
031    import java.util.Map;
032    
033    import javax.jms.Destination;
034    import javax.jms.JMSException;
035    import javax.jms.Message;
036    import javax.jms.Queue;
037    import javax.jms.TextMessage;
038    import javax.jms.Topic;
039    
040    import ca.uhn.hl7v2.protocol.JMSDestination;
041    import ca.uhn.hl7v2.protocol.TransportException;
042    import ca.uhn.hl7v2.protocol.TransportLayer;
043    import ca.uhn.hl7v2.protocol.Transportable;
044    import ca.uhn.log.HapiLog;
045    import ca.uhn.log.HapiLogFactory;
046    
047    /**
048     * A <code>TransportLayer</code> that exchanges messages through JMS destinations.   
049     * 
050     * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
051     * @version $Revision: 1.1 $ updated on $Date: 2007/02/19 02:24:26 $ by $Author: jamesagnew $
052     */
053    public class JMSTransport extends AbstractTransport implements TransportLayer {
054    
055        private static final HapiLog log = HapiLogFactory.getHapiLog(URLTransport.class);    
056    
057        public static final String INBOUND_DESTINATION_NAME_KEY = "INBOUND_DESTINATION_NAME";
058        public static final String INBOUND_CLIENT_ID_KEY = "INBOUND_CLIENT_ID";
059        public static final String INBOUND_CONNECTION_METADATA_KEY = "INBOUND_CONNECTION_METADATA";
060        public static final String OUTBOUND_DESTINATION_NAME_KEY = "OUTBOUND_DESTINATION_NAME";
061        public static final String OUTBOUND_CLIENT_ID_KEY = "OUTBOUND_CLIENT_ID";
062        public static final String OUTBOUND_CONNECTION_METADATA_KEY = "OUTBOUND_CONNECTION_METADATA";
063         
064        private JMSDestination myInbound;
065        private JMSDestination myOutbound;
066        private Map myMetadata;
067        
068        /**
069         * @param theInboundDestination wrapper around the Queue or Topic to which outgoing messages 
070         *      are to be sent
071         * @param theOutboundDestination wrapper around the Queue or Topic from which incoming messages
072         *      are to be retrieved
073         */
074        public JMSTransport(JMSDestination theInboundDestination, JMSDestination theOutboundDestination) {
075            myInbound = theInboundDestination;
076            myOutbound = theOutboundDestination;
077        }
078        
079        /**
080         * @param theConnection JMS connection over which messages are exchanged 
081         * @param theDestination JMS destination to which messages are produced and 
082         *      from which messages are consumed 
083         */
084        public JMSTransport() {
085            myMetadata = makeMetadata();
086        }
087        
088        /** 
089         * Sets common metadata on the basis of connection and destination.  
090         */ 
091        private Map makeMetadata() {
092            Map md = new HashMap();
093            try {
094                md.put(INBOUND_CLIENT_ID_KEY, myInbound.getConnection().getClientID());
095                md.put(INBOUND_CONNECTION_METADATA_KEY, myInbound.getConnection().getMetaData());
096                md.put(INBOUND_DESTINATION_NAME_KEY, myInbound.getName());
097                md.put(OUTBOUND_CLIENT_ID_KEY, myOutbound.getConnection().getClientID());
098                md.put(OUTBOUND_CONNECTION_METADATA_KEY, myOutbound.getConnection().getMetaData());
099                md.put(OUTBOUND_DESTINATION_NAME_KEY, myOutbound.getName());
100            } catch (JMSException e) {
101                log.error("Error setting JMSTransport metadata", e);
102            }
103            return md;
104        }
105        
106        /**
107         * @param theDestination a Queue or Topic 
108         * @return either getQueueName() or getTopicName() 
109         */
110        private static String getName(Destination theDestination) throws JMSException {
111            String name = null;
112            
113            if (theDestination instanceof Queue) {
114                name = ((Queue) theDestination).getQueueName();
115            } else if (theDestination instanceof Topic) {
116                name = ((Topic) theDestination).getTopicName();
117            } else {
118                throw new IllegalArgumentException("We don't support Destinations of type " 
119                    + theDestination.getClass().getName());
120            }
121            return name;
122        }
123    
124        /** 
125         * @see ca.uhn.hl7v2.protocol.Transport#doSend(ca.uhn.hl7v2.protocol.Transportable)
126         */
127        public void doSend(Transportable theMessage) throws TransportException {
128            try {            
129                Message message = toMessage(theMessage);
130                myOutbound.send(message);
131            } catch (JMSException e) {
132                throw new TransportException(e);
133            }
134        } 
135        
136        /**
137         * Fills a JMS message object with text and metadata from the given 
138         * <code>Transportable</code>.  The default implementation obtains a 
139         * the Message from getMessage(), and expects this to be a TextMessage.   
140         * Override this method if you want to use a different message type.  
141         * 
142         * @param theSource a Transportable from which to obtain data for filling the 
143         *      given Message
144         * @return a Message containing data from the given Transportable
145         */
146        protected Message toMessage(Transportable theSource) throws TransportException {
147            Message message;
148            try {
149                message = myOutbound.createMessage();
150             
151                if ( !(message instanceof TextMessage)) {
152                    throw new TransportException("This implementation expects getMessage() to return "
153                        + " a TextMessage.  Override this method if another message type is to be used");
154                }
155    
156                ((TextMessage) message).setText(theSource.getMessage());
157            
158                Iterator it = theSource.getMetadata().keySet().iterator();
159                while (it.hasNext()) {
160                    Object key = it.next();
161                    Object val = theSource.getMetadata().get(key);
162                    message.setObjectProperty(key.toString(), val);
163                }
164            } catch (JMSException e) {
165                throw new TransportException(e);
166            }       
167            
168            return message;
169        }
170        
171        /**
172         * Copies data from the given Message into a Transportable.  The default 
173         * implementation expects a TextMessage, but this can be overridden.  
174         * 
175         * @param theMessage a JMS Message from which to obtain data  
176         * @return a Transportable containing data from the given Message
177         */
178        protected Transportable toTransportable(Message theMessage) throws TransportException {
179            if ( !(theMessage instanceof TextMessage)) {
180                throw new TransportException("This implementation expects getMessage() to return "
181                    + " a TextMessage.  Override this method if another message type is to be used");
182            }
183            
184            Transportable result = null;
185            try {
186                String text = ((TextMessage) theMessage).getText();
187                result = new TransportableImpl(text);
188                result.getMetadata().putAll(getCommonMetadata());
189            } catch (JMSException e) {
190                throw new TransportException(e);
191            }
192    
193            return result;
194        }
195        
196        /** 
197         * @see ca.uhn.hl7v2.protocol.AbstractTransport#doReceive()
198         */
199        public Transportable doReceive() throws TransportException {
200            Transportable result = null;
201            try {
202                Message message = myInbound.receive();
203                result = toTransportable(message);
204            } catch (JMSException e) {
205                throw new TransportException(e);            
206            }
207            return result;
208        }
209    
210        /** 
211         * Returns metadata under the static keys defined by this class.  
212         *  
213         * @see ca.uhn.hl7v2.protocol.TransportLayer#getCommonMetadata()
214         */
215        public Map getCommonMetadata() {
216            return myMetadata;
217        }
218    
219        /** 
220         * @see ca.uhn.hl7v2.protocol.impl.AbstractTransport#doConnect()
221         */
222        public void doConnect() throws TransportException {
223            try {
224                myInbound.connect();
225                if (myInbound != myOutbound) {
226                    myOutbound.connect();
227                }
228            } catch (JMSException e) {
229                throw new TransportException(e);
230            }
231        }
232    
233        /** 
234         * @see ca.uhn.hl7v2.protocol.impl.AbstractTransport#doDisconnect()
235         */
236        public void doDisconnect() throws TransportException {
237            try {
238                myInbound.disconnect();
239                if (myInbound != myOutbound) {
240                    myOutbound.disconnect();
241                }
242            } catch (JMSException e) {
243                throw new TransportException(e);
244            }
245        }
246        
247    }