001: /*
002: * ========================================================================
003: *
004: * Copyright 2001-2004 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus.internal.client.connector.http;
021:
022: import java.io.BufferedReader;
023: import java.io.ByteArrayInputStream;
024: import java.io.ByteArrayOutputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.InputStreamReader;
028: import java.io.OutputStream;
029:
030: import java.net.HttpURLConnection;
031: import java.net.ProtocolException;
032: import java.net.URL;
033:
034: import java.security.Permission;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038:
039: /**
040: * Wrapper class for the real <code>HttpURLConnection</code> to the test servlet
041: * that reads the complete input stream into an internal buffer on
042: * the first call to getInputStream(). This is to ensure that the test servlet
043: * is not blocked on i/o when the test caller asks for the results.
044: * <p>
045: * The wrapper returns the buffered input stream from getInputStream and
046: * delegates the rest of the calls.
047: * <p>
048: * This class is final so we don't have to provide access to protected instance
049: * variables and methods of the wrapped connection.
050: *
051: * @version $Id: AutoReadHttpURLConnection.java 239010 2004-06-19 15:10:53Z vmassol $
052: */
053: final class AutoReadHttpURLConnection extends HttpURLConnection {
054: /**
055: * The logger
056: */
057: private static final Log LOGGER = LogFactory
058: .getLog(AutoReadHttpURLConnection.class);
059:
060: /**
061: * Default size of array for copying data.
062: */
063: private static final int DEFAULT_CHUNK_SIZE = 16384;
064:
065: /**
066: * The wrapped connection.
067: */
068: private HttpURLConnection delegate;
069:
070: /**
071: * The read input stream.
072: */
073: private InputStream streamBuffer;
074:
075: /**
076: * Constructs a an <code>AutoReadHttpURLConnection</code> object from an
077: * <code>HttpURLConnection</code>.
078: *
079: * @param theConnection the original connection to wrap
080: */
081: AutoReadHttpURLConnection(HttpURLConnection theConnection) {
082: super (null);
083: this .delegate = theConnection;
084: }
085:
086: /**
087: * Returns an input stream containing the fully read contents of
088: * the wrapped connection's input stream
089: *
090: * @return the input stream
091: * @exception IOException if an error occurs when reading the input stream
092: */
093: public synchronized InputStream getInputStream() throws IOException {
094: // Catch IOException to log the content of the error stream
095: try {
096: if (this .streamBuffer == null) {
097: LOGGER.debug("Original connection = " + this .delegate);
098:
099: InputStream is = this .delegate.getInputStream();
100:
101: this .streamBuffer = getBufferedInputStream(is);
102: }
103: } catch (IOException e) {
104: logErrorStream(this .delegate.getErrorStream());
105: throw e;
106: }
107:
108: return this .streamBuffer;
109: }
110:
111: /**
112: * Logs the HTTP error stream (used to get more information when we fail
113: * to read from the HTTP URL connection).
114: *
115: * @param theErrorStream the error stream containing the error description
116: * @exception IOException if an error occurs when reading the input stream
117: */
118: private void logErrorStream(InputStream theErrorStream)
119: throws IOException {
120: if (theErrorStream != null) {
121: // Log content of error stream
122: BufferedReader errorStream = new BufferedReader(
123: new InputStreamReader(theErrorStream));
124: String buffer;
125:
126: while ((buffer = errorStream.readLine()) != null) {
127: LOGGER.debug("ErrorStream [" + buffer + "]");
128: }
129: }
130: }
131:
132: /**
133: * Fully read the HTTP Connection response stream until there is no
134: * more bytes to read.
135: *
136: * @param theInputStream the input stream to fully read
137: * @return the data read as a buffered input stream
138: * @exception IOException if an error occurs when reading the input stream
139: */
140: private InputStream getBufferedInputStream(
141: InputStream theInputStream) throws IOException {
142: ByteArrayOutputStream os = new ByteArrayOutputStream(
143: DEFAULT_CHUNK_SIZE);
144:
145: copy(theInputStream, os);
146:
147: ByteArrayInputStream bais = new ByteArrayInputStream(os
148: .toByteArray());
149:
150: return bais;
151: }
152:
153: /**
154: * Copies the input stream passed as parameter to the output stream also
155: * passed as parameter. The full stream is read until there is no more
156: * bytes to read.
157: *
158: * @param theInputStream the input stream to read from
159: * @param theOutputStream the output stream to write to
160: * @exception IOException if an error occurs when reading the input stream
161: */
162: private void copy(InputStream theInputStream,
163: OutputStream theOutputStream) throws IOException {
164: // Only copy if there are data to copy ... The problem is that not
165: // all servers return a content-length header. If there is no header
166: // getContentLength() returns -1. It seems to work and it seems
167: // that all servers that return no content-length header also do
168: // not block on read() operations !
169: LOGGER.debug("Content-Length : ["
170: + this .delegate.getContentLength() + "]");
171:
172: if (theInputStream != null
173: && this .delegate.getContentLength() != 0) {
174: byte[] buf = new byte[DEFAULT_CHUNK_SIZE];
175: int count;
176:
177: while (-1 != (count = theInputStream.read(buf))) {
178: // log read data
179: printReadLogs(count, buf);
180: theOutputStream.write(buf, 0, count);
181: }
182: }
183: }
184:
185: /**
186: * Format log data read from socket for pretty printing (replaces
187: * asc char 10 by "\r", asc char 13 by "\n").
188: *
189: * @param theCount the number of bytes read in the buffer
190: * @param theBuffer the buffer containing the data to print
191: */
192: private void printReadLogs(int theCount, byte[] theBuffer) {
193: // Log portion of read data and replace asc 10 by \r and asc
194: // 13 by /n
195: StringBuffer prefix = new StringBuffer();
196:
197: for (int i = 0; i < theCount; i++) {
198: if (theBuffer[i] == 10) {
199: prefix.append("\\r");
200: } else if (theBuffer[i] == 13) {
201: prefix.append("\\n");
202: } else {
203: prefix.append((char) theBuffer[i]);
204: }
205: }
206:
207: LOGGER.debug("Read [" + theCount + "]: [" + prefix + "]");
208: }
209:
210: // Delegated methods
211:
212: /**
213: * @see java.net.HttpURLConnection#connect()
214: */
215: public void connect() throws IOException {
216: this .delegate.connect();
217: }
218:
219: /**
220: * @see java.net.HttpURLConnection#getAllowUserInteraction()
221: */
222: public boolean getAllowUserInteraction() {
223: return this .delegate.getAllowUserInteraction();
224: }
225:
226: /**
227: * @see java.net.HttpURLConnection#getContent()
228: */
229: public Object getContent() throws IOException {
230: return this .delegate.getContent();
231: }
232:
233: /**
234: * @see java.net.HttpURLConnection#getContentEncoding()
235: */
236: public String getContentEncoding() {
237: return this .delegate.getContentEncoding();
238: }
239:
240: /**
241: * @see java.net.HttpURLConnection#getContentLength()
242: */
243: public int getContentLength() {
244: return this .delegate.getContentLength();
245: }
246:
247: /**
248: * @see java.net.HttpURLConnection#getContentType()
249: */
250: public String getContentType() {
251: return this .delegate.getContentType();
252: }
253:
254: /**
255: * @see java.net.HttpURLConnection#getDate()
256: */
257: public long getDate() {
258: return this .delegate.getDate();
259: }
260:
261: /**
262: * @see java.net.HttpURLConnection#getDefaultUseCaches()
263: */
264: public boolean getDefaultUseCaches() {
265: return this .delegate.getDefaultUseCaches();
266: }
267:
268: /**
269: * @see java.net.HttpURLConnection#getDoInput()
270: */
271: public boolean getDoInput() {
272: return this .delegate.getDoInput();
273: }
274:
275: /**
276: * @see java.net.HttpURLConnection#getDoOutput()
277: */
278: public boolean getDoOutput() {
279: return this .delegate.getDoOutput();
280: }
281:
282: /**
283: * @see java.net.HttpURLConnection#getExpiration()
284: */
285: public long getExpiration() {
286: return this .delegate.getExpiration();
287: }
288:
289: /**
290: * @see java.net.HttpURLConnection#getHeaderField(int)
291: */
292: public String getHeaderField(int thePosition) {
293: return this .delegate.getHeaderField(thePosition);
294: }
295:
296: /**
297: * @see java.net.HttpURLConnection#getHeaderField(String)
298: */
299: public String getHeaderField(String theName) {
300: return this .delegate.getHeaderField(theName);
301: }
302:
303: /**
304: * @see java.net.HttpURLConnection#getHeaderFieldDate(String, long)
305: */
306: public long getHeaderFieldDate(String theName, long theDefaultValue) {
307: return this .delegate.getHeaderFieldDate(theName,
308: theDefaultValue);
309: }
310:
311: /**
312: * @see java.net.HttpURLConnection#getHeaderFieldInt(String, int)
313: */
314: public int getHeaderFieldInt(String theName, int theDefaultValue) {
315: return this .delegate
316: .getHeaderFieldInt(theName, theDefaultValue);
317: }
318:
319: /**
320: * @see java.net.HttpURLConnection#getHeaderFieldKey(int)
321: */
322: public String getHeaderFieldKey(int thePosition) {
323: return this .delegate.getHeaderFieldKey(thePosition);
324: }
325:
326: /**
327: * @see java.net.HttpURLConnection#getIfModifiedSince()
328: */
329: public long getIfModifiedSince() {
330: return this .delegate.getIfModifiedSince();
331: }
332:
333: /**
334: * @see java.net.HttpURLConnection#getLastModified()
335: */
336: public long getLastModified() {
337: return this .delegate.getLastModified();
338: }
339:
340: /**
341: * @see java.net.HttpURLConnection#getOutputStream()
342: */
343: public OutputStream getOutputStream() throws IOException {
344: return this .delegate.getOutputStream();
345: }
346:
347: /**
348: * @see java.net.HttpURLConnection#getPermission()
349: */
350: public Permission getPermission() throws IOException {
351: return this .delegate.getPermission();
352: }
353:
354: /**
355: * @see java.net.HttpURLConnection#getRequestProperty(String)
356: */
357: public String getRequestProperty(String theKey) {
358: return this .delegate.getRequestProperty(theKey);
359: }
360:
361: /**
362: * @see java.net.HttpURLConnection#getURL()
363: */
364: public URL getURL() {
365: return this .delegate.getURL();
366: }
367:
368: /**
369: * @see java.net.HttpURLConnection#getUseCaches()
370: */
371: public boolean getUseCaches() {
372: return this .delegate.getUseCaches();
373: }
374:
375: /**
376: * @see java.net.HttpURLConnection#setAllowUserInteraction(boolean)
377: */
378: public void setAllowUserInteraction(boolean hasInteraction) {
379: this .delegate.setAllowUserInteraction(hasInteraction);
380: }
381:
382: /**
383: * @see java.net.HttpURLConnection#setDefaultUseCaches(boolean)
384: */
385: public void setDefaultUseCaches(boolean isUsingDefaultCache) {
386: this .delegate.setDefaultUseCaches(isUsingDefaultCache);
387: }
388:
389: /**
390: * @see java.net.HttpURLConnection#setDoInput(boolean)
391: */
392: public void setDoInput(boolean isInput) {
393: this .delegate.setDoInput(isInput);
394: }
395:
396: /**
397: * @see java.net.HttpURLConnection#setDoOutput(boolean)
398: */
399: public void setDoOutput(boolean isOutput) {
400: this .delegate.setDoOutput(isOutput);
401: }
402:
403: /**
404: * @see java.net.HttpURLConnection#setIfModifiedSince(long)
405: */
406: public void setIfModifiedSince(long isModifiedSince) {
407: this .delegate.setIfModifiedSince(isModifiedSince);
408: }
409:
410: /**
411: * @see java.net.HttpURLConnection#setRequestProperty(String, String)
412: */
413: public void setRequestProperty(String theKey, String theValue) {
414: this .delegate.setRequestProperty(theKey, theValue);
415: }
416:
417: /**
418: * @see java.net.HttpURLConnection#setUseCaches(boolean)
419: */
420: public void setUseCaches(boolean isUsingCaches) {
421: this .delegate.setUseCaches(isUsingCaches);
422: }
423:
424: /**
425: * @see java.net.HttpURLConnection#toString()
426: */
427: public String toString() {
428: return this .delegate.toString();
429: }
430:
431: /**
432: * @see java.net.HttpURLConnection#disconnect()
433: */
434: public void disconnect() {
435: this .delegate.disconnect();
436: }
437:
438: /**
439: * @see java.net.HttpURLConnection#getErrorStream()
440: */
441: public InputStream getErrorStream() {
442: return this .delegate.getErrorStream();
443: }
444:
445: /**
446: * @see java.net.HttpURLConnection#getRequestMethod()
447: */
448: public String getRequestMethod() {
449: return this .delegate.getRequestMethod();
450: }
451:
452: /**
453: * @see java.net.HttpURLConnection#getResponseCode()
454: */
455: public int getResponseCode() throws IOException {
456: return this .delegate.getResponseCode();
457: }
458:
459: /**
460: * @see java.net.HttpURLConnection#getResponseMessage()
461: */
462: public String getResponseMessage() throws IOException {
463: return this .delegate.getResponseMessage();
464: }
465:
466: /**
467: * @see java.net.HttpURLConnection#setRequestMethod(String)
468: */
469: public void setRequestMethod(String theMethod)
470: throws ProtocolException {
471: this .delegate.setRequestMethod(theMethod);
472: }
473:
474: /**
475: * @see java.net.HttpURLConnection#usingProxy()
476: */
477: public boolean usingProxy() {
478: return this.delegate.usingProxy();
479: }
480: }
|