2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

View Javadoc

1   /* 
2    * ========================================================================
3    * 
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * 
19   * ========================================================================
20   */
21  package org.apache.cactus.internal.client.connector.http;
22  
23  import java.io.BufferedReader;
24  import java.io.ByteArrayInputStream;
25  import java.io.ByteArrayOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.InputStreamReader;
29  import java.io.OutputStream;
30  
31  import java.net.HttpURLConnection;
32  import java.net.ProtocolException;
33  import java.net.URL;
34  
35  import java.security.Permission;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  
40  /**
41   * Wrapper class for the real <code>HttpURLConnection</code> to the test servlet
42   * that reads the complete input stream into an internal buffer on
43   * the first call to getInputStream(). This is to ensure that the test servlet
44   * is not blocked on i/o when the test caller asks for the results.
45   * <p>
46   * The wrapper returns the buffered input stream from getInputStream and
47   * delegates the rest of the calls.
48   * <p>
49   * This class is final so we don't have to provide access to protected instance
50   * variables and methods of the wrapped connection.
51   *
52   * @version $Id: AutoReadHttpURLConnection.java 239010 2004-06-19 15:10:53Z vmassol $
53   */
54  final class AutoReadHttpURLConnection extends HttpURLConnection
55  {
56      /**
57       * The logger.
58       */
59      private static final Log LOGGER = 
60          LogFactory.getLog(AutoReadHttpURLConnection.class);
61  
62      /**
63       * Default size of array for copying data.
64       */
65      private static final int DEFAULT_CHUNK_SIZE = 16384;
66  
67      /**
68       * The wrapped connection.
69       */
70      private HttpURLConnection delegate;
71  
72      /**
73       * The read input stream.
74       */
75      private InputStream streamBuffer;
76  
77      /**
78       * Constructs a an <code>AutoReadHttpURLConnection</code> object from an
79       * <code>HttpURLConnection</code>.
80       *
81       * @param theConnection the original connection to wrap
82       */
83      AutoReadHttpURLConnection(HttpURLConnection theConnection)
84      {
85          super(null);
86          this.delegate = theConnection;
87      }
88  
89      /**
90       * Returns an input stream containing the fully read contents of
91       * the wrapped connection's input stream.
92       *
93       * @return the input stream
94       * @exception IOException if an error occurs when reading the input stream
95       */
96      public synchronized InputStream getInputStream() throws IOException
97      {
98          // Catch IOException to log the content of the error stream
99          try
100         {
101             if (this.streamBuffer == null)
102             {
103                 LOGGER.debug("Original connection = " + this.delegate);
104 
105                 InputStream is = this.delegate.getInputStream();
106 
107                 this.streamBuffer = getBufferedInputStream(is);
108             }
109         }
110         catch (IOException e)
111         {
112             logErrorStream(this.delegate.getErrorStream());
113             throw e;
114         }
115 
116         return this.streamBuffer;
117     }
118 
119     /**
120      * Logs the HTTP error stream (used to get more information when we fail
121      * to read from the HTTP URL connection).
122      *
123      * @param theErrorStream the error stream containing the error description
124      * @exception IOException if an error occurs when reading the input stream
125      */
126     private void logErrorStream(InputStream theErrorStream) throws IOException
127     {
128         if (theErrorStream != null)
129         {
130             // Log content of error stream
131             BufferedReader errorStream = 
132                 new BufferedReader(new InputStreamReader(theErrorStream));
133             String buffer;
134 
135             while ((buffer = errorStream.readLine()) != null)
136             {
137                 LOGGER.debug("ErrorStream [" + buffer + "]");
138             }
139         }
140     }
141 
142     /**
143      * Fully read the HTTP Connection response stream until there is no
144      * more bytes to read.
145      *
146      * @param theInputStream the input stream to fully read
147      * @return the data read as a buffered input stream
148      * @exception IOException if an error occurs when reading the input stream
149      */
150     private InputStream getBufferedInputStream(InputStream theInputStream)
151         throws IOException
152     {
153         ByteArrayOutputStream os = 
154             new ByteArrayOutputStream(DEFAULT_CHUNK_SIZE);
155 
156         copy(theInputStream, os);
157 
158         ByteArrayInputStream bais = new ByteArrayInputStream(os.toByteArray());
159 
160         return bais;
161     }
162 
163     /**
164      * Copies the input stream passed as parameter to the output stream also
165      * passed as parameter. The full stream is read until there is no more
166      * bytes to read.
167      *
168      * @param theInputStream the input stream to read from
169      * @param theOutputStream the output stream to write to
170      * @exception IOException if an error occurs when reading the input stream
171      */
172     private void copy(InputStream theInputStream, OutputStream theOutputStream)
173         throws IOException
174     {
175         // Only copy if there are data to copy ... The problem is that not
176         // all servers return a content-length header. If there is no header
177         // getContentLength() returns -1. It seems to work and it seems
178         // that all servers that return no content-length header also do
179         // not block on read() operations !
180         LOGGER.debug("Content-Length : [" + this.delegate.getContentLength()
181             + "]");
182 
183         if (theInputStream != null && this.delegate.getContentLength() != 0)
184         {
185             byte[] buf = new byte[DEFAULT_CHUNK_SIZE];
186             int count;
187 
188             while (-1 != (count = theInputStream.read(buf)))
189             {
190                 // log read data
191                 printReadLogs(count, buf);
192                 theOutputStream.write(buf, 0, count);
193             }
194         }
195     }
196 
197     /**
198      * Format log data read from socket for pretty printing (replaces
199      * asc char 10 by "\r", asc char 13 by "\n").
200      *
201      * @param theCount the number of bytes read in the buffer
202      * @param theBuffer the buffer containing the data to print
203      */
204     private void printReadLogs(int theCount, byte[] theBuffer)
205     {
206         // Log portion of read data and replace asc 10 by \r and asc
207         // 13 by /n
208         StringBuffer prefix = new StringBuffer();
209 
210         for (int i = 0; i < theCount; i++)
211         {
212             if (theBuffer[i] == 10)
213             {
214                 prefix.append("\\r");
215             }
216             else if (theBuffer[i] == 13)
217             {
218                 prefix.append("\\n");
219             }
220             else
221             {
222                 prefix.append((char) theBuffer[i]);
223             }
224         }
225 
226         LOGGER.debug("Read [" + theCount + "]: [" + prefix + "]");
227     }
228 
229     // Delegated methods
230 
231     /**
232      * {@inheritDoc}
233      * @see java.net.HttpURLConnection#connect()
234      */
235     public void connect() throws IOException
236     {
237         this.delegate.connect();
238     }
239 
240     /**
241      * {@inheritDoc}
242      * @see java.net.HttpURLConnection#getAllowUserInteraction()
243      */
244     public boolean getAllowUserInteraction()
245     {
246         return this.delegate.getAllowUserInteraction();
247     }
248 
249     /**
250      * {@inheritDoc}
251      * @see java.net.HttpURLConnection#getContent()
252      */
253     public Object getContent() throws IOException
254     {
255         return this.delegate.getContent();
256     }
257 
258     /**
259      * {@inheritDoc}
260      * @see java.net.HttpURLConnection#getContentEncoding()
261      */
262     public String getContentEncoding()
263     {
264         return this.delegate.getContentEncoding();
265     }
266 
267     /**
268      * {@inheritDoc}
269      * @see java.net.HttpURLConnection#getContentLength()
270      */
271     public int getContentLength()
272     {
273         return this.delegate.getContentLength();
274     }
275 
276     /**
277      * {@inheritDoc}
278      * @see java.net.HttpURLConnection#getContentType()
279      */
280     public String getContentType()
281     {
282         return this.delegate.getContentType();
283     }
284 
285     /**
286      * {@inheritDoc}
287      * @see java.net.HttpURLConnection#getDate()
288      */
289     public long getDate()
290     {
291         return this.delegate.getDate();
292     }
293 
294     /**
295      * {@inheritDoc}
296      * @see java.net.HttpURLConnection#getDefaultUseCaches()
297      */
298     public boolean getDefaultUseCaches()
299     {
300         return this.delegate.getDefaultUseCaches();
301     }
302 
303     /**
304      * {@inheritDoc}
305      * @see java.net.HttpURLConnection#getDoInput()
306      */
307     public boolean getDoInput()
308     {
309         return this.delegate.getDoInput();
310     }
311 
312     /**{@inheritDoc}
313      * @see java.net.HttpURLConnection#getDoOutput()
314      */
315     public boolean getDoOutput()
316     {
317         return this.delegate.getDoOutput();
318     }
319 
320     /**
321      * {@inheritDoc}
322      * @see java.net.HttpURLConnection#getExpiration()
323      */
324     public long getExpiration()
325     {
326         return this.delegate.getExpiration();
327     }
328 
329     /**
330      * {@inheritDoc}
331      * @see java.net.HttpURLConnection#getHeaderField(int)
332      */
333     public String getHeaderField(int thePosition)
334     {
335         return this.delegate.getHeaderField(thePosition);
336     }
337 
338     /**
339      * {@inheritDoc}
340      * @see java.net.HttpURLConnection#getHeaderField(String)
341      */
342     public String getHeaderField(String theName)
343     {
344         return this.delegate.getHeaderField(theName);
345     }
346 
347     /**
348      * {@inheritDoc}
349      * @see java.net.HttpURLConnection#getHeaderFieldDate(String, long)
350      */
351     public long getHeaderFieldDate(String theName, long theDefaultValue)
352     {
353         return this.delegate.getHeaderFieldDate(theName, theDefaultValue);
354     }
355 
356     /**
357      * {@inheritDoc}
358      * @see java.net.HttpURLConnection#getHeaderFieldInt(String, int)
359      */
360     public int getHeaderFieldInt(String theName, int theDefaultValue)
361     {
362         return this.delegate.getHeaderFieldInt(theName, theDefaultValue);
363     }
364 
365     /**
366      * {@inheritDoc}
367      * @see java.net.HttpURLConnection#getHeaderFieldKey(int)
368      */
369     public String getHeaderFieldKey(int thePosition)
370     {
371         return this.delegate.getHeaderFieldKey(thePosition);
372     }
373 
374     /**
375      * {@inheritDoc}
376      * @see java.net.HttpURLConnection#getIfModifiedSince()
377      */
378     public long getIfModifiedSince()
379     {
380         return this.delegate.getIfModifiedSince();
381     }
382 
383     /**
384      * {@inheritDoc}
385      * @see java.net.HttpURLConnection#getLastModified()
386      */
387     public long getLastModified()
388     {
389         return this.delegate.getLastModified();
390     }
391 
392     /**
393      * {@inheritDoc}
394      * @see java.net.HttpURLConnection#getOutputStream()
395      */
396     public OutputStream getOutputStream() throws IOException
397     {
398         return this.delegate.getOutputStream();
399     }
400 
401     /**
402      * {@inheritDoc}
403      * @see java.net.HttpURLConnection#getPermission()
404      */
405     public Permission getPermission() throws IOException
406     {
407         return this.delegate.getPermission();
408     }
409 
410     /**
411      * {@inheritDoc}
412      * @see java.net.HttpURLConnection#getRequestProperty(String)
413      */
414     public String getRequestProperty(String theKey)
415     {
416         return this.delegate.getRequestProperty(theKey);
417     }
418 
419     /**
420      * {@inheritDoc}
421      * @see java.net.HttpURLConnection#getURL()
422      */
423     public URL getURL()
424     {
425         return this.delegate.getURL();
426     }
427 
428     /**
429      * {@inheritDoc}
430      * @see java.net.HttpURLConnection#getUseCaches()
431      */
432     public boolean getUseCaches()
433     {
434         return this.delegate.getUseCaches();
435     }
436 
437     /**
438      * {@inheritDoc}
439      * @see java.net.HttpURLConnection#setAllowUserInteraction(boolean)
440      */
441     public void setAllowUserInteraction(boolean hasInteraction)
442     {
443         this.delegate.setAllowUserInteraction(hasInteraction);
444     }
445 
446     /**
447      * {@inheritDoc}
448      * @see java.net.HttpURLConnection#setDefaultUseCaches(boolean)
449      */
450     public void setDefaultUseCaches(boolean isUsingDefaultCache)
451     {
452         this.delegate.setDefaultUseCaches(isUsingDefaultCache);
453     }
454 
455     /**
456      * {@inheritDoc}
457      * @see java.net.HttpURLConnection#setDoInput(boolean)
458      */
459     public void setDoInput(boolean isInput)
460     {
461         this.delegate.setDoInput(isInput);
462     }
463 
464     /**
465      * {@inheritDoc}
466      * @see java.net.HttpURLConnection#setDoOutput(boolean)
467      */
468     public void setDoOutput(boolean isOutput)
469     {
470         this.delegate.setDoOutput(isOutput);
471     }
472 
473     /**
474      * {@inheritDoc}
475      * @see java.net.HttpURLConnection#setIfModifiedSince(long)
476      */
477     public void setIfModifiedSince(long isModifiedSince)
478     {
479         this.delegate.setIfModifiedSince(isModifiedSince);
480     }
481 
482     /**
483      * {@inheritDoc}
484      * @see java.net.HttpURLConnection#setRequestProperty(String, String)
485      */
486     public void setRequestProperty(String theKey, String theValue)
487     {
488         this.delegate.setRequestProperty(theKey, theValue);
489     }
490 
491     /**
492      * {@inheritDoc}
493      * @see java.net.HttpURLConnection#setUseCaches(boolean)
494      */
495     public void setUseCaches(boolean isUsingCaches)
496     {
497         this.delegate.setUseCaches(isUsingCaches);
498     }
499 
500     /**
501      * {@inheritDoc}
502      * @see java.net.HttpURLConnection#toString()
503      */
504     public String toString()
505     {
506         return this.delegate.toString();
507     }
508 
509     /**
510      * {@inheritDoc}
511      * @see java.net.HttpURLConnection#disconnect()
512      */
513     public void disconnect()
514     {
515         this.delegate.disconnect();
516     }
517 
518     /**
519      * {@inheritDoc}
520      * @see java.net.HttpURLConnection#getErrorStream()
521      */
522     public InputStream getErrorStream()
523     {
524         return this.delegate.getErrorStream();
525     }
526 
527     /**
528      * {@inheritDoc}
529      * @see java.net.HttpURLConnection#getRequestMethod()
530      */
531     public String getRequestMethod()
532     {
533         return this.delegate.getRequestMethod();
534     }
535 
536     /**
537      * {@inheritDoc}
538      * @see java.net.HttpURLConnection#getResponseCode()
539      */
540     public int getResponseCode() throws IOException
541     {
542         return this.delegate.getResponseCode();
543     }
544 
545     /**
546      * {@inheritDoc}
547      * @see java.net.HttpURLConnection#getResponseMessage()
548      */
549     public String getResponseMessage() throws IOException
550     {
551         return this.delegate.getResponseMessage();
552     }
553 
554     /**
555      * {@inheritDoc}
556      * @see java.net.HttpURLConnection#setRequestMethod(String)
557      */
558     public void setRequestMethod(String theMethod) throws ProtocolException
559     {
560         this.delegate.setRequestMethod(theMethod);
561     }
562 
563     /**
564      * {@inheritDoc}
565      * @see java.net.HttpURLConnection#usingProxy()
566      */
567     public boolean usingProxy()
568     {
569         return this.delegate.usingProxy();
570     }
571 }