001: /*
002: * Copyright 2005-2007 Noelios Consulting.
003: *
004: * The contents of this file are subject to the terms of the Common Development
005: * and Distribution License (the "License"). You may not use this file except in
006: * compliance with the License.
007: *
008: * You can obtain a copy of the license at
009: * http://www.opensource.org/licenses/cddl1.txt See the License for the specific
010: * language governing permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL HEADER in each file and
013: * include the License file at http://www.opensource.org/licenses/cddl1.txt If
014: * applicable, add the following below this CDDL HEADER, with the fields
015: * enclosed by brackets "[]" replaced with your own identifying information:
016: * Portions Copyright [yyyy] [name of copyright owner]
017: */
018:
019: package com.noelios.restlet.ext.net;
020:
021: import java.io.FileNotFoundException;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.net.ConnectException;
026: import java.net.HttpURLConnection;
027: import java.net.SocketTimeoutException;
028: import java.net.URL;
029: import java.util.logging.Level;
030:
031: import javax.net.ssl.HttpsURLConnection;
032:
033: import org.restlet.data.Parameter;
034: import org.restlet.data.Request;
035: import org.restlet.data.Status;
036: import org.restlet.resource.Representation;
037: import org.restlet.util.Series;
038:
039: import com.noelios.restlet.Engine;
040: import com.noelios.restlet.http.HttpClientCall;
041:
042: /**
043: * HTTP client connector call based on JDK's java.net.HttpURLConnection class.
044: *
045: * @author Jerome Louvel (contact@noelios.com)
046: */
047: public class HttpUrlConnectionCall extends HttpClientCall {
048: /** The wrapped HTTP URL connection. */
049: private HttpURLConnection connection;
050:
051: /** Indicates if the response headers were added. */
052: private boolean responseHeadersAdded;
053:
054: /**
055: * Constructor.
056: *
057: * @param helper
058: * The parent HTTP client helper.
059: * @param method
060: * The method name.
061: * @param requestUri
062: * The request URI.
063: * @param hasEntity
064: * Indicates if the call will have an entity to send to the
065: * server.
066: * @throws IOException
067: */
068: public HttpUrlConnectionCall(HttpClientHelper helper,
069: String method, String requestUri, boolean hasEntity)
070: throws IOException {
071: super (helper, method, requestUri);
072:
073: if (requestUri.startsWith("http")) {
074: URL url = new URL(requestUri);
075: this .connection = (HttpURLConnection) url.openConnection();
076:
077: // These properties can only be used with Java 1.5 and upper
078: // releases
079: int majorVersionNumber = Engine.getJavaMajorVersion();
080: int minorVersionNumber = Engine.getJavaMinorVersion();
081: if ((majorVersionNumber > 1)
082: || (majorVersionNumber == 1 && minorVersionNumber >= 5)) {
083: this .connection.setConnectTimeout(getHelper()
084: .getConnectTimeout());
085: this .connection.setReadTimeout(getHelper()
086: .getReadTimeout());
087: }
088:
089: this .connection.setAllowUserInteraction(getHelper()
090: .isAllowUserInteraction());
091: this .connection.setDoOutput(hasEntity);
092: this .connection.setInstanceFollowRedirects(getHelper()
093: .isFollowRedirects());
094: this .connection.setUseCaches(getHelper().isUseCaches());
095: this .responseHeadersAdded = false;
096: setConfidential(this .connection instanceof HttpsURLConnection);
097: } else {
098: throw new IllegalArgumentException(
099: "Only HTTP or HTTPS resource URIs are allowed here");
100: }
101: }
102:
103: /**
104: * Returns the HTTP client helper.
105: *
106: * @return The HTTP client helper.
107: */
108: public HttpClientHelper getHelper() {
109: return (HttpClientHelper) super .getHelper();
110: }
111:
112: /**
113: * Returns the connection.
114: *
115: * @return The connection.
116: */
117: public HttpURLConnection getConnection() {
118: return this .connection;
119: }
120:
121: /**
122: * Sends the request to the client. Commits the request line, headers and
123: * optional entity and send them over the network.
124: *
125: * @param request
126: * The high-level request.
127: * @return The result status.
128: */
129: public Status sendRequest(Request request) {
130: Status result = null;
131:
132: try {
133: if (request.isEntityAvailable()) {
134: Representation entity = request.getEntity();
135:
136: // These properties can only be used with Java 1.5 and upper
137: // releases
138: int majorVersionNumber = Engine.getJavaMajorVersion();
139: int minorVersionNumber = Engine.getJavaMinorVersion();
140: if ((majorVersionNumber > 1)
141: || (majorVersionNumber == 1 && minorVersionNumber >= 5)) {
142: // Adjust the streaming mode
143: if (entity.getSize() > 0) {
144: // The size of the entity is known in advance
145: getConnection().setFixedLengthStreamingMode(
146: (int) entity.getSize());
147: } else {
148: // The size of the entity is not known in advance
149: if (getHelper().getChunkLength() >= 0) {
150: // Use chunked encoding
151: getConnection().setChunkedStreamingMode(
152: getHelper().getChunkLength());
153: } else {
154: // Use entity buffering to determine the content
155: // length
156: }
157: }
158: }
159: }
160:
161: // Set the request method
162: getConnection().setRequestMethod(getMethod());
163:
164: // Set the request headers
165: for (Parameter header : getRequestHeaders()) {
166: getConnection().addRequestProperty(header.getName(),
167: header.getValue());
168: }
169:
170: // Ensure that the connections is active
171: getConnection().connect();
172:
173: // Send the optional entity
174: result = super .sendRequest(request);
175: } catch (ConnectException ce) {
176: getHelper()
177: .getLogger()
178: .log(
179: Level.FINE,
180: "An error occurred during the connection to the remote HTTP server.",
181: ce);
182: result = new Status(Status.CONNECTOR_ERROR_CONNECTION,
183: "Unable to connect to the remote server. "
184: + ce.getMessage());
185: } catch (SocketTimeoutException ste) {
186: getHelper()
187: .getLogger()
188: .log(
189: Level.FINE,
190: "An timeout error occurred during the communication with the remote HTTP server.",
191: ste);
192: result = new Status(Status.CONNECTOR_ERROR_COMMUNICATION,
193: "Unable to complete the HTTP call due to a communication timeout error. "
194: + ste.getMessage());
195: } catch (FileNotFoundException fnfe) {
196: getHelper()
197: .getLogger()
198: .log(
199: Level.FINE,
200: "An unexpected error occurred during the sending of the HTTP request.",
201: fnfe);
202: result = new Status(Status.CONNECTOR_ERROR_INTERNAL,
203: "Unable to find a local file for sending. "
204: + fnfe.getMessage());
205: } catch (IOException ioe) {
206: getHelper()
207: .getLogger()
208: .log(
209: Level.FINE,
210: "An error occurred during the communication with the remote HTTP server.",
211: ioe);
212: result = new Status(
213: Status.CONNECTOR_ERROR_COMMUNICATION,
214: "Unable to complete the HTTP call due to a communication error with the remote server. "
215: + ioe.getMessage());
216: } catch (Exception e) {
217: getHelper()
218: .getLogger()
219: .log(
220: Level.FINE,
221: "An unexpected error occurred during the sending of the HTTP request.",
222: e);
223: result = new Status(Status.CONNECTOR_ERROR_INTERNAL,
224: "Unable to send the HTTP request. "
225: + e.getMessage());
226: }
227:
228: return result;
229: }
230:
231: /**
232: * Returns the request entity stream if it exists.
233: *
234: * @return The request entity stream if it exists.
235: */
236: public OutputStream getRequestStream() {
237: try {
238: return getConnection().getOutputStream();
239: } catch (IOException ioe) {
240: return null;
241: }
242: }
243:
244: /**
245: * Returns the response address.<br/> Corresponds to the IP address of the
246: * responding server.
247: *
248: * @return The response address.
249: */
250: public String getServerAddress() {
251: return getConnection().getURL().getHost();
252: }
253:
254: /**
255: * Returns the modifiable list of response headers.
256: *
257: * @return The modifiable list of response headers.
258: */
259: public Series<Parameter> getResponseHeaders() {
260: Series<Parameter> result = super .getResponseHeaders();
261:
262: if (!this .responseHeadersAdded) {
263: // Read the response headers
264: int i = 1;
265: String headerName = getConnection().getHeaderFieldKey(i);
266: String headerValue = getConnection().getHeaderField(i);
267: while (headerName != null) {
268: result.add(headerName, headerValue);
269: i++;
270: headerName = getConnection().getHeaderFieldKey(i);
271: headerValue = getConnection().getHeaderField(i);
272: }
273:
274: this .responseHeadersAdded = true;
275: }
276:
277: return result;
278: }
279:
280: /**
281: * Returns the response status code.
282: *
283: * @return The response status code.
284: * @throws IOException
285: */
286: public int getStatusCode() throws IOException {
287: return getConnection().getResponseCode();
288: }
289:
290: /**
291: * Returns the response reason phrase.
292: *
293: * @return The response reason phrase.
294: */
295: public String getReasonPhrase() {
296: try {
297: return getConnection().getResponseMessage();
298: } catch (IOException e) {
299: return null;
300: }
301: }
302:
303: /**
304: * Returns the response stream if it exists.
305: *
306: * @return The response stream if it exists.
307: */
308: public InputStream getResponseStream() {
309: InputStream result = null;
310:
311: try {
312: result = getConnection().getInputStream();
313: } catch (IOException ioe) {
314: result = getConnection().getErrorStream();
315: }
316:
317: if (result == null) {
318: // Maybe an error stream is available instead
319: result = getConnection().getErrorStream();
320: }
321:
322: return result;
323: }
324: }
|