001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.http.client;
017:
018: import com.google.gwt.core.client.GWT;
019: import com.google.gwt.core.client.JavaScriptObject;
020: import com.google.gwt.user.client.impl.HTTPRequestImpl;
021:
022: import java.util.HashMap;
023: import java.util.Map;
024:
025: /**
026: * Builder for constructing {@link com.google.gwt.http.client.Request} objects.
027: *
028: * <p>
029: * By default, this builder is restricted to building HTTP GET and POST requests
030: * due to a bug in Safari's implementation of the <code>XmlHttpRequest</code>
031: * object.
032: * </p>
033: *
034: * <p>
035: * Please see <a href="http://bugs.webkit.org/show_bug.cgi?id=3812">
036: * http://bugs.webkit.org/show_bug.cgi?id=3812</a> for more details.
037: * </p>
038: *
039: * <h3>Required Module</h3>
040: * Modules that use this class should inherit
041: * <code>com.google.gwt.http.HTTP</code>.
042: *
043: * {@gwt.include com/google/gwt/examples/http/InheritsExample.gwt.xml}
044: *
045: */
046: public class RequestBuilder {
047: /**
048: * HTTP request method constants.
049: */
050: public static final class Method {
051: private final String name;
052:
053: private Method(String name) {
054: this .name = name;
055: }
056:
057: @Override
058: public String toString() {
059: return name;
060: }
061: }
062:
063: /**
064: * Specifies that the HTTP GET method should be used.
065: */
066: public static final Method GET = new Method("GET");
067:
068: /**
069: * Specifies that the HTTP POST method should be used.
070: */
071: public static final Method POST = new Method("POST");
072:
073: private static final HTTPRequestImpl httpRequest = (HTTPRequestImpl) GWT
074: .create(HTTPRequestImpl.class);
075:
076: /*
077: * Map of header name to value that will be added to the JavaScript
078: * XmlHttpRequest object before sending a request.
079: */
080: private Map<String, String> headers;
081:
082: /*
083: * HTTP method to use when opening an JavaScript XmlHttpRequest object
084: */
085: private String httpMethod;
086:
087: /*
088: * Password to use when opening an JavaScript XmlHttpRequest object
089: */
090: private String password;
091:
092: /*
093: * Timeout in milliseconds before the request timeouts and fails.
094: */
095: private int timeoutMillis;
096:
097: /*
098: * URL to use when opening an JavaScript XmlHttpRequest object.
099: */
100: private String url;
101:
102: /*
103: * User to use when opening an JavaScript XmlHttpRequest object
104: */
105: private String user;
106:
107: /**
108: * Creates a builder using the parameters for configuration.
109: *
110: * @param httpMethod HTTP method to use for the request
111: * @param url URL that has already has already been encoded. Please see
112: * {@link com.google.gwt.http.client.URL#encode(String)} and
113: * {@link com.google.gwt.http.client.URL#encodeComponent(String)} for
114: * how to do this.
115: * @throws IllegalArgumentException if the httpMethod or URL are empty
116: * @throws NullPointerException if the httpMethod or the URL are null
117: */
118: public RequestBuilder(Method httpMethod, String url) {
119: this ((httpMethod == null) ? null : httpMethod.toString(), url);
120: }
121:
122: /**
123: * Creates a builder using the parameters values for configuration.
124: *
125: * @param httpMethod HTTP method to use for the request
126: * @param url URL that has already has already been URL encoded. Please see
127: * {@link com.google.gwt.http.client.URL#encode(String)} and
128: * {@link com.google.gwt.http.client.URL#encodeComponent(String)} for
129: * how to do this.
130: * @throws IllegalArgumentException if the httpMethod or URL are empty
131: * @throws NullPointerException if the httpMethod or the URL are null
132: *
133: * <p>
134: * <b>WARNING:</b>This method is provided in order to allow the creation of
135: * HTTP request other than GET and POST to be made. If this is done, the
136: * developer must accept that the behavior on Safari is undefined.
137: * </p>
138: */
139: protected RequestBuilder(String httpMethod, String url) {
140:
141: StringValidator.throwIfEmptyOrNull("httpMethod", httpMethod);
142: StringValidator.throwIfEmptyOrNull("url", url);
143:
144: this .httpMethod = httpMethod;
145: this .url = url;
146: }
147:
148: /**
149: * Sends an HTTP request based on the current builder configuration. If no
150: * request headers have been set, the header "Content-Type" will be used with
151: * a value of "text/plain; charset=utf-8".
152: *
153: * @param requestData the data to send as part of the request
154: * @param callback the response handler to be notified when the request fails
155: * or completes
156: * @return a {@link Request} object that can be used to track the request
157: */
158: public Request sendRequest(String requestData,
159: RequestCallback callback) throws RequestException {
160:
161: if (user == null && password != null) {
162: throw new IllegalStateException(
163: "A password is set, but no user is set");
164: }
165:
166: JavaScriptObject xmlHttpRequest = httpRequest
167: .createXmlHTTPRequest();
168: String openError;
169: if (password != null) {
170: openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod,
171: url, true, user, password);
172: } else if (user != null) {
173: openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod,
174: url, true, user);
175: } else {
176: openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod,
177: url, true);
178: }
179: if (openError != null) {
180: RequestPermissionException requestPermissionException = new RequestPermissionException(
181: url);
182: requestPermissionException.initCause(new RequestException(
183: openError));
184: throw requestPermissionException;
185: }
186:
187: setHeaders(xmlHttpRequest);
188:
189: Request request = new Request(xmlHttpRequest, timeoutMillis,
190: callback);
191:
192: String sendError = XMLHTTPRequest.send(xmlHttpRequest, request,
193: requestData, callback);
194: if (sendError != null) {
195: throw new RequestException(sendError);
196: }
197:
198: return request;
199: }
200:
201: /**
202: * Sets a request header with the given name and value. If a header with the
203: * specified name has already been set then the new value overwrites the
204: * current value.
205: *
206: * @param header the name of the header
207: * @param value the value of the header
208: *
209: * @throws NullPointerException if header or value are null
210: * @throws IllegalArgumentException if header or value are the empty string
211: */
212: public void setHeader(String header, String value) {
213: StringValidator.throwIfEmptyOrNull("header", header);
214: StringValidator.throwIfEmptyOrNull("value", value);
215:
216: if (headers == null) {
217: headers = new HashMap<String, String>();
218: }
219:
220: headers.put(header, value);
221: }
222:
223: /**
224: * Sets the password to use in the request URL. This is ignored if there is no
225: * user specified.
226: *
227: * @param password password to use in the request URL
228: *
229: * @throws IllegalArgumentException if the password is empty
230: * @throws NullPointerException if the password is null
231: */
232: public void setPassword(String password) {
233: StringValidator.throwIfEmptyOrNull("password", password);
234:
235: this .password = password;
236: }
237:
238: /**
239: * Sets the number of milliseconds to wait for a request to complete. Should
240: * the request timeout, the
241: * {@link com.google.gwt.http.client.RequestCallback#onError(Request, Throwable)}
242: * method will be called on the callback instance given to the
243: * {@link com.google.gwt.http.client.RequestBuilder#sendRequest(String, RequestCallback)}
244: * method. The callback method will receive an instance of the
245: * {@link com.google.gwt.http.client.RequestTimeoutException} class as its
246: * {@link java.lang.Throwable} argument.
247: *
248: * @param timeoutMillis number of milliseconds to wait before canceling the
249: * request, a value of zero disables timeouts
250: *
251: * @throws IllegalArgumentException if the timeout value is negative
252: */
253: public void setTimeoutMillis(int timeoutMillis) {
254: if (timeoutMillis < 0) {
255: throw new IllegalArgumentException(
256: "Timeouts cannot be negative");
257: }
258:
259: this .timeoutMillis = timeoutMillis;
260: }
261:
262: /**
263: * Sets the user name that will be used in the request URL.
264: *
265: * @param user user name to use
266: * @throws IllegalArgumentException if the user is empty
267: * @throws NullPointerException if the user is null
268: */
269: public void setUser(String user) {
270: StringValidator.throwIfEmptyOrNull("user", user);
271:
272: this .user = user;
273: }
274:
275: /*
276: * Internal method that actually sets our cached headers on the underlying
277: * JavaScript XmlHttpRequest object. If there are no headers set, then we set
278: * the "Content-Type" to "text/plain; charset=utf-8". This is really lining us
279: * up for integration with RPC.
280: */
281: private void setHeaders(JavaScriptObject xmlHttpRequest)
282: throws RequestException {
283: if (headers != null && headers.size() > 0) {
284: for (Map.Entry<String, String> header : headers.entrySet()) {
285: String errorMessage = XMLHTTPRequest.setRequestHeader(
286: xmlHttpRequest, header.getKey(), header
287: .getValue());
288: if (errorMessage != null) {
289: throw new RequestException(errorMessage);
290: }
291: }
292: } else {
293: XMLHTTPRequest.setRequestHeader(xmlHttpRequest,
294: "Content-Type", "text/plain; charset=utf-8");
295: }
296: }
297: }
|