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 "URLTransport.java".  Description: 
010    "A TransportLayer that reads and writes from an URL." 
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.io.BufferedInputStream;
030    import java.io.BufferedOutputStream;
031    import java.io.IOException;
032    import java.io.InputStreamReader;
033    import java.io.OutputStreamWriter;
034    import java.io.Reader;
035    import java.io.Writer;
036    import java.net.URL;
037    import java.net.URLConnection;
038    
039    import org.slf4j.Logger;
040    import org.slf4j.LoggerFactory;
041    
042    import ca.uhn.hl7v2.protocol.TransportException;
043    import ca.uhn.hl7v2.protocol.TransportLayer;
044    import ca.uhn.hl7v2.protocol.Transportable;
045    
046    /**
047     * A <code>TransportLayer</code> that reads and writes from an URL (for example
048     * over HTTP).    
049     * 
050     * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
051     * @author <a href="mailto:alexei.guevara@uhn.on.ca">Alexei Guevara</a>
052     * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
053     */
054    public class URLTransport extends AbstractTransport implements TransportLayer {
055        
056        private static final Logger log = LoggerFactory.getLogger(URLTransport.class);    
057    
058        /**
059         * Key in Transportable metadata map under which URL is stored.  
060         */
061        public static final String URL_KEY = "URL";
062    
063        private String myContentType = "application/hl7+doc+xml";
064        private URL myURL;
065        private URLConnection myConnection;
066        protected int myBufferSize = 3000;
067        
068        private final boolean myConnectOnSend;
069        private final boolean myConnectOnReceive;
070        private final boolean myConnectOnConnect;
071    
072        /**
073         * The boolean configuration flags determine when new connections are made.  For example if this 
074         * transport is being used for query/response, you might set connectOnSend to true and
075         * the others to false, so that each query/response is done over a fresh connection.  If 
076         * you are using a transport just to read data from a URL, you might set connectOnReceive to 
077         * true and the others to false.  
078         *  
079         * @param theURL the URL at which messages are to be read and written 
080         * @param connectOnSend makes a new connection before each send  
081         * @param connectOnReceive makes a new connection before each receive 
082         * @param connectOnConnect makes a new connection when connect() is called 
083         */
084        public URLTransport(URL theURL, boolean connectOnSend, boolean connectOnReceive, boolean connectOnConnect) {
085            myURL = theURL;
086            getCommonMetadata().put(URL_KEY, theURL);
087            
088            myConnectOnSend = connectOnSend;
089            myConnectOnReceive = connectOnReceive;
090            myConnectOnConnect = connectOnConnect;
091        }
092    
093        /** 
094         * Writes the given message to the URL. 
095         * 
096         * @param theMessage the message to send 
097         * @see ca.uhn.hl7v2.protocol.AbstractTransport#doSend(ca.uhn.hl7v2.protocol.Transportable)
098         */
099        public void doSend(Transportable theMessage) throws TransportException {
100            if (myConnectOnSend) {
101                makeConnection();
102            }
103    
104            try {
105                Writer out = new OutputStreamWriter(new BufferedOutputStream(myConnection.getOutputStream()));
106                out.write(theMessage.getMessage());
107                out.flush();
108            } catch (IOException e) {
109                throw new TransportException(e);
110            }
111        }
112    
113        /**
114         * @see ca.uhn.hl7v2.protocol.AbstractTransport#doReceive()
115         */
116        public Transportable doReceive() throws TransportException {
117            
118            if (myConnectOnReceive) {
119                makeConnection();
120            }
121    
122            StringBuffer response = new StringBuffer();
123    
124            try {
125                log.debug("Getting InputStream from URLConnection");
126                Reader in = new InputStreamReader(new BufferedInputStream(myConnection.getInputStream()));
127                log.debug("Got InputStream from URLConnection");
128    
129                char[] buf = new char[myBufferSize];
130                int bytesRead = 0;
131    
132                IntRef bytesReadRef = new IntRef();
133    
134                while (bytesRead >= 0) {
135    
136                    try {
137                        ReaderThread readerThread = new ReaderThread(in, buf, bytesReadRef);
138                        readerThread.start();
139                        readerThread.join(10000);
140    
141                        bytesRead = bytesReadRef.getValue();
142    
143                        if (bytesRead == 0) {
144                            throw new TransportException("Timeout waiting for response");
145                        }
146                    }
147                    catch (InterruptedException x) {
148                    }
149    
150                    if (bytesRead > 0) {
151                        response.append(buf, 0, bytesRead);
152                    }
153    
154                }
155    
156                in.close();
157            } catch (IOException e) {
158                log.error(e.getMessage(), e);
159            }
160    
161            if (response.length() == 0) {
162                throw new TransportException("Timeout waiting for response");
163            }
164    
165            return new TransportableImpl(response.toString());
166        }
167    
168    
169        /** 
170         * Calls openConnection() on the underlying URL and configures the connection, 
171         * if this transport is configured to connect when connect() is called (see 
172         * constructor params).
173         *   
174         * @see ca.uhn.hl7v2.protocol.AbstractTransport#doConnect()
175         */
176        public void doConnect() throws TransportException {
177            if (myConnectOnConnect) {
178                makeConnection();
179            }
180        }
181        
182        //makes new connection 
183        private void makeConnection() throws TransportException {
184            try {
185                myConnection = myURL.openConnection();
186                myConnection.setDoOutput(true);
187                myConnection.setDoInput(true);
188                myConnection.setRequestProperty("Content-Type", getContentType());
189                myConnection.connect();
190            } catch (IOException e) {
191                throw new TransportException(e);
192            }     
193            log.debug("Made connection to {}", myURL.toExternalForm());
194        }
195        
196        /**
197         * @return the string used in the request property "Content-Type" (defaults to 
198         *      "application/hl7+doc+xml")
199         */
200        public String getContentType() {
201            return myContentType;
202        }
203        
204        /**
205         * @param theContentType the string to be used in the request property "Content-Type" 
206         *      (defaults to "application/hl7+doc+xml")
207         */
208        public void setContentType(String theContentType) {
209            myContentType = theContentType;
210        }
211    
212        /** 
213         * @see ca.uhn.hl7v2.protocol.TransportLayer#disconnect()
214         */
215        public void doDisconnect() throws TransportException {
216            myConnection = null;
217        }
218        
219    }