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.http;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.net.InetAddress;
025: import java.net.UnknownHostException;
026: import java.nio.channels.ReadableByteChannel;
027: import java.nio.channels.WritableByteChannel;
028: import java.util.logging.Level;
029:
030: import org.restlet.data.Encoding;
031: import org.restlet.data.Language;
032: import org.restlet.data.Method;
033: import org.restlet.data.Parameter;
034: import org.restlet.data.Request;
035: import org.restlet.data.Status;
036: import org.restlet.data.Tag;
037: import org.restlet.resource.InputRepresentation;
038: import org.restlet.resource.ReadableRepresentation;
039: import org.restlet.resource.Representation;
040: import org.restlet.service.ConnectorService;
041:
042: import com.noelios.restlet.util.HeaderReader;
043:
044: /**
045: * Low-level HTTP client call.
046: *
047: * @author Jerome Louvel (contact@noelios.com)
048: */
049: public class HttpClientCall extends HttpCall {
050: /** The parent HTTP client helper. */
051: private HttpClientHelper helper;
052:
053: /**
054: * Constructor setting the request address to the local host.
055: *
056: * @param helper
057: * The parent HTTP client helper.
058: * @param method
059: * The method name.
060: * @param requestUri
061: * The request URI.
062: */
063: public HttpClientCall(HttpClientHelper helper, String method,
064: String requestUri) {
065: setLogger(helper.getLogger());
066: this .helper = helper;
067: setMethod(method);
068: setRequestUri(requestUri);
069: setClientAddress(getLocalAddress());
070: }
071:
072: /**
073: * Returns the HTTP client helper.
074: *
075: * @return The HTTP client helper.
076: */
077: public HttpClientHelper getHelper() {
078: return this .helper;
079: }
080:
081: /**
082: * Returns the local IP address or 127.0.0.1 if the resolution fails.
083: *
084: * @return The local IP address or 127.0.0.1 if the resolution fails.
085: */
086: public static String getLocalAddress() {
087: try {
088: return InetAddress.getLocalHost().getHostAddress();
089: } catch (UnknownHostException e) {
090: return "127.0.0.1";
091: }
092: }
093:
094: /**
095: * Returns the request entity channel if it exists.
096: *
097: * @return The request entity channel if it exists.
098: */
099: public WritableByteChannel getRequestChannel() {
100: return null;
101: }
102:
103: /**
104: * Returns the request entity stream if it exists.
105: *
106: * @return The request entity stream if it exists.
107: */
108: public OutputStream getRequestStream() {
109: return null;
110: }
111:
112: /**
113: * Sends the request to the client. Commits the request line, headers and
114: * optional entity and send them over the network.
115: *
116: * @param request
117: * The high-level request.
118: */
119: public Status sendRequest(Request request) {
120: Status result = null;
121:
122: try {
123: Representation entity = request.isEntityAvailable() ? request
124: .getEntity()
125: : null;
126:
127: if (entity != null) {
128: // Get the connector service to callback
129: ConnectorService connectorService = getConnectorService(request);
130: if (connectorService != null)
131: connectorService.beforeSend(entity);
132:
133: // In order to workaround bug #6472250
134: // (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6472250),
135: // it is very important to reuse that exact same "rs" reference
136: // when manipulating the request stream, otherwise "insufficient
137: // data sent" exceptions will occur in "fixedLengthMode"
138: OutputStream rs = getRequestStream();
139: WritableByteChannel wbc = getRequestChannel();
140: if (wbc != null) {
141: if (entity != null) {
142: entity.write(wbc);
143: }
144: } else if (rs != null) {
145: if (entity != null) {
146: entity.write(rs);
147: }
148:
149: rs.flush();
150: }
151:
152: // Call-back after writing
153: if (connectorService != null)
154: connectorService.afterSend(entity);
155:
156: if (rs != null) {
157: rs.close();
158: } else if (wbc != null) {
159: wbc.close();
160: }
161: }
162:
163: // Now we can access the status code, this MUST happen after closing
164: // any open request stream.
165: result = new Status(getStatusCode(), null,
166: getReasonPhrase(), null);
167: } catch (IOException ioe) {
168: getHelper()
169: .getLogger()
170: .log(
171: Level.FINE,
172: "An error occured during the communication with the remote HTTP server.",
173: ioe);
174: result = new Status(
175: Status.CONNECTOR_ERROR_COMMUNICATION,
176: "Unable to complete the HTTP call due to a communication error with the remote server. "
177: + ioe.getMessage());
178: }
179:
180: return result;
181: }
182:
183: /**
184: * Returns the response channel if it exists.
185: *
186: * @return The response channel if it exists.
187: */
188: public ReadableByteChannel getResponseChannel() {
189: return null;
190: }
191:
192: /**
193: * Returns the response stream if it exists.
194: *
195: * @return The response stream if it exists.
196: */
197: public InputStream getResponseStream() {
198: return null;
199: }
200:
201: /**
202: * Returns the response entity if available. Note that no metadata is
203: * associated by default, you have to manually set them from your headers.
204: *
205: * @return The response entity if available.
206: */
207: public Representation getResponseEntity() {
208: Representation result = null;
209:
210: if (getResponseStream() != null) {
211: result = new InputRepresentation(getResponseStream(), null);
212: } else if (getResponseChannel() != null) {
213: result = new ReadableRepresentation(getResponseChannel(),
214: null);
215: } else if (getMethod().equals(Method.HEAD.getName())) {
216: result = new Representation() {
217: @Override
218: public ReadableByteChannel getChannel()
219: throws IOException {
220: return null;
221: }
222:
223: @Override
224: public InputStream getStream() throws IOException {
225: return null;
226: }
227:
228: @Override
229: public void write(OutputStream outputStream)
230: throws IOException {
231: // Do nothing
232: }
233:
234: @Override
235: public void write(WritableByteChannel writableChannel)
236: throws IOException {
237: // Do nothing
238: }
239: };
240: }
241:
242: if (result != null) {
243: for (Parameter header : getResponseHeaders()) {
244: if (header.getName().equalsIgnoreCase(
245: HttpConstants.HEADER_CONTENT_TYPE)) {
246: ContentType contentType = new ContentType(header
247: .getValue());
248: if (contentType != null) {
249: result.setMediaType(contentType.getMediaType());
250: result.setCharacterSet(contentType
251: .getCharacterSet());
252: }
253: } else if (header.getName().equalsIgnoreCase(
254: HttpConstants.HEADER_CONTENT_LENGTH)) {
255: result.setSize(Long.parseLong(header.getValue()));
256: } else if (header.getName().equalsIgnoreCase(
257: HttpConstants.HEADER_EXPIRES)) {
258: result.setExpirationDate(parseDate(header
259: .getValue(), false));
260: } else if (header.getName().equalsIgnoreCase(
261: HttpConstants.HEADER_CONTENT_ENCODING)) {
262: HeaderReader hr = new HeaderReader(header
263: .getValue());
264: String value = hr.readValue();
265: while (value != null) {
266: Encoding encoding = new Encoding(value);
267: if (!encoding.equals(Encoding.IDENTITY)) {
268: result.getEncodings().add(encoding);
269: }
270: value = hr.readValue();
271: }
272: } else if (header.getName().equalsIgnoreCase(
273: HttpConstants.HEADER_CONTENT_LANGUAGE)) {
274: HeaderReader hr = new HeaderReader(header
275: .getValue());
276: String value = hr.readValue();
277: while (value != null) {
278: result.getLanguages().add(new Language(value));
279: value = hr.readValue();
280: }
281: } else if (header.getName().equalsIgnoreCase(
282: HttpConstants.HEADER_LAST_MODIFIED)) {
283: result.setModificationDate(parseDate(header
284: .getValue(), false));
285: } else if (header.getName().equalsIgnoreCase(
286: HttpConstants.HEADER_ETAG)) {
287: result.setTag(Tag.parse(header.getValue()));
288: } else if (header.getName().equalsIgnoreCase(
289: HttpConstants.HEADER_CONTENT_LOCATION)) {
290: result.setIdentifier(header.getValue());
291: }
292: }
293: }
294:
295: return result;
296: }
297:
298: }
|