Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
21478 rajender 1
/*
2
 * Copyright (C) 2011 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
package com.android.volley;
18
 
19
import android.net.TrafficStats;
20
import android.net.Uri;
21
import android.os.Handler;
22
import android.os.Looper;
23
import android.text.TextUtils;
24
 
25
import com.android.volley.VolleyLog.MarkerLog;
26
 
27
import java.io.UnsupportedEncodingException;
28
import java.net.URLEncoder;
29
import java.util.Collections;
30
import java.util.Map;
31
 
32
/**
33
 * Base class for all network requests.
34
 *
35
 * @param <T> The type of parsed response this request expects.
36
 */
37
public abstract class Request<T> implements Comparable<Request<T>> {
38
 
39
    /**
40
     * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
41
     */
42
    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
43
 
44
    /**
45
     * Supported request methods.
46
     */
47
    public interface Method {
48
        int DEPRECATED_GET_OR_POST = -1;
49
        int GET = 0;
50
        int POST = 1;
51
        int PUT = 2;
52
        int DELETE = 3;
53
        int HEAD = 4;
54
        int OPTIONS = 5;
55
        int TRACE = 6;
56
        int PATCH = 7;
57
    }
58
 
59
    /** An event log tracing the lifetime of this request; for debugging. */
60
    private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
61
 
62
    /**
63
     * Request method of this request.  Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
64
     * TRACE, and PATCH.
65
     */
66
    private final int mMethod;
67
 
68
    /** URL of this request. */
69
    private final String mUrl;
70
 
71
    /** Default tag for {@link TrafficStats}. */
72
    private final int mDefaultTrafficStatsTag;
73
 
74
    /** Listener interface for errors. */
75
    private final Response.ErrorListener mErrorListener;
76
 
77
    /** Sequence number of this request, used to enforce FIFO ordering. */
78
    private Integer mSequence;
79
 
80
    /** The request queue this request is associated with. */
81
    private RequestQueue mRequestQueue;
82
 
83
    /** Whether or not responses to this request should be cached. */
84
    private boolean mShouldCache = true;
85
 
86
    /** Whether or not this request has been canceled. */
87
    private boolean mCanceled = false;
88
 
89
    /** Whether or not a response has been delivered for this request yet. */
90
    private boolean mResponseDelivered = false;
91
 
92
    /** The retry policy for this request. */
93
    private RetryPolicy mRetryPolicy;
94
 
95
    /**
96
     * When a request can be retrieved from cache but must be refreshed from
97
     * the network, the cache entry will be stored here so that in the event of
98
     * a "Not Modified" response, we can be sure it hasn't been evicted from cache.
99
     */
100
    private Cache.Entry mCacheEntry = null;
101
 
102
    /** An opaque token tagging this request; used for bulk cancellation. */
103
    private Object mTag;
104
 
105
    /**
106
     * Creates a new request with the given URL and error listener.  Note that
107
     * the normal response listener is not provided here as delivery of responses
108
     * is provided by subclasses, who have a better idea of how to deliver an
109
     * already-parsed response.
110
     *
111
     * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
112
     */
113
    @Deprecated
114
    public Request(String url, Response.ErrorListener listener) {
115
        this(Method.DEPRECATED_GET_OR_POST, url, listener);
116
    }
117
 
118
    /**
119
     * Creates a new request with the given method (one of the values from {@link Method}),
120
     * URL, and error listener.  Note that the normal response listener is not provided here as
121
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
122
     * an already-parsed response.
123
     */
124
    public Request(int method, String url, Response.ErrorListener listener) {
125
        mMethod = method;
126
        mUrl = url;
127
        mErrorListener = listener;
128
        setRetryPolicy(new DefaultRetryPolicy());
129
 
130
        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
131
    }
132
 
133
    /**
134
     * Return the method for this request.  Can be one of the values in {@link Method}.
135
     */
136
    public int getMethod() {
137
        return mMethod;
138
    }
139
 
140
    /**
141
     * Set a tag on this request. Can be used to cancel all requests with this
142
     * tag by {@link RequestQueue#cancelAll(Object)}.
143
     *
144
     * @return This Request object to allow for chaining.
145
     */
146
    public Request<?> setTag(Object tag) {
147
        mTag = tag;
148
        return this;
149
    }
150
 
151
    /**
152
     * Returns this request's tag.
153
     * @see Request#setTag(Object)
154
     */
155
    public Object getTag() {
156
        return mTag;
157
    }
158
 
159
    /**
160
     * @return this request's {@link com.android.volley.Response.ErrorListener}.
161
     */
162
    public Response.ErrorListener getErrorListener() {
163
        return mErrorListener;
164
    }
165
 
166
    /**
167
     * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
168
     */
169
    public int getTrafficStatsTag() {
170
        return mDefaultTrafficStatsTag;
171
    }
172
 
173
    /**
174
     * @return The hashcode of the URL's host component, or 0 if there is none.
175
     */
176
    private static int findDefaultTrafficStatsTag(String url) {
177
        if (!TextUtils.isEmpty(url)) {
178
            Uri uri = Uri.parse(url);
179
            if (uri != null) {
180
                String host = uri.getHost();
181
                if (host != null) {
182
                    return host.hashCode();
183
                }
184
            }
185
        }
186
        return 0;
187
    }
188
 
189
    /**
190
     * Sets the retry policy for this request.
191
     *
192
     * @return This Request object to allow for chaining.
193
     */
194
    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
195
        mRetryPolicy = retryPolicy;
196
        return this;
197
    }
198
 
199
    /**
200
     * Adds an event to this request's event log; for debugging.
201
     */
202
    public void addMarker(String tag) {
203
        if (MarkerLog.ENABLED) {
204
            mEventLog.add(tag, Thread.currentThread().getId());
205
        }
206
    }
207
 
208
    /**
209
     * Notifies the request queue that this request has finished (successfully or with error).
210
     *
211
     * <p>Also dumps all events from this request's event log; for debugging.</p>
212
     */
213
    void finish(final String tag) {
214
        if (mRequestQueue != null) {
215
            mRequestQueue.finish(this);
216
        }
217
        if (MarkerLog.ENABLED) {
218
            final long threadId = Thread.currentThread().getId();
219
            if (Looper.myLooper() != Looper.getMainLooper()) {
220
                // If we finish marking off of the main thread, we need to
221
                // actually do it on the main thread to ensure correct ordering.
222
                Handler mainThread = new Handler(Looper.getMainLooper());
223
                mainThread.post(new Runnable() {
224
                    @Override
225
                    public void run() {
226
                        mEventLog.add(tag, threadId);
227
                        mEventLog.finish(this.toString());
228
                    }
229
                });
230
                return;
231
            }
232
 
233
            mEventLog.add(tag, threadId);
234
            mEventLog.finish(this.toString());
235
        }
236
    }
237
 
238
    /**
239
     * Associates this request with the given queue. The request queue will be notified when this
240
     * request has finished.
241
     *
242
     * @return This Request object to allow for chaining.
243
     */
244
    public Request<?> setRequestQueue(RequestQueue requestQueue) {
245
        mRequestQueue = requestQueue;
246
        return this;
247
    }
248
 
249
    /**
250
     * Sets the sequence number of this request.  Used by {@link RequestQueue}.
251
     *
252
     * @return This Request object to allow for chaining.
253
     */
254
    public final Request<?> setSequence(int sequence) {
255
        mSequence = sequence;
256
        return this;
257
    }
258
 
259
    /**
260
     * Returns the sequence number of this request.
261
     */
262
    public final int getSequence() {
263
        if (mSequence == null) {
264
            throw new IllegalStateException("getSequence called before setSequence");
265
        }
266
        return mSequence;
267
    }
268
 
269
    /**
270
     * Returns the URL of this request.
271
     */
272
    public String getUrl() {
273
        return mUrl;
274
    }
275
 
276
    /**
277
     * Returns the cache key for this request.  By default, this is the URL.
278
     */
279
    public String getCacheKey() {
280
        return getUrl();
281
    }
282
 
283
    /**
284
     * Annotates this request with an entry retrieved for it from cache.
285
     * Used for cache coherency support.
286
     *
287
     * @return This Request object to allow for chaining.
288
     */
289
    public Request<?> setCacheEntry(Cache.Entry entry) {
290
        mCacheEntry = entry;
291
        return this;
292
    }
293
 
294
    /**
295
     * Returns the annotated cache entry, or null if there isn't one.
296
     */
297
    public Cache.Entry getCacheEntry() {
298
        return mCacheEntry;
299
    }
300
 
301
    /**
302
     * Mark this request as canceled.  No callback will be delivered.
303
     */
304
    public void cancel() {
305
        mCanceled = true;
306
    }
307
 
308
    /**
309
     * Returns true if this request has been canceled.
310
     */
311
    public boolean isCanceled() {
312
        return mCanceled;
313
    }
314
 
315
    /**
316
     * Returns a list of extra HTTP headers to go along with this request. Can
317
     * throw {@link AuthFailureError} as authentication may be required to
318
     * provide these values.
319
     * @throws AuthFailureError In the event of auth failure
320
     */
321
    public Map<String, String> getHeaders() throws AuthFailureError {
322
        return Collections.emptyMap();
323
    }
324
 
325
    /**
326
     * Returns a Map of POST parameters to be used for this request, or null if
327
     * a simple GET should be used.  Can throw {@link AuthFailureError} as
328
     * authentication may be required to provide these values.
329
     *
330
     * <p>Note that only one of getPostParams() and getPostBody() can return a non-null
331
     * value.</p>
332
     * @throws AuthFailureError In the event of auth failure
333
     *
334
     * @deprecated Use {@link #getParams()} instead.
335
     */
336
    @Deprecated
337
    protected Map<String, String> getPostParams() throws AuthFailureError {
338
        return getParams();
339
    }
340
 
341
    /**
342
     * Returns which encoding should be used when converting POST parameters returned by
343
     * {@link #getPostParams()} into a raw POST body.
344
     *
345
     * <p>This controls both encodings:
346
     * <ol>
347
     *     <li>The string encoding used when converting parameter names and values into bytes prior
348
     *         to URL encoding them.</li>
349
     *     <li>The string encoding used when converting the URL encoded parameters into a raw
350
     *         byte array.</li>
351
     * </ol>
352
     *
353
     * @deprecated Use {@link #getParamsEncoding()} instead.
354
     */
355
    @Deprecated
356
    protected String getPostParamsEncoding() {
357
        return getParamsEncoding();
358
    }
359
 
360
    /**
361
     * @deprecated Use {@link #getBodyContentType()} instead.
362
     */
363
    @Deprecated
364
    public String getPostBodyContentType() {
365
        return getBodyContentType();
366
    }
367
 
368
    /**
369
     * Returns the raw POST body to be sent.
370
     *
371
     * @throws AuthFailureError In the event of auth failure
372
     *
373
     * @deprecated Use {@link #getBody()} instead.
374
     */
375
    @Deprecated
376
    public byte[] getPostBody() throws AuthFailureError {
377
        // Note: For compatibility with legacy clients of volley, this implementation must remain
378
        // here instead of simply calling the getBody() function because this function must
379
        // call getPostParams() and getPostParamsEncoding() since legacy clients would have
380
        // overridden these two member functions for POST requests.
381
        Map<String, String> postParams = getPostParams();
382
        if (postParams != null && postParams.size() > 0) {
383
            return encodeParameters(postParams, getPostParamsEncoding());
384
        }
385
        return null;
386
    }
387
 
388
    /**
389
     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw
390
     * {@link AuthFailureError} as authentication may be required to provide these values.
391
     *
392
     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
393
     *
394
     * @throws AuthFailureError in the event of auth failure
395
     */
396
    protected Map<String, String> getParams() throws AuthFailureError {
397
        return null;
398
    }
399
 
400
    /**
401
     * Returns which encoding should be used when converting POST or PUT parameters returned by
402
     * {@link #getParams()} into a raw POST or PUT body.
403
     *
404
     * <p>This controls both encodings:
405
     * <ol>
406
     *     <li>The string encoding used when converting parameter names and values into bytes prior
407
     *         to URL encoding them.</li>
408
     *     <li>The string encoding used when converting the URL encoded parameters into a raw
409
     *         byte array.</li>
410
     * </ol>
411
     */
412
    protected String getParamsEncoding() {
413
        return DEFAULT_PARAMS_ENCODING;
414
    }
415
 
416
    /**
417
     * Returns the content type of the POST or PUT body.
418
     */
419
    public String getBodyContentType() {
420
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
421
    }
422
 
423
    /**
424
     * Returns the raw POST or PUT body to be sent.
425
     *
426
     * <p>By default, the body consists of the request parameters in
427
     * application/x-www-form-urlencoded format. When overriding this method, consider overriding
428
     * {@link #getBodyContentType()} as well to match the new body format.
429
     *
430
     * @throws AuthFailureError in the event of auth failure
431
     */
432
    public byte[] getBody() throws AuthFailureError {
433
        Map<String, String> params = getParams();
434
        if (params != null && params.size() > 0) {
435
            return encodeParameters(params, getParamsEncoding());
436
        }
437
        return null;
438
    }
439
 
440
    /**
441
     * Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
442
     */
443
    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
444
        StringBuilder encodedParams = new StringBuilder();
445
        try {
446
            for (Map.Entry<String, String> entry : params.entrySet()) {
447
                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
448
                encodedParams.append('=');
449
                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
450
                encodedParams.append('&');
451
            }
452
            return encodedParams.toString().getBytes(paramsEncoding);
453
        } catch (UnsupportedEncodingException uee) {
454
            throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
455
        }
456
    }
457
 
458
    /**
459
     * Set whether or not responses to this request should be cached.
460
     *
461
     * @return This Request object to allow for chaining.
462
     */
463
    public final Request<?> setShouldCache(boolean shouldCache) {
464
        mShouldCache = shouldCache;
465
        return this;
466
    }
467
 
468
    /**
469
     * Returns true if responses to this request should be cached.
470
     */
471
    public final boolean shouldCache() {
472
        return mShouldCache;
473
    }
474
 
475
    /**
476
     * Priority values.  Requests will be processed from higher priorities to
477
     * lower priorities, in FIFO order.
478
     */
479
    public enum Priority {
480
        LOW,
481
        NORMAL,
482
        HIGH,
483
        IMMEDIATE
484
    }
485
 
486
    /**
487
     * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
488
     */
489
    public Priority getPriority() {
490
        return Priority.NORMAL;
491
    }
492
 
493
    /**
494
     * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
495
     * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
496
     * attempts remaining, this will cause delivery of a {@link TimeoutError} error.
497
     */
498
    public final int getTimeoutMs() {
499
        return mRetryPolicy.getCurrentTimeout();
500
    }
501
 
502
    /**
503
     * Returns the retry policy that should be used  for this request.
504
     */
505
    public RetryPolicy getRetryPolicy() {
506
        return mRetryPolicy;
507
    }
508
 
509
    /**
510
     * Mark this request as having a response delivered on it.  This can be used
511
     * later in the request's lifetime for suppressing identical responses.
512
     */
513
    public void markDelivered() {
514
        mResponseDelivered = true;
515
    }
516
 
517
    /**
518
     * Returns true if this request has had a response delivered for it.
519
     */
520
    public boolean hasHadResponseDelivered() {
521
        return mResponseDelivered;
522
    }
523
 
524
    /**
525
     * Subclasses must implement this to parse the raw network response
526
     * and return an appropriate response type. This method will be
527
     * called from a worker thread.  The response will not be delivered
528
     * if you return null.
529
     * @param response Response from the network
530
     * @return The parsed response, or null in the case of an error
531
     */
532
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
533
 
534
    /**
535
     * Subclasses can override this method to parse 'networkError' and return a more specific error.
536
     *
537
     * <p>The default implementation just returns the passed 'networkError'.</p>
538
     *
539
     * @param volleyError the error retrieved from the network
540
     * @return an NetworkError augmented with additional information
541
     */
542
    protected VolleyError parseNetworkError(VolleyError volleyError) {
543
        return volleyError;
544
    }
545
 
546
    /**
547
     * Subclasses must implement this to perform delivery of the parsed
548
     * response to their listeners.  The given response is guaranteed to
549
     * be non-null; responses that fail to parse are not delivered.
550
     * @param response The parsed response returned by
551
     * {@link #parseNetworkResponse(NetworkResponse)}
552
     */
553
    abstract protected void deliverResponse(T response);
554
 
555
    /**
556
     * Delivers error message to the ErrorListener that the Request was
557
     * initialized with.
558
     *
559
     * @param error Error details
560
     */
561
    public void deliverError(VolleyError error) {
562
        if (mErrorListener != null) {
563
            mErrorListener.onErrorResponse(error);
564
        }
565
    }
566
 
567
    /**
568
     * Our comparator sorts from high to low priority, and secondarily by
569
     * sequence number to provide FIFO ordering.
570
     */
571
    @Override
572
    public int compareTo(Request<T> other) {
573
        Priority left = this.getPriority();
574
        Priority right = other.getPriority();
575
 
576
        // High-priority requests are "lesser" so they are sorted to the front.
577
        // Equal priorities are sorted by sequence number to provide FIFO ordering.
578
        return left == right ?
579
                this.mSequence - other.mSequence :
580
                right.ordinal() - left.ordinal();
581
    }
582
 
583
    @Override
584
    public String toString() {
585
        String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
586
        return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
587
                + getPriority() + " " + mSequence;
588
    }
589
}