| /* |
| * Copyright 2008 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.JavaScriptException; |
| import com.google.gwt.xhr.client.ReadyStateChangeHandler; |
| import com.google.gwt.xhr.client.XMLHttpRequest; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Builder for constructing {@link com.google.gwt.http.client.Request} objects. |
| * |
| * <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; |
| } |
| |
| @Override |
| public String toString() { |
| return name; |
| } |
| } |
| |
| /** |
| * Specifies that the HTTP DELETE method should be used. |
| */ |
| public static final Method DELETE = new Method("DELETE"); |
| |
| /** |
| * Specifies that the HTTP GET method should be used. |
| */ |
| public static final Method GET = new Method("GET"); |
| |
| /** |
| * Specifies that the HTTP HEAD method should be used. |
| */ |
| public static final Method HEAD = new Method("HEAD"); |
| |
| /** |
| * Specifies that the HTTP POST method should be used. |
| */ |
| public static final Method POST = new Method("POST"); |
| |
| /** |
| * Specifies that the HTTP PUT method should be used. |
| */ |
| public static final Method PUT = new Method("PUT"); |
| |
| /** |
| * The callback to call when the request completes. |
| */ |
| private RequestCallback callback; |
| |
| /** |
| * Map of header name to value that will be added to the JavaScript |
| * XmlHttpRequest object before sending a request. |
| */ |
| private Map<String, String> headers; |
| |
| /** |
| * HTTP method to use when opening a JavaScript XmlHttpRequest object. |
| */ |
| private final String httpMethod; |
| |
| /** |
| * Whether to include credentials for a Cross Origin Request. |
| */ |
| private boolean includeCredentials; |
| |
| /** |
| * Password to use when opening a JavaScript XmlHttpRequest object. |
| */ |
| private String password; |
| |
| /** |
| * Request data to use when sending a JavaScript XmlHttpRequest object. |
| */ |
| private String requestData; |
| |
| /** |
| * Timeout in milliseconds before the request timeouts and fails. |
| */ |
| private int timeoutMillis; |
| |
| /** |
| * URL to use when opening a JavaScript XmlHttpRequest object. |
| */ |
| private final String url; |
| |
| /** |
| * User to use when opening a 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)}, |
| * {@link com.google.gwt.http.client.URL#encodePathSegment(String)} and |
| * {@link com.google.gwt.http.client.URL#encodeQueryString(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#encodePathSegment(String)} and |
| * {@link com.google.gwt.http.client.URL#encodeQueryString(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 |
| */ |
| protected RequestBuilder(String httpMethod, String url) { |
| |
| StringValidator.throwIfEmptyOrNull("httpMethod", httpMethod); |
| StringValidator.throwIfEmptyOrNull("url", url); |
| |
| this.httpMethod = httpMethod; |
| this.url = url; |
| } |
| |
| /** |
| * Returns the callback previously set by |
| * {@link #setCallback(RequestCallback)}, or <code>null</code> if no callback |
| * was set. |
| */ |
| public RequestCallback getCallback() { |
| return callback; |
| } |
| |
| /** |
| * Returns the value of a header previous set by |
| * {@link #setHeader(String, String)}, or <code>null</code> if no such header |
| * was set. |
| * |
| * @param header the name of the header |
| */ |
| public String getHeader(String header) { |
| if (headers == null) { |
| return null; |
| } |
| return headers.get(header); |
| } |
| |
| /** |
| * Returns the HTTP method specified in the constructor. |
| */ |
| public String getHTTPMethod() { |
| return httpMethod; |
| } |
| |
| /** |
| * Returns the password previously set by {@link #setPassword(String)}, or |
| * <code>null</code> if no password was set. |
| */ |
| public String getPassword() { |
| return password; |
| } |
| |
| /** |
| * Returns the requestData previously set by {@link #setRequestData(String)}, |
| * or <code>null</code> if no requestData was set. |
| */ |
| public String getRequestData() { |
| return requestData; |
| } |
| |
| /** |
| * Returns the timeoutMillis previously set by {@link #setTimeoutMillis(int)}, |
| * or <code>0</code> if no timeoutMillis was set. |
| */ |
| public int getTimeoutMillis() { |
| return timeoutMillis; |
| } |
| |
| /** |
| * Returns the HTTP URL specified in the constructor. |
| */ |
| public String getUrl() { |
| return url; |
| } |
| |
| /** |
| * Returns the user previously set by {@link #setUser(String)}, or |
| * <code>null</code> if no user was set. |
| */ |
| public String getUser() { |
| return user; |
| } |
| |
| /** |
| * 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". You must call |
| * {@link #setRequestData(String)} and {@link #setCallback(RequestCallback)} |
| * before calling this method. |
| * |
| * @return a {@link Request} object that can be used to track the request |
| * @throws RequestException if the call fails to initiate |
| * @throws NullPointerException if a request callback has not been set |
| */ |
| public Request send() throws RequestException { |
| StringValidator.throwIfNull("callback", callback); |
| return doSend(requestData, callback); |
| } |
| |
| /** |
| * Sends an HTTP request based on the current builder configuration with the |
| * specified data and callback. If no request headers have been set, the |
| * header "Content-Type" will be used with a value of "text/plain; |
| * charset=utf-8". This method does not cache <code>requestData</code> or |
| * <code>callback</code>. |
| * |
| * @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 |
| * @throws NullPointerException if <code>callback</code> <code>null</code> |
| */ |
| public Request sendRequest(String requestData, RequestCallback callback) |
| throws RequestException { |
| StringValidator.throwIfNull("callback", callback); |
| return doSend(requestData, callback); |
| } |
| |
| /** |
| * Sets the response handler for this request. This method <b>must</b> be |
| * called before calling {@link #send()}. |
| * |
| * @param callback the response handler to be notified when the request fails |
| * or completes |
| * |
| * @throws NullPointerException if <code>callback</code> is <code>null</code> |
| */ |
| public void setCallback(RequestCallback callback) { |
| StringValidator.throwIfNull("callback", callback); |
| |
| this.callback = callback; |
| } |
| |
| /** |
| * 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<String, String>(); |
| } |
| |
| 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 data to send as part of this request. This method <b>must</b> be |
| * called before calling {@link #send()}. |
| * |
| * @param requestData the data to send as part of the request |
| */ |
| public void setRequestData(String requestData) { |
| this.requestData = requestData; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Sets whether the cross origin request will include credentials. |
| * |
| * @param withCredentials whether to include credentials in XHR |
| */ |
| public void setIncludeCredentials(boolean includeCredentials) { |
| this.includeCredentials = includeCredentials; |
| } |
| |
| /** |
| * 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". |
| * |
| * @return a {@link Request} object that can be used to track the request |
| * @throws RequestException if the call fails to initiate |
| * @throws NullPointerException if request data has not been set |
| * @throws NullPointerException if a request callback has not been set |
| */ |
| private Request doSend(String requestData, final RequestCallback callback) |
| throws RequestException { |
| XMLHttpRequest xmlHttpRequest = XMLHttpRequest.create(); |
| |
| try { |
| if (user != null && password != null) { |
| xmlHttpRequest.open(httpMethod, url, user, password); |
| } else if (user != null) { |
| xmlHttpRequest.open(httpMethod, url, user); |
| } else { |
| xmlHttpRequest.open(httpMethod, url); |
| } |
| } catch (JavaScriptException e) { |
| RequestPermissionException requestPermissionException = new RequestPermissionException( |
| url); |
| requestPermissionException.initCause(new RequestException(e.getMessage())); |
| throw requestPermissionException; |
| } |
| |
| setHeaders(xmlHttpRequest); |
| if (includeCredentials) { |
| xmlHttpRequest.setWithCredentials(true); |
| } |
| |
| final Request request = new Request(xmlHttpRequest, timeoutMillis, callback); |
| |
| // Must set the onreadystatechange handler before calling send(). |
| xmlHttpRequest.setOnReadyStateChange(new ReadyStateChangeHandler() { |
| public void onReadyStateChange(XMLHttpRequest xhr) { |
| if (xhr.getReadyState() == XMLHttpRequest.DONE) { |
| xhr.clearOnReadyStateChange(); |
| request.fireOnResponseReceived(callback); |
| } |
| } |
| }); |
| |
| try { |
| xmlHttpRequest.send(requestData); |
| } catch (JavaScriptException e) { |
| throw new RequestException(e.getMessage()); |
| } |
| |
| return request; |
| } |
| |
| /* |
| * 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(XMLHttpRequest xmlHttpRequest) |
| throws RequestException { |
| if (headers != null && headers.size() > 0) { |
| for (Map.Entry<String, String> header : headers.entrySet()) { |
| try { |
| xmlHttpRequest.setRequestHeader(header.getKey(), header.getValue()); |
| } catch (JavaScriptException e) { |
| throw new RequestException(e.getMessage()); |
| } |
| } |
| } else { |
| xmlHttpRequest.setRequestHeader("Content-Type", |
| "text/plain; charset=utf-8"); |
| } |
| } |
| } |