blob: 5d57570f4e06c7a3b04ed527ab8fec1be9973031 [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.http.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.impl.HTTPRequestImpl;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Builder for constructing {@link com.google.gwt.http.client.Request} objects.
*
* <p>
* By default, this builder is restricted to building HTTP GET and POST requests
* due to a bug in Safari's implementation of the <code>XmlHttpRequest</code>
* object.
* </p>
*
* <p>
* Please see <a href="http://bugs.webkit.org/show_bug.cgi?id=3812">
* http://bugs.webkit.org/show_bug.cgi?id=3812</a> for more details.
* </p>
*
* <h3>Required Module</h3>
* Modules that use this class should inherit
* <code>com.google.gwt.http.HTTP</code>.
*
* {@gwt.include com/google/gwt/examples/http/InheritsExample.gwt.xml}
*
*/
public class RequestBuilder {
/**
* HTTP request method constants.
*/
public static final class Method {
private final String name;
private Method(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
/**
* Specifies that the HTTP GET method should be used.
*/
public static final Method GET = new Method("GET");
/**
* Specifies that the HTTP POST method should be used.
*/
public static final Method POST = new Method("POST");
private static final HTTPRequestImpl httpRequest = (HTTPRequestImpl) GWT.create(HTTPRequestImpl.class);
/*
* Map of header name to value that will be added to the JavaScript
* XmlHttpRequest object before sending a request.
*/
private Map headers;
/*
* HTTP method to use when opening an JavaScript XmlHttpRequest object
*/
private String httpMethod;
/*
* Password to use when opening an JavaScript XmlHttpRequest object
*/
private String password;
/*
* Timeout in milliseconds before the request timeouts and fails.
*/
private int timeoutMillis;
/*
* URL to use when opening an JavaScript XmlHttpRequest object.
*/
private String url;
/*
* User to use when opening an JavaScript XmlHttpRequest object
*/
private String user;
/**
* Creates a builder using the parameters for configuration.
*
* @param httpMethod HTTP method to use for the request
* @param url URL that has already has already been encoded. Please see
* {@link com.google.gwt.http.client.URL#encode(String)} and
* {@link com.google.gwt.http.client.URL#encodeComponent(String)} for
* how to do this.
* @throws IllegalArgumentException if the httpMethod or URL are empty
* @throws NullPointerException if the httpMethod or the URL are null
*/
public RequestBuilder(Method httpMethod, String url) {
this((httpMethod == null) ? null : httpMethod.toString(), url);
}
/**
* Creates a builder using the parameters values for configuration.
*
* @param httpMethod HTTP method to use for the request
* @param url URL that has already has already been URL encoded. Please see
* {@link com.google.gwt.http.client.URL#encode(String)} and
* {@link com.google.gwt.http.client.URL#encodeComponent(String)} for
* how to do this.
* @throws IllegalArgumentException if the httpMethod or URL are empty
* @throws NullPointerException if the httpMethod or the URL are null
*
* <p>
* <b>WARNING:</b>This method is provided in order to allow the creation of
* HTTP request other than GET and POST to be made. If this is done, the
* developer must accept that the behavior on Safari is undefined.
* </p>
*/
protected RequestBuilder(String httpMethod, String url) {
StringValidator.throwIfEmptyOrNull("httpMethod", httpMethod);
StringValidator.throwIfEmptyOrNull("url", url);
this.httpMethod = httpMethod;
this.url = url;
}
/**
* Sends an HTTP request based on the current builder configuration. If no
* request headers have been set, the header "Content-Type" will be used with
* a value of "text/plain; charset=utf-8".
*
* @param requestData the data to send as part of the request
* @param callback the response handler to be notified when the request fails
* or completes
* @return a {@link Request} object that can be used to track the request
*/
public Request sendRequest(String requestData, RequestCallback callback)
throws RequestException {
if (user == null && password != null) {
throw new IllegalStateException("A password is set, but no user is set");
}
JavaScriptObject xmlHttpRequest = httpRequest.createXmlHTTPRequest();
String openError;
if (password != null) {
openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod, url, true,
user, password);
} else if (user != null) {
openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod, url, true,
user);
} else {
openError = XMLHTTPRequest.open(xmlHttpRequest, httpMethod, url, true);
}
if (openError != null) {
RequestPermissionException requestPermissionException = new RequestPermissionException(url);
requestPermissionException.initCause(new RequestException(openError));
throw requestPermissionException;
}
setHeaders(xmlHttpRequest);
Request request = new Request(xmlHttpRequest, timeoutMillis, callback);
String sendError = XMLHTTPRequest.send(xmlHttpRequest, request,
requestData, callback);
if (sendError != null) {
throw new RequestException(sendError);
}
return request;
}
/**
* Sets a request header with the given name and value. If a header with the
* specified name has already been set then the new value overwrites the
* current value.
*
* @param header the name of the header
* @param value the value of the header
*
* @throws NullPointerException if header or value are null
* @throws IllegalArgumentException if header or value are the empty string
*/
public void setHeader(String header, String value) {
StringValidator.throwIfEmptyOrNull("header", header);
StringValidator.throwIfEmptyOrNull("value", value);
if (headers == null) {
headers = new HashMap();
}
headers.put(header, value);
}
/**
* Sets the password to use in the request URL. This is ignored if there is no
* user specified.
*
* @param password password to use in the request URL
*
* @throws IllegalArgumentException if the password is empty
* @throws NullPointerException if the password is null
*/
public void setPassword(String password) {
StringValidator.throwIfEmptyOrNull("password", password);
this.password = password;
}
/**
* Sets the number of milliseconds to wait for a request to complete. Should
* the request timeout, the
* {@link com.google.gwt.http.client.RequestCallback#onError(Request, Throwable)}
* method will be called on the callback instance given to the
* {@link com.google.gwt.http.client.RequestBuilder#sendRequest(String, RequestCallback)}
* method. The callback method will receive an instance of the
* {@link com.google.gwt.http.client.RequestTimeoutException} class as its
* {@link java.lang.Throwable} argument.
*
* @param timeoutMillis number of milliseconds to wait before canceling the
* request, a value of zero disables timeouts
*
* @throws IllegalArgumentException if the timeout value is negative
*/
public void setTimeoutMillis(int timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("Timeouts cannot be negative");
}
this.timeoutMillis = timeoutMillis;
}
/**
* Sets the user name that will be used in the request URL.
*
* @param user user name to use
* @throws IllegalArgumentException if the user is empty
* @throws NullPointerException if the user is null
*/
public void setUser(String user) {
StringValidator.throwIfEmptyOrNull("user", user);
this.user = user;
}
/*
* Internal method that actually sets our cached headers on the underlying
* JavaScript XmlHttpRequest object. If there are no headers set, then we set
* the "Content-Type" to "text/plain; charset=utf-8". This is really lining us
* up for integration with RPC.
*/
private void setHeaders(JavaScriptObject xmlHttpRequest)
throws RequestException {
if (headers != null && headers.size() > 0) {
Set entrySet = headers.entrySet();
Iterator iter = entrySet.iterator();
while (iter.hasNext()) {
Map.Entry header = (Map.Entry) iter.next();
String errorMessage = XMLHTTPRequest.setRequestHeader(xmlHttpRequest,
(String) header.getKey(), (String) header.getValue());
if (errorMessage != null) {
throw new RequestException(errorMessage);
}
}
} else {
XMLHTTPRequest.setRequestHeader(xmlHttpRequest, "Content-Type",
"text/plain; charset=utf-8");
}
}
}