KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > gargoylesoftware > htmlunit > javascript > host > XMLHttpRequest


1 /*
2  * Copyright (c) 2002, 2005 Gargoyle Software Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12  * 3. The end-user documentation included with the redistribution, if any, must
13  * include the following acknowledgment:
14  *
15  * "This product includes software developed by Gargoyle Software Inc.
16  * (http://www.GargoyleSoftware.com/)."
17  *
18  * Alternately, this acknowledgment may appear in the software itself, if
19  * and wherever such third-party acknowledgments normally appear.
20  * 4. The name "Gargoyle Software" must not be used to endorse or promote
21  * products derived from this software without prior written permission.
22  * For written permission, please contact info@GargoyleSoftware.com.
23  * 5. Products derived from this software may not be called "HtmlUnit", nor may
24  * "HtmlUnit" appear in their name, without prior written permission of
25  * Gargoyle Software Inc.
26  *
27  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
28  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
30  * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
33  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37  */

38 package com.gargoylesoftware.htmlunit.javascript.host;
39
40 import java.io.IOException JavaDoc;
41 import java.net.MalformedURLException JavaDoc;
42 import java.net.URL JavaDoc;
43 import java.util.Iterator JavaDoc;
44 import java.util.List JavaDoc;
45
46 import org.org.apache.commons.httpclient.NameValuePair;
47 import org.mozilla.javascript.Context;
48 import org.mozilla.javascript.Function;
49 import org.mozilla.javascript.Scriptable;
50
51 import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider;
52 import com.gargoylesoftware.htmlunit.Page;
53 import com.gargoylesoftware.htmlunit.SubmitMethod;
54 import com.gargoylesoftware.htmlunit.WebClient;
55 import com.gargoylesoftware.htmlunit.WebRequestSettings;
56 import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
57 import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
58 import com.gargoylesoftware.htmlunit.xml.XmlPage;
59
60 /**
61  * A JavaScript object for a XMLHttpRequest.
62  *
63  * @author Daniel Gredler
64  * @version $Revision: 100 $
65  * @see <a HREF="http://developer.apple.com/internet/webcontent/xmlhttpreq.html">Safari documentation</a>
66  */

67 public class XMLHttpRequest extends SimpleScriptable {
68
69     private static final long serialVersionUID = 2369039843039430664L;
70
71     /** The object has been created, but not initialized (the open() method has not been called). */
72     public static final int STATE_UNINITIALIZED = 0;
73     /** The object has been created, but the send() method has not been called. */
74     public static final int STATE_LOADING = 1;
75     /** The send() method has been called, but the status and headers are not yet available. */
76     public static final int STATE_LOADED = 2;
77     /** Some data has been received. */
78     public static final int STATE_INTERACTIVE = 3;
79     /** All the data has been received; the complete data is available in responseBody and responseText. */
80     public static final int STATE_COMPLETED = 4;
81
82     private int state_;
83     private Function stateChangeHandler_;
84     private WebRequestSettings requestSettings_;
85     private boolean async_;
86     private Thread JavaDoc requestThread_;
87     private XmlPage page_;
88
89     /**
90      * Creates a new instance. JavaScript objects must have a default constructor.
91      */

92     public XMLHttpRequest() {
93         state_ = STATE_UNINITIALIZED;
94     }
95
96     /**
97      * Returns the event handler that fires on every state change.
98      * @return The event handler that fires on every state change.
99      */

100     public Function jsxGet_onreadystatechange() {
101         return stateChangeHandler_;
102     }
103
104     /**
105      * Sets the event handler that fires on every state change.
106      * @param stateChangeHandler The event handler that fires on every state change.
107      */

108     public void jsxSet_onreadystatechange( Function stateChangeHandler ) {
109         stateChangeHandler_ = stateChangeHandler;
110     }
111
112     /**
113      * Sets the state as specified and invokes the state change handler if one has been set.
114      * @param state The new state.
115      * @param context The context within which the state change handler is to be invoked;
116      * if <tt>null</tt>, the current thread's context is used.
117      */

118     private void setState( final int state, Context context ) {
119         state_ = state;
120         if( stateChangeHandler_ != null ) {
121             if( context == null ) {
122                 context = Context.getCurrentContext();
123             }
124             final Scriptable scope = stateChangeHandler_.getParentScope();
125             final Object JavaDoc[] args = new Object JavaDoc[ 0 ];
126             stateChangeHandler_.call( context, scope, this, args );
127         }
128     }
129
130     /**
131      * Returns the current state of the HTTP request. The possible values are:
132      * <ul>
133      * <li>0 = uninitialized</li>
134      * <li>1 = loading</li>
135      * <li>2 = loaded</li>
136      * <li>3 = interactive</li>
137      * <li>4 = complete</li>
138      * </ul>
139      * @return The current state of the HTTP request.
140      */

141     public int jsxGet_readyState() {
142         return state_;
143     }
144
145     /**
146      * Returns a string version of the data retrieved from the server.
147      * @return A string version of the data retrieved from the server.
148      */

149     public String JavaDoc jsxGet_responseText() {
150         if( page_ != null ) {
151             return page_.getContent();
152         }
153         else {
154             getLog().debug( "XMLHttpRequest.responseText was retrieved before the response was available." );
155             return "";
156         }
157     }
158
159     /**
160      * Returns a DOM-compatible document object version of the data retrieved from the server.
161      * @return A DOM-compatible document object version of the data retrieved from the server.
162      */

163     public Object JavaDoc jsxGet_responseXML() {
164         if( page_ != null ) {
165             return page_.getXmlDocument();
166         }
167         else {
168             getLog().error( "XMLHttpRequest.responseXML was retrieved before the response was available." );
169             return null;
170         }
171     }
172
173     /**
174      * Returns the numeric status returned by the server, such as 404 for "Not Found"
175      * or 200 for "OK".
176      * @return The numeric status returned by the server.
177      */

178     public int jsxGet_status() {
179         if( page_ != null ) {
180             return page_.getWebResponse().getStatusCode();
181         }
182         else {
183             getLog().error( "XMLHttpRequest.status was retrieved before the response was available." );
184             return 0;
185         }
186     }
187
188     /**
189      * Returns the string message accompanying the status code, such as "Not Found" or "OK".
190      * @return The string message accompanying the status code.
191      */

192     public String JavaDoc jsxGet_statusText() {
193         if( page_ != null ) {
194             return page_.getWebResponse().getStatusMessage();
195         }
196         else {
197             getLog().error( "XMLHttpRequest.statusText was retrieved before the response was available." );
198             return null;
199         }
200     }
201
202     /**
203      * Cancels the current HTTP request.
204      */

205     public void jsxFunction_abort() {
206         if( requestThread_ != null ) {
207             requestThread_.interrupt();
208         }
209     }
210
211     /**
212      * Returns the labels and values of all the HTTP headers.
213      * @return The labels and values of all the HTTP headers.
214      */

215     public String JavaDoc jsxFunction_getAllResponseHeaders() {
216         if( page_ != null ) {
217             StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
218             final List JavaDoc headers = page_.getWebResponse().getResponseHeaders();
219             for( final Iterator JavaDoc i = headers.iterator(); i.hasNext(); ) {
220                 NameValuePair header = (NameValuePair) i.next();
221                 buffer.append(header.getName()).append(": ").append(header.getValue()).append("\n");
222             }
223             return buffer.toString();
224         }
225         else {
226             getLog().error( "XMLHttpRequest.getAllResponseHeaders() was called before the response was available." );
227             return null;
228         }
229     }
230
231     /**
232      * Retrieves the value of an HTTP header from the response body.
233      * @param headerName The (case-insensitive) name of the header to retrieve.
234      * @return The value of the specified HTTP header.
235      */

236     public String JavaDoc jsxFunction_getResponseHeader( final String JavaDoc headerName ) {
237         if( page_ != null ) {
238             return page_.getWebResponse().getResponseHeaderValue( headerName );
239         }
240         else {
241             getLog().error( "XMLHttpRequest.getResponseHeader() was called before the response was available." );
242             return null;
243         }
244     }
245
246     /**
247      * Assigns the destination URL, method and other optional attributes of a pending request.
248      * @param method The method to use to send the request to the server (GET, POST, etc).
249      * @param url The url to send the request to.
250      * @param async Whether or not to send the request to the server asynchronously.
251      * @param user If authentication is needed for the specified URL, the username to use to authenticate.
252      * @param password If authentication is needed for the specified URL, the password to use to authenticate.
253      */

254     public void jsxFunction_open( final String JavaDoc method, final String JavaDoc url, final boolean async,
255         final String JavaDoc user, final String JavaDoc password ) {
256         // (URL + Method + User + Password) become a WebRequestSettings instance.
257
try {
258             WebRequestSettings settings = new WebRequestSettings( new URL JavaDoc( url ) );
259             SubmitMethod submitMethod;
260             if( "POST".equalsIgnoreCase( method ) ) {
261                 submitMethod = SubmitMethod.POST;
262             }
263             else {
264                 submitMethod = SubmitMethod.GET;
265             }
266             settings.setSubmitMethod( submitMethod );
267             if( user != null ) {
268                 DefaultCredentialsProvider dcp = new DefaultCredentialsProvider();
269                 dcp.addCredentials( user, password );
270                 settings.setCredentialsProvider( dcp );
271             }
272             requestSettings_ = settings;
273         }
274         catch( final MalformedURLException JavaDoc e ) {
275             getLog().error( "Unable to initialize XMLHttpRequest using malformed URL '" + url + "'." );
276             return;
277         }
278         // Async stays a boolean.
279
async_ = async;
280         // Change the state!
281
setState( STATE_LOADING, null );
282     }
283
284     /**
285      * Sends the specified content to the server in an HTTP request and receives the response.
286      * @param content The body of the message being sent with the request.
287      */

288     public void jsxFunction_send( final String JavaDoc content ) {
289         // Create and start a thread in which to execute the request.
290
final WebClient wc = JavaScriptEngine.getWebClientForCurrentThread();
291         final Context context = Context.getCurrentContext();
292         final Thread JavaDoc t = new Thread JavaDoc( "XMLHttpRequest.send() Thread" ) {
293             public void run() {
294                 try {
295                     setState( STATE_LOADED, context );
296                     if( content != null && ! "undefined".equals( content ) ) {
297                         requestSettings_.setRequestBody( content );
298                     }
299                     final Page page = wc.getPage( requestSettings_ );
300                     final String JavaDoc contentType = page.getWebResponse().getContentType();
301                     if( "text/xml".equals( contentType ) == false ) {
302                         setState( STATE_LOADING, context );
303                         String JavaDoc msg = "The Content-Type of the data returned from the server must be 'text/xml'; " +
304                             "actual content type was '" + contentType + "'.";
305                         throw Context.reportRuntimeError( msg );
306                     }
307                     page_ = (XmlPage) page;
308                     setState( STATE_INTERACTIVE, context );
309                     setState( STATE_COMPLETED, context );
310                 }
311                 catch( final IOException JavaDoc e ) {
312                     setState( STATE_LOADING, context );
313                     throw Context.reportRuntimeError( "Unable to send the XMLHttpRequest: " + e );
314                 }
315             }
316         };
317         requestThread_ = t;
318         requestThread_.start();
319         // If the call is to be synchronous, wait for the thread to return.
320
if( ! async_ ) {
321             try {
322                 requestThread_.join();
323             }
324             catch( final InterruptedException JavaDoc e ) {
325                 getLog().info( t.getName() + " interrupted; abort() probably called." );
326             }
327         }
328     }
329
330     /**
331      * Sets the specified header to the specified value. The <tt>open</tt> method must be
332      * called before this method, or an error will occur.
333      * @param name The name of the header being set.
334      * @param value The value of the header being set.
335      */

336     public void jsxFunction_setRequestHeader( final String JavaDoc name, final String JavaDoc value ) {
337         if( requestSettings_ != null ) {
338             requestSettings_.addAdditionalHeader( name, value );
339         }
340         else {
341             throw Context.reportRuntimeError( "The open() method must be called before setRequestHeader()." );
342         }
343     }
344
345 }
346
Popular Tags