summaryrefslogtreecommitdiffstats
path: root/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/client/cache/CacheConfig.java
blob: 964ea2719370c3d63ba06e4e1c6ecd5bb1d59326 (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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
/*
 * ====================================================================
 * 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 ch.boye.httpclientandroidlib.util.Args;

/**
 * <p>Java Beans-style configuration for a {@link CachingHttpClient}. Any class
 * in the caching module that has configuration options should take a
 * {@link CacheConfig} argument in one of its constructors. A
 * {@code CacheConfig} instance has sane and conservative defaults, so the
 * easiest way to specify options is to get an instance and then set just
 * the options you want to modify from their defaults.</p>
 *
 * <p><b>N.B.</b> This class is only for caching-specific configuration; to
 * configure the behavior of the rest of the client, configure the
 * {@link ch.boye.httpclientandroidlib.client.HttpClient} used as the &quot;backend&quot;
 * for the {@code CachingHttpClient}.</p>
 *
 * <p>Cache configuration can be grouped into the following categories:</p>
 *
 * <p><b>Cache size.</b> If the backend storage supports these limits, you
 * can specify the {@link CacheConfig#getMaxCacheEntries maximum number of
 * cache entries} as well as the {@link CacheConfig#getMaxObjectSizeBytes
 * maximum cacheable response body size}.</p>
 *
 * <p><b>Public/private caching.</b> By default, the caching module considers
 * itself to be a shared (public) cache, and will not, for example, cache
 * responses to requests with {@code Authorization} headers or responses
 * marked with {@code Cache-Control: private}. If, however, the cache
 * is only going to be used by one logical "user" (behaving similarly to a
 * browser cache), then you will want to {@link
 * CacheConfig#setSharedCache(boolean) turn off the shared cache setting}.</p>
 *
 * <p><b>303 caching</b>. RFC2616 explicitly disallows caching 303 responses;
 * however, the HTTPbis working group says they can be cached
 * if explicitly indicated in the response headers and permitted by the request method.
 * (They also indicate that disallowing 303 caching is actually an unintended
 * spec error in RFC2616).
 * This behavior is off by default, to err on the side of a conservative
 * adherence to the existing standard, but you may want to
 * {@link Builder#setAllow303Caching(boolean) enable it}.
 *
 * <p><b>Weak ETags on PUT/DELETE If-Match requests</b>. RFC2616 explicitly
 * prohibits the use of weak validators in non-GET requests, however, the
 * HTTPbis working group says while the limitation for weak validators on ranged
 * requests makes sense, weak ETag validation is useful on full non-GET
 * requests; e.g., PUT with If-Match. This behavior is off by default, to err on
 * the side of a conservative adherence to the existing standard, but you may
 * want to {@link Builder#setWeakETagOnPutDeleteAllowed(boolean) enable it}.
 *
 * <p><b>Heuristic caching</b>. Per RFC2616, a cache may cache certain cache
 * entries even if no explicit cache control headers are set by the origin.
 * This behavior is off by default, but you may want to turn this on if you
 * are working with an origin that doesn't set proper headers but where you
 * still want to cache the responses. You will want to {@link
 * CacheConfig#setHeuristicCachingEnabled(boolean) enable heuristic caching},
 * then specify either a {@link CacheConfig#getHeuristicDefaultLifetime()
 * default freshness lifetime} and/or a {@link
 * CacheConfig#setHeuristicCoefficient(float) fraction of the time since
 * the resource was last modified}. See Sections
 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.2">
 * 13.2.2</a> and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.4">
 * 13.2.4</a> of the HTTP/1.1 RFC for more details on heuristic caching.</p>
 *
 * <p><b>Background validation</b>. The cache module supports the
 * {@code stale-while-revalidate} directive of
 * <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
 * certain cache entry revalidations to happen in the background. You may
 * want to tweak the settings for the {@link
 * CacheConfig#getAsynchronousWorkersCore() minimum} and {@link
 * CacheConfig#getAsynchronousWorkersMax() maximum} number of background
 * worker threads, as well as the {@link
 * CacheConfig#getAsynchronousWorkerIdleLifetimeSecs() maximum time they
 * can be idle before being reclaimed}. You can also control the {@link
 * CacheConfig#getRevalidationQueueSize() size of the queue} used for
 * revalidations when there aren't enough workers to keep up with demand.</b>
 */
public class CacheConfig implements Cloneable {

    /** Default setting for the maximum object size that will be
     * cached, in bytes.
     */
    public final static int DEFAULT_MAX_OBJECT_SIZE_BYTES = 8192;

    /** Default setting for the maximum number of cache entries
     * that will be retained.
     */
    public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000;

    /** Default setting for the number of retries on a failed
     * cache update
     */
    public final static int DEFAULT_MAX_UPDATE_RETRIES = 1;

    /** Default setting for 303 caching
     */
    public final static boolean DEFAULT_303_CACHING_ENABLED = false;

    /** Default setting to allow weak tags on PUT/DELETE methods
     */
    public final static boolean DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED = false;

    /** Default setting for heuristic caching
     */
    public final static boolean DEFAULT_HEURISTIC_CACHING_ENABLED = false;

    /** Default coefficient used to heuristically determine freshness
     * lifetime from the Last-Modified time of a cache entry.
     */
    public final static float DEFAULT_HEURISTIC_COEFFICIENT = 0.1f;

    /** Default lifetime in seconds to be assumed when we cannot calculate
     * freshness heuristically.
     */
    public final static long DEFAULT_HEURISTIC_LIFETIME = 0;

    /** Default number of worker threads to allow for background revalidations
     * resulting from the stale-while-revalidate directive.
     */
    public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;

    /** Default minimum number of worker threads to allow for background
     * revalidations resulting from the stale-while-revalidate directive.
     */
    public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;

    /** Default maximum idle lifetime for a background revalidation thread
     * before it gets reclaimed.
     */
    public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;

    /** Default maximum queue length for background revalidation requests.
     */
    public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;

    public static final CacheConfig DEFAULT = new Builder().build();

    // TODO: make final
    private long maxObjectSize;
    private int maxCacheEntries;
    private int maxUpdateRetries;
    private boolean allow303Caching;
    private boolean weakETagOnPutDeleteAllowed;
    private boolean heuristicCachingEnabled;
    private float heuristicCoefficient;
    private long heuristicDefaultLifetime;
    private boolean isSharedCache;
    private int asynchronousWorkersMax;
    private int asynchronousWorkersCore;
    private int asynchronousWorkerIdleLifetimeSecs;
    private int revalidationQueueSize;
    private boolean neverCacheHTTP10ResponsesWithQuery;

    /**
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public CacheConfig() {
        super();
        this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
        this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
        this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
        this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
        this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
        this.heuristicCachingEnabled = DEFAULT_HEURISTIC_CACHING_ENABLED;
        this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
        this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
        this.isSharedCache = true;
        this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
        this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
        this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
        this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
    }

    CacheConfig(
            final long maxObjectSize,
            final int maxCacheEntries,
            final int maxUpdateRetries,
            final boolean allow303Caching,
            final boolean weakETagOnPutDeleteAllowed,
            final boolean heuristicCachingEnabled,
            final float heuristicCoefficient,
            final long heuristicDefaultLifetime,
            final boolean isSharedCache,
            final int asynchronousWorkersMax,
            final int asynchronousWorkersCore,
            final int asynchronousWorkerIdleLifetimeSecs,
            final int revalidationQueueSize,
            final boolean neverCacheHTTP10ResponsesWithQuery) {
        super();
        this.maxObjectSize = maxObjectSize;
        this.maxCacheEntries = maxCacheEntries;
        this.maxUpdateRetries = maxUpdateRetries;
        this.allow303Caching = allow303Caching;
        this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
        this.heuristicCachingEnabled = heuristicCachingEnabled;
        this.heuristicCoefficient = heuristicCoefficient;
        this.heuristicDefaultLifetime = heuristicDefaultLifetime;
        this.isSharedCache = isSharedCache;
        this.asynchronousWorkersMax = asynchronousWorkersMax;
        this.asynchronousWorkersCore = asynchronousWorkersCore;
        this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
        this.revalidationQueueSize = revalidationQueueSize;
    }

    /**
     * Returns the current maximum response body size that will be cached.
     * @return size in bytes
     *
     * @deprecated (4.2)  use {@link #getMaxObjectSize()}
     */
    @Deprecated
    public int getMaxObjectSizeBytes() {
        return maxObjectSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxObjectSize;
    }

    /**
     * Specifies the maximum response body size that will be eligible for caching.
     * @param maxObjectSizeBytes size in bytes
     *
     * @deprecated (4.2)  use {@link Builder}.
     */
    @Deprecated
    public void setMaxObjectSizeBytes(final int maxObjectSizeBytes) {
        if (maxObjectSizeBytes > Integer.MAX_VALUE) {
            this.maxObjectSize = Integer.MAX_VALUE;
        } else {
            this.maxObjectSize = maxObjectSizeBytes;
        }
    }

    /**
     * Returns the current maximum response body size that will be cached.
     * @return size in bytes
     *
     * @since 4.2
     */
    public long getMaxObjectSize() {
        return maxObjectSize;
    }

    /**
     * Specifies the maximum response body size that will be eligible for caching.
     * @param maxObjectSize size in bytes
     *
     * @since 4.2
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setMaxObjectSize(final long maxObjectSize) {
        this.maxObjectSize = maxObjectSize;
    }

    /**
     * Returns whether the cache will never cache HTTP 1.0 responses with a query string or not.
     * @return {@code true} to not cache query string responses, {@code false} to cache if explicit cache headers are
     * found
     */
    public boolean isNeverCacheHTTP10ResponsesWithQuery() {
        return neverCacheHTTP10ResponsesWithQuery;
    }

    /**
     * Returns the maximum number of cache entries the cache will retain.
     */
    public int getMaxCacheEntries() {
        return maxCacheEntries;
    }

    /**
     * Sets the maximum number of cache entries the cache will retain.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setMaxCacheEntries(final int maxCacheEntries) {
        this.maxCacheEntries = maxCacheEntries;
    }

    /**
     * Returns the number of times to retry a cache update on failure
     */
    public int getMaxUpdateRetries(){
        return maxUpdateRetries;
    }

    /**
     * Sets the number of times to retry a cache update on failure
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setMaxUpdateRetries(final int maxUpdateRetries){
        this.maxUpdateRetries = maxUpdateRetries;
    }

    /**
     * Returns whether 303 caching is enabled.
     * @return {@code true} if it is enabled.
     */
    public boolean is303CachingEnabled() {
        return allow303Caching;
    }

    /**
     * Returns whether weak etags is allowed with PUT/DELETE methods.
     * @return {@code true} if it is allowed.
     */
    public boolean isWeakETagOnPutDeleteAllowed() {
        return weakETagOnPutDeleteAllowed;
    }

    /**
     * Returns whether heuristic caching is enabled.
     * @return {@code true} if it is enabled.
     */
    public boolean isHeuristicCachingEnabled() {
        return heuristicCachingEnabled;
    }

    /**
     * Enables or disables heuristic caching.
     * @param heuristicCachingEnabled should be {@code true} to
     *   permit heuristic caching, {@code false} to disable it.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
        this.heuristicCachingEnabled = heuristicCachingEnabled;
    }

    /**
     * Returns lifetime coefficient used in heuristic freshness caching.
     */
    public float getHeuristicCoefficient() {
        return heuristicCoefficient;
    }

    /**
     * Sets coefficient to be used in heuristic freshness caching. This is
     * interpreted as the fraction of the time between the {@code Last-Modified}
     * and {@code Date} headers of a cached response during which the cached
     * response will be considered heuristically fresh.
     * @param heuristicCoefficient should be between {@code 0.0} and
     *   {@code 1.0}.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setHeuristicCoefficient(final float heuristicCoefficient) {
        this.heuristicCoefficient = heuristicCoefficient;
    }

    /**
     * Get the default lifetime to be used if heuristic freshness calculation is
     * not possible.
     */
    public long getHeuristicDefaultLifetime() {
        return heuristicDefaultLifetime;
    }

    /**
     * Sets default lifetime in seconds to be used if heuristic freshness
     * calculation is not possible. Explicit cache control directives on
     * either the request or origin response will override this, as will
     * the heuristic {@code Last-Modified} freshness calculation if it is
     * available.
     * @param heuristicDefaultLifetimeSecs is the number of seconds to
     *   consider a cache-eligible response fresh in the absence of other
     *   information. Set this to {@code 0} to disable this style of
     *   heuristic caching.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setHeuristicDefaultLifetime(final long heuristicDefaultLifetimeSecs) {
        this.heuristicDefaultLifetime = heuristicDefaultLifetimeSecs;
    }

    /**
     * Returns whether the cache will behave as a shared cache or not.
     * @return {@code true} for a shared cache, {@code false} for a non-
     * shared (private) cache
     */
    public boolean isSharedCache() {
        return isSharedCache;
    }

    /**
     * Sets whether the cache should behave as a shared cache or not.
     * @param isSharedCache true to behave as a shared cache, false to
     * behave as a non-shared (private) cache. To have the cache
     * behave like a browser cache, you want to set this to {@code false}.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setSharedCache(final boolean isSharedCache) {
        this.isSharedCache = isSharedCache;
    }

    /**
     * Returns the maximum number of threads to allow for background
     * revalidations due to the {@code stale-while-revalidate} directive. A
     * value of 0 means background revalidations are disabled.
     */
    public int getAsynchronousWorkersMax() {
        return asynchronousWorkersMax;
    }

    /**
     * Sets the maximum number of threads to allow for background
     * revalidations due to the {@code stale-while-revalidate} directive.
     * @param max number of threads; a value of 0 disables background
     * revalidations.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setAsynchronousWorkersMax(final int max) {
        this.asynchronousWorkersMax = max;
    }

    /**
     * Returns the minimum number of threads to keep alive for background
     * revalidations due to the {@code stale-while-revalidate} directive.
     */
    public int getAsynchronousWorkersCore() {
        return asynchronousWorkersCore;
    }

    /**
     * Sets the minimum number of threads to keep alive for background
     * revalidations due to the {@code stale-while-revalidate} directive.
     * @param min should be greater than zero and less than or equal
     *   to <code>getAsynchronousWorkersMax()</code>
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setAsynchronousWorkersCore(final int min) {
        this.asynchronousWorkersCore = min;
    }

    /**
     * Returns the current maximum idle lifetime in seconds for a
     * background revalidation worker thread. If a worker thread is idle
     * for this long, and there are more than the core number of worker
     * threads alive, the worker will be reclaimed.
     */
    public int getAsynchronousWorkerIdleLifetimeSecs() {
        return asynchronousWorkerIdleLifetimeSecs;
    }

    /**
     * Sets the current maximum idle lifetime in seconds for a
     * background revalidation worker thread. If a worker thread is idle
     * for this long, and there are more than the core number of worker
     * threads alive, the worker will be reclaimed.
     * @param secs idle lifetime in seconds
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setAsynchronousWorkerIdleLifetimeSecs(final int secs) {
        this.asynchronousWorkerIdleLifetimeSecs = secs;
    }

    /**
     * Returns the current maximum queue size for background revalidations.
     */
    public int getRevalidationQueueSize() {
        return revalidationQueueSize;
    }

    /**
     * Sets the current maximum queue size for background revalidations.
     *
     * @deprecated (4.3) use {@link Builder}.
     */
    @Deprecated
    public void setRevalidationQueueSize(final int size) {
        this.revalidationQueueSize = size;
    }

    @Override
    protected CacheConfig clone() throws CloneNotSupportedException {
        return (CacheConfig) super.clone();
    }

    public static Builder custom() {
        return new Builder();
    }

    public static Builder copy(final CacheConfig config) {
        Args.notNull(config, "Cache config");
        return new Builder()
            .setMaxObjectSize(config.getMaxObjectSize())
            .setMaxCacheEntries(config.getMaxCacheEntries())
            .setMaxUpdateRetries(config.getMaxUpdateRetries())
            .setHeuristicCachingEnabled(config.isHeuristicCachingEnabled())
            .setHeuristicCoefficient(config.getHeuristicCoefficient())
            .setHeuristicDefaultLifetime(config.getHeuristicDefaultLifetime())
            .setSharedCache(config.isSharedCache())
            .setAsynchronousWorkersMax(config.getAsynchronousWorkersMax())
            .setAsynchronousWorkersCore(config.getAsynchronousWorkersCore())
            .setAsynchronousWorkerIdleLifetimeSecs(config.getAsynchronousWorkerIdleLifetimeSecs())
            .setRevalidationQueueSize(config.getRevalidationQueueSize())
            .setNeverCacheHTTP10ResponsesWithQueryString(config.isNeverCacheHTTP10ResponsesWithQuery());
    }


    public static class Builder {

        private long maxObjectSize;
        private int maxCacheEntries;
        private int maxUpdateRetries;
        private boolean allow303Caching;
        private boolean weakETagOnPutDeleteAllowed;
        private boolean heuristicCachingEnabled;
        private float heuristicCoefficient;
        private long heuristicDefaultLifetime;
        private boolean isSharedCache;
        private int asynchronousWorkersMax;
        private int asynchronousWorkersCore;
        private int asynchronousWorkerIdleLifetimeSecs;
        private int revalidationQueueSize;
        private boolean neverCacheHTTP10ResponsesWithQuery;

        Builder() {
            this.maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
            this.maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
            this.maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
            this.allow303Caching = DEFAULT_303_CACHING_ENABLED;
            this.weakETagOnPutDeleteAllowed = DEFAULT_WEAK_ETAG_ON_PUTDELETE_ALLOWED;
            this.heuristicCachingEnabled = false;
            this.heuristicCoefficient = DEFAULT_HEURISTIC_COEFFICIENT;
            this.heuristicDefaultLifetime = DEFAULT_HEURISTIC_LIFETIME;
            this.isSharedCache = true;
            this.asynchronousWorkersMax = DEFAULT_ASYNCHRONOUS_WORKERS_MAX;
            this.asynchronousWorkersCore = DEFAULT_ASYNCHRONOUS_WORKERS_CORE;
            this.asynchronousWorkerIdleLifetimeSecs = DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS;
            this.revalidationQueueSize = DEFAULT_REVALIDATION_QUEUE_SIZE;
        }

        /**
         * Specifies the maximum response body size that will be eligible for caching.
         * @param maxObjectSize size in bytes
         */
        public Builder setMaxObjectSize(final long maxObjectSize) {
            this.maxObjectSize = maxObjectSize;
            return this;
        }

        /**
         * Sets the maximum number of cache entries the cache will retain.
         */
        public Builder setMaxCacheEntries(final int maxCacheEntries) {
            this.maxCacheEntries = maxCacheEntries;
            return this;
        }

        /**
         * Sets the number of times to retry a cache update on failure
         */
        public Builder setMaxUpdateRetries(final int maxUpdateRetries) {
            this.maxUpdateRetries = maxUpdateRetries;
            return this;
        }

        /**
         * Enables or disables 303 caching.
         * @param allow303Caching should be {@code true} to
         *   permit 303 caching, {@code false} to disable it.
         */
        public Builder setAllow303Caching(final boolean allow303Caching) {
            this.allow303Caching = allow303Caching;
            return this;
        }

        /**
         * Allows or disallows weak etags to be used with PUT/DELETE If-Match requests.
         * @param weakETagOnPutDeleteAllowed should be {@code true} to
         *   permit weak etags, {@code false} to reject them.
         */
        public Builder setWeakETagOnPutDeleteAllowed(final boolean weakETagOnPutDeleteAllowed) {
            this.weakETagOnPutDeleteAllowed = weakETagOnPutDeleteAllowed;
            return this;
        }

        /**
         * Enables or disables heuristic caching.
         * @param heuristicCachingEnabled should be {@code true} to
         *   permit heuristic caching, {@code false} to enable it.
         */
        public Builder setHeuristicCachingEnabled(final boolean heuristicCachingEnabled) {
            this.heuristicCachingEnabled = heuristicCachingEnabled;
            return this;
        }

        /**
         * Sets coefficient to be used in heuristic freshness caching. This is
         * interpreted as the fraction of the time between the {@code Last-Modified}
         * and {@code Date} headers of a cached response during which the cached
         * response will be considered heuristically fresh.
         * @param heuristicCoefficient should be between {@code 0.0} and
         *   {@code 1.0}.
         */
        public Builder setHeuristicCoefficient(final float heuristicCoefficient) {
            this.heuristicCoefficient = heuristicCoefficient;
            return this;
        }

        /**
         * Sets default lifetime in seconds to be used if heuristic freshness
         * calculation is not possible. Explicit cache control directives on
         * either the request or origin response will override this, as will
         * the heuristic {@code Last-Modified} freshness calculation if it is
         * available.
         * @param heuristicDefaultLifetime is the number of seconds to
         *   consider a cache-eligible response fresh in the absence of other
         *   information. Set this to {@code 0} to disable this style of
         *   heuristic caching.
         */
        public Builder setHeuristicDefaultLifetime(final long heuristicDefaultLifetime) {
            this.heuristicDefaultLifetime = heuristicDefaultLifetime;
            return this;
        }

        /**
         * Sets whether the cache should behave as a shared cache or not.
         * @param isSharedCache true to behave as a shared cache, false to
         * behave as a non-shared (private) cache. To have the cache
         * behave like a browser cache, you want to set this to {@code false}.
         */
        public Builder setSharedCache(final boolean isSharedCache) {
            this.isSharedCache = isSharedCache;
            return this;
        }

        /**
         * Sets the maximum number of threads to allow for background
         * revalidations due to the {@code stale-while-revalidate} directive.
         * @param asynchronousWorkersMax number of threads; a value of 0 disables background
         * revalidations.
         */
        public Builder setAsynchronousWorkersMax(final int asynchronousWorkersMax) {
            this.asynchronousWorkersMax = asynchronousWorkersMax;
            return this;
        }

        /**
         * Sets the minimum number of threads to keep alive for background
         * revalidations due to the {@code stale-while-revalidate} directive.
         * @param asynchronousWorkersCore should be greater than zero and less than or equal
         *   to <code>getAsynchronousWorkersMax()</code>
         */
        public Builder setAsynchronousWorkersCore(final int asynchronousWorkersCore) {
            this.asynchronousWorkersCore = asynchronousWorkersCore;
            return this;
        }

        /**
         * Sets the current maximum idle lifetime in seconds for a
         * background revalidation worker thread. If a worker thread is idle
         * for this long, and there are more than the core number of worker
         * threads alive, the worker will be reclaimed.
         * @param asynchronousWorkerIdleLifetimeSecs idle lifetime in seconds
         */
        public Builder setAsynchronousWorkerIdleLifetimeSecs(final int asynchronousWorkerIdleLifetimeSecs) {
            this.asynchronousWorkerIdleLifetimeSecs = asynchronousWorkerIdleLifetimeSecs;
            return this;
        }

        /**
         * Sets the current maximum queue size for background revalidations.
         */
        public Builder setRevalidationQueueSize(final int revalidationQueueSize) {
            this.revalidationQueueSize = revalidationQueueSize;
            return this;
        }

        /**
         * Sets whether the cache should never cache HTTP 1.0 responses with a query string or not.
         * @param neverCacheHTTP10ResponsesWithQuery true to never cache responses with a query
         * string, false to cache if explicit cache headers are found.  Set this to {@code true}
         * to better emulate IE, which also never caches responses, regardless of what caching
         * headers may be present.
         */
        public Builder setNeverCacheHTTP10ResponsesWithQueryString(
                final boolean neverCacheHTTP10ResponsesWithQuery) {
            this.neverCacheHTTP10ResponsesWithQuery = neverCacheHTTP10ResponsesWithQuery;
            return this;
        }

        public CacheConfig build() {
            return new CacheConfig(
                    maxObjectSize,
                    maxCacheEntries,
                    maxUpdateRetries,
                    allow303Caching,
                    weakETagOnPutDeleteAllowed,
                    heuristicCachingEnabled,
                    heuristicCoefficient,
                    heuristicDefaultLifetime,
                    isSharedCache,
                    asynchronousWorkersMax,
                    asynchronousWorkersCore,
                    asynchronousWorkerIdleLifetimeSecs,
                    revalidationQueueSize,
                    neverCacheHTTP10ResponsesWithQuery);
        }

    }

    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append("[maxObjectSize=").append(this.maxObjectSize)
                .append(", maxCacheEntries=").append(this.maxCacheEntries)
                .append(", maxUpdateRetries=").append(this.maxUpdateRetries)
                .append(", 303CachingEnabled=").append(this.allow303Caching)
                .append(", weakETagOnPutDeleteAllowed=").append(this.weakETagOnPutDeleteAllowed)
                .append(", heuristicCachingEnabled=").append(this.heuristicCachingEnabled)
                .append(", heuristicCoefficient=").append(this.heuristicCoefficient)
                .append(", heuristicDefaultLifetime=").append(this.heuristicDefaultLifetime)
                .append(", isSharedCache=").append(this.isSharedCache)
                .append(", asynchronousWorkersMax=").append(this.asynchronousWorkersMax)
                .append(", asynchronousWorkersCore=").append(this.asynchronousWorkersCore)
                .append(", asynchronousWorkerIdleLifetimeSecs=").append(this.asynchronousWorkerIdleLifetimeSecs)
                .append(", revalidationQueueSize=").append(this.revalidationQueueSize)
                .append(", neverCacheHTTP10ResponsesWithQuery=").append(this.neverCacheHTTP10ResponsesWithQuery)
                .append("]");
        return builder.toString();
    }

}