summaryrefslogtreecommitdiffstats
path: root/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/AsynchronousValidationRequest.java
blob: 60ad6ca1f56cc8e9a24ce986c713018f537519ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 * ====================================================================
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */
package ch.boye.httpclientandroidlib.impl.client.cache;

import java.io.IOException;

import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
/* LogFactory removed by HttpClient for Android script. */
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpException;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.cache.HeaderConstants;
import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;

/**
 * Class used to represent an asynchronous revalidation event, such as with
 * "stale-while-revalidate"
 */
class AsynchronousValidationRequest implements Runnable {
    private final AsynchronousValidator parent;
    private final CachingExec cachingExec;
    private final HttpRoute route;
    private final HttpRequestWrapper request;
    private final HttpClientContext context;
    private final HttpExecutionAware execAware;
    private final HttpCacheEntry cacheEntry;
    private final String identifier;
    private final int consecutiveFailedAttempts;

    public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());

    /**
     * Used internally by {@link AsynchronousValidator} to schedule a
     * revalidation.
     * @param request
     * @param context
     * @param cacheEntry
     * @param identifier
     * @param consecutiveFailedAttempts
     */
    AsynchronousValidationRequest(
            final AsynchronousValidator parent,
            final CachingExec cachingExec,
            final HttpRoute route,
            final HttpRequestWrapper request,
            final HttpClientContext context,
            final HttpExecutionAware execAware,
            final HttpCacheEntry cacheEntry,
            final String identifier,
            final int consecutiveFailedAttempts) {
        this.parent = parent;
        this.cachingExec = cachingExec;
        this.route = route;
        this.request = request;
        this.context = context;
        this.execAware = execAware;
        this.cacheEntry = cacheEntry;
        this.identifier = identifier;
        this.consecutiveFailedAttempts = consecutiveFailedAttempts;
    }

    public void run() {
        try {
            if (revalidateCacheEntry()) {
                parent.jobSuccessful(identifier);
            } else {
                parent.jobFailed(identifier);
            }
        } finally {
            parent.markComplete(identifier);
        }
    }

    /**
     * Revalidate the cache entry and return if the operation was successful.
     * Success means a connection to the server was established and replay did
     * not indicate a server error.
     * @return <code>true</code> if the cache entry was successfully validated;
     * otherwise <code>false</code>
     */
    protected boolean revalidateCacheEntry() {
        try {
            final CloseableHttpResponse httpResponse = cachingExec.revalidateCacheEntry(route, request, context, execAware, cacheEntry);
            try {
                final int statusCode = httpResponse.getStatusLine().getStatusCode();
                return isNotServerError(statusCode) && isNotStale(httpResponse);
            } finally {
                httpResponse.close();
            }
        } catch (final IOException ioe) {
            log.debug("Asynchronous revalidation failed due to I/O error", ioe);
            return false;
        } catch (final HttpException pe) {
            log.error("HTTP protocol exception during asynchronous revalidation", pe);
            return false;
        } catch (final RuntimeException re) {
            log.error("RuntimeException thrown during asynchronous revalidation: " + re);
            return false;
        }
    }

    /**
     * Return whether the status code indicates a server error or not.
     * @param statusCode the status code to be checked
     * @return if the status code indicates a server error or not
     */
    private boolean isNotServerError(final int statusCode) {
        return statusCode < 500;
    }

    /**
     * Try to detect if the returned response is generated from a stale cache entry.
     * @param httpResponse the response to be checked
     * @return whether the response is stale or not
     */
    private boolean isNotStale(final HttpResponse httpResponse) {
        final Header[] warnings = httpResponse.getHeaders(HeaderConstants.WARNING);
        if (warnings != null)
        {
            for (final Header warning : warnings)
            {
                /**
                 * warn-codes
                 * 110 = Response is stale
                 * 111 = Revalidation failed
                 */
                final String warningValue = warning.getValue();
                if (warningValue.startsWith("110") || warningValue.startsWith("111"))
                {
                    return false;
                }
            }
        }
        return true;
    }

    String getIdentifier() {
        return identifier;
    }

    /**
     * The number of consecutively failed revalidation attempts.
     * @return the number of consecutively failed revalidation attempts.
     */
    public int getConsecutiveFailedAttempts() {
        return consecutiveFailedAttempts;
    }

}