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