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 "URLTransport.java".  Description: 
010"A TransportLayer that reads and writes from an URL." 
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.io.BufferedInputStream;
030import java.io.BufferedOutputStream;
031import java.io.IOException;
032import java.io.InputStreamReader;
033import java.io.OutputStreamWriter;
034import java.io.Reader;
035import java.io.Writer;
036import java.net.URL;
037import java.net.URLConnection;
038
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042import ca.uhn.hl7v2.protocol.TransportException;
043import ca.uhn.hl7v2.protocol.TransportLayer;
044import 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 */
054public 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}