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