View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
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 net.sf.ehcache.config;
18  
19  import net.sf.ehcache.Cache;
20  import net.sf.ehcache.writer.CacheWriterManager;
21  import net.sf.ehcache.writer.writebehind.WriteBehindManager;
22  import net.sf.ehcache.writer.writethrough.WriteThroughManager;
23  
24  /***
25   * Class to hold the CacheWriterManager configuration
26   *
27   * @author Geert Bevin
28   * @version $Id: CacheWriterConfiguration.html 13146 2011-08-01 17:12:39Z oletizi $
29   */
30  public class CacheWriterConfiguration implements Cloneable {
31      /***
32       * Default writeMode
33       */
34      public static final WriteMode DEFAULT_WRITE_MODE = WriteMode.WRITE_THROUGH;
35      /***
36       * Default notifyListenersOnException behavior
37       */
38      public static final boolean DEFAULT_NOTIFY_LISTENERS_ON_EXCEPTION = false;
39      /***
40       * Default minimum write delay
41       */
42      public static final int DEFAULT_MIN_WRITE_DELAY = 1;
43      /***
44       * Default maximum write delay
45       */
46      public static final int DEFAULT_MAX_WRITE_DELAY = 1;
47      /***
48       * Default rate limit per second
49       */
50      public static final int DEFAULT_RATE_LIMIT_PER_SECOND = 0;
51      /***
52       * Default write coalescing behavior
53       */
54      public static final boolean DEFAULT_WRITE_COALESCING = false;
55      /***
56       * Default writeBatching behavior
57       */
58      public static final boolean DEFAULT_WRITE_BATCHING = false;
59      /***
60       * Default write batch size
61       */
62      public static final int DEFAULT_WRITE_BATCH_SIZE = 1;
63      /***
64       * Default retry attempts
65       */
66      public static final int DEFAULT_RETRY_ATTEMPTS = 0;
67      /***
68       * Default retry attempt delay
69       */
70      public static final int DEFAULT_RETRY_ATTEMPT_DELAY_SECONDS = 1;
71  
72      /***
73       * Default concurrency level for write behind
74       */
75      public static final int DEFAULT_WRITE_BEHIND_CONCURRENCY = 1;
76  
77      /***
78       * Default max queue size for write behind
79       */
80      public static final int DEFAULT_WRITE_BEHIND_MAX_QUEUE_SIZE = 0;
81  
82      /***
83       * Represents how elements are written to the {@link net.sf.ehcache.writer.CacheWriter}
84       */
85      public static enum WriteMode {
86          /***
87           * Write mode enum constant that can be used to configure a cache writer to use write through
88           */
89          WRITE_THROUGH {
90              /***
91               * {@inheritDoc}
92               */
93              @Override
94              public CacheWriterManager createWriterManager(Cache cache) {
95                  return new WriteThroughManager();
96              }
97          },
98  
99          /***
100          * Write mode enum constant that can be used to configure a cache writer to use write behind
101          */
102         WRITE_BEHIND {
103             /***
104              * {@inheritDoc}
105              */
106             @Override
107             public CacheWriterManager createWriterManager(Cache cache) {
108                 return new WriteBehindManager();
109             }
110         };
111 
112         /***
113          * Create a new {@code WriterManager} for a particular cache instance
114          *
115          * @param cache the cache instance for which the {@code WriterManager} should be created
116          * @return the newly created {@code WriterManager}
117          */
118         public abstract CacheWriterManager createWriterManager(Cache cache);
119     }
120 
121     private WriteMode writeMode = DEFAULT_WRITE_MODE;
122     private boolean notifyListenersOnException = DEFAULT_NOTIFY_LISTENERS_ON_EXCEPTION;
123     private int minWriteDelay = DEFAULT_MIN_WRITE_DELAY;
124     private int maxWriteDelay = DEFAULT_MAX_WRITE_DELAY;
125     private int rateLimitPerSecond = DEFAULT_RATE_LIMIT_PER_SECOND;
126     private boolean writeCoalescing = DEFAULT_WRITE_COALESCING;
127     private boolean writeBatching = DEFAULT_WRITE_BATCHING;
128     private int writeBatchSize = DEFAULT_WRITE_BATCH_SIZE;
129     private int retryAttempts = DEFAULT_RETRY_ATTEMPTS;
130     private int retryAttemptDelaySeconds = DEFAULT_RETRY_ATTEMPT_DELAY_SECONDS;
131     private int writeBehindConcurrency = DEFAULT_WRITE_BEHIND_CONCURRENCY;
132     private int writeBehindMaxQueueSize = DEFAULT_WRITE_BEHIND_MAX_QUEUE_SIZE;
133     private CacheWriterFactoryConfiguration cacheWriterFactoryConfiguration;
134 
135     /***
136      * Clones this object, following the usual contract.
137      *
138      * @return a copy, which independent other than configurations than cannot change.
139      */
140     @Override
141     public CacheWriterConfiguration clone() {
142         CacheWriterConfiguration config;
143         try {
144             config = (CacheWriterConfiguration) super.clone();
145         } catch (CloneNotSupportedException e) {
146             throw new RuntimeException(e);
147         }
148 
149         if (cacheWriterFactoryConfiguration != null) {
150             config.cacheWriterFactoryConfiguration = cacheWriterFactoryConfiguration.clone();
151         }
152 
153         return config;
154     }
155 
156     /***
157      * Converts the {@code valueMode} string argument to uppercase and looks up enum constant in WriteMode.
158      */
159     public void setWriteMode(String writeMode) {
160         if (writeMode == null) {
161             throw new IllegalArgumentException("WriteMode can't be null");
162         }
163         this.writeMode = WriteMode.valueOf(WriteMode.class, writeMode.replace('-', '_').toUpperCase());
164     }
165 
166     /***
167      * @return this configuration instance
168      * @see #setWriteMode(String)
169      */
170     public CacheWriterConfiguration writeMode(String writeMode) {
171         setWriteMode(writeMode);
172         return this;
173     }
174 
175     /***
176      * @return this configuration instance
177      * @see #setWriteMode(String)
178      */
179     public CacheWriterConfiguration writeMode(WriteMode writeMode) {
180         if (null == writeMode) {
181             throw new IllegalArgumentException("WriteMode can't be null");
182         }
183         this.writeMode = writeMode;
184         return this;
185     }
186 
187     /***
188      * Get the write mode in terms of the mode enum
189      */
190     public WriteMode getWriteMode() {
191         return this.writeMode;
192     }
193 
194     /***
195      * Sets whether to notify listeners when an exception occurs on a writer operation.
196      * <p/>
197      * This is only applicable to write through mode.
198      * <p/>
199      * Defaults to {@value #DEFAULT_NOTIFY_LISTENERS_ON_EXCEPTION}.
200      *
201      * @param notifyListenersOnException {@code true} if listeners should be notified when an exception occurs on a writer operation; {@code false} otherwise
202      */
203     public void setNotifyListenersOnException(boolean notifyListenersOnException) {
204         this.notifyListenersOnException = notifyListenersOnException;
205     }
206 
207     /***
208      * @return this configuration instance
209      * @see #setNotifyListenersOnException(boolean)
210      */
211     public CacheWriterConfiguration notifyListenersOnException(boolean notifyListenersOnException) {
212         setNotifyListenersOnException(notifyListenersOnException);
213         return this;
214     }
215 
216     /***
217      * Check whether listeners should be notified when an exception occurs on a writer operation
218      */
219     public boolean getNotifyListenersOnException() {
220         return this.notifyListenersOnException;
221     }
222 
223     /***
224      * Set the minimum number of seconds to wait before writing behind. If set to a value greater than 0, it permits
225      * operations to build up in the queue. This is different from the maximum write delay in that by waiting a minimum
226      * amount of time, work is always being built up. If the minimum write delay is set to zero and the {@code CacheWriter}
227      * performs its work very quickly, the overhead of processing the write behind queue items becomes very noticeable
228      * in a cluster since all the operations might be done for individual items instead of for a collection of them.
229      * <p/>
230      * This is only applicable to write behind mode.
231      * <p/>
232      * Defaults to {@value #DEFAULT_MIN_WRITE_DELAY}).
233      *
234      * @param minWriteDelay the minimum number of seconds to wait before writing behind
235      */
236     public void setMinWriteDelay(int minWriteDelay) {
237         if (minWriteDelay < 0) {
238             this.minWriteDelay = 0;
239         } else {
240             this.minWriteDelay = minWriteDelay;
241         }
242     }
243 
244     /***
245      * @return this configuration instance
246      * @see #setMinWriteDelay(int)
247      */
248     public CacheWriterConfiguration minWriteDelay(int minWriteDelay) {
249         setMinWriteDelay(minWriteDelay);
250         return this;
251     }
252 
253     /***
254      * Get the minimum number of seconds to wait before writing behind
255      */
256     public int getMinWriteDelay() {
257         return this.minWriteDelay;
258     }
259 
260     /***
261      * Set the maximum number of seconds to wait before writing behind. If set to a value greater than 0, it permits
262      * operations to build up in the queue to enable effective coalescing and batching optimisations.
263      * <p/>
264      * This is only applicable to write behind mode.
265      * <p/>
266      * Defaults to {@value #DEFAULT_MAX_WRITE_DELAY}).
267      *
268      * @param maxWriteDelay the maximum number of seconds to wait before writing behind
269      */
270     public void setMaxWriteDelay(int maxWriteDelay) {
271         if (maxWriteDelay < 0) {
272             this.maxWriteDelay = 0;
273         } else {
274             this.maxWriteDelay = maxWriteDelay;
275         }
276     }
277 
278     /***
279      * @return this configuration instance
280      * @see #setMaxWriteDelay(int)
281      */
282     public CacheWriterConfiguration maxWriteDelay(int maxWriteDelay) {
283         setMaxWriteDelay(maxWriteDelay);
284         return this;
285     }
286 
287     /***
288      * Get the maximum number of seconds to wait before writing behind
289      */
290     public int getMaxWriteDelay() {
291         return this.maxWriteDelay;
292     }
293 
294     /***
295      * Sets the maximum number of write operations to allow per second when {@link #writeBatching} is enabled.
296      * <p/>
297      * This is only applicable to write behind mode.
298      * <p/>
299      * Defaults to {@value #DEFAULT_RATE_LIMIT_PER_SECOND}.
300      *
301      * @param rateLimitPerSecond the number of write operations to allow; use a number {@code &lt;=0} to disable rate limiting.
302      */
303     public void setRateLimitPerSecond(int rateLimitPerSecond) {
304         if (rateLimitPerSecond < 0) {
305             this.rateLimitPerSecond = 0;
306         } else {
307             this.rateLimitPerSecond = rateLimitPerSecond;
308         }
309     }
310 
311     /***
312      * @return this configuration instance
313      * @see #setRateLimitPerSecond(int rateLimitPerSecond)
314      */
315     public CacheWriterConfiguration rateLimitPerSecond(int rateLimitPerSecond) {
316         setRateLimitPerSecond(rateLimitPerSecond);
317         return this;
318     }
319 
320     /***
321      * Get the maximum number of write operations to allow per second.
322      */
323     public int getRateLimitPerSecond() {
324         return rateLimitPerSecond;
325     }
326 
327     /***
328      * Sets whether to use write coalescing. If set to {@code true} and multiple operations on the same key are present
329      * in the write-behind queue, only the latest write is done, as the others are redundant. This can dramatically
330      * reduce load on the underlying resource.
331      * <p/>
332      * This is only applicable to write behind mode.
333      * <p/>
334      * Defaults to {@value #DEFAULT_WRITE_COALESCING}.
335      *
336      * @param writeCoalescing {@code true} to enable write coalescing; or {@code false} to disable it
337      */
338     public void setWriteCoalescing(boolean writeCoalescing) {
339         this.writeCoalescing = writeCoalescing;
340     }
341 
342     /***
343      * @return this configuration instance
344      * @see #setWriteCoalescing(boolean)
345      */
346     public CacheWriterConfiguration writeCoalescing(boolean writeCoalescing) {
347         setWriteCoalescing(writeCoalescing);
348         return this;
349     }
350 
351     /***
352      * @return this configuration instance
353      * @see #setWriteCoalescing(boolean)
354      */
355     public boolean getWriteCoalescing() {
356         return writeCoalescing;
357     }
358 
359     /***
360      * Sets whether to batch write operations. If set to {@code true}, {@link net.sf.ehcache.writer.CacheWriter#writeAll} and {@code CacheWriter#deleteAll}
361      * will be called rather than {@link net.sf.ehcache.writer.CacheWriter#write} and {@link net.sf.ehcache.writer.CacheWriter#delete} being called for each key. Resources such
362      * as databases can perform more efficiently if updates are batched, thus reducing load.
363      * <p/>
364      * This is only applicable to write behind mode.
365      * <p/>
366      * Defaults to {@value #DEFAULT_WRITE_BATCHING}.
367      *
368      * @param writeBatching {@code true} if write operations should be batched; {@code false} otherwise
369      */
370     public void setWriteBatching(boolean writeBatching) {
371         this.writeBatching = writeBatching;
372     }
373 
374     /***
375      * @return this configuration instance
376      * @see #setWriteBatching(boolean)
377      */
378     public CacheWriterConfiguration writeBatching(boolean writeBatching) {
379         setWriteBatching(writeBatching);
380         return this;
381     }
382 
383     /***
384      * Check whether write operations should be batched
385      */
386     public boolean getWriteBatching() {
387         return this.writeBatching;
388     }
389 
390     /***
391      * Sets the number of operations to include in each batch when {@link #writeBatching} is enabled. If there are less
392      * entries in the write-behind queue than the batch size, the queue length size is used.
393      * <p/>
394      * This is only applicable to write behind mode.
395      * <p/>
396      * Defaults to {@value #DEFAULT_WRITE_BATCH_SIZE}.
397      *
398      * @param writeBatchSize the number of operations to include in each batch; numbers smaller than {@code 1} will cause
399      *                       the default batch size to be used
400      */
401     public void setWriteBatchSize(int writeBatchSize) {
402         if (writeBatchSize < 1) {
403             this.writeBatchSize = DEFAULT_WRITE_BATCH_SIZE;
404         } else {
405             this.writeBatchSize = writeBatchSize;
406         }
407     }
408 
409     /***
410      * @return this configuration instance
411      * @see #setWriteBatchSize(int)
412      */
413     public CacheWriterConfiguration writeBatchSize(int writeBatchSize) {
414         setWriteBatchSize(writeBatchSize);
415         return this;
416     }
417 
418     /***
419      * Retrieves the size of the batch operation.
420      */
421     public int getWriteBatchSize() {
422         return writeBatchSize;
423     }
424 
425     /***
426      * Sets the number of times the operation is retried in the {@code CacheWriter}, this happens after the
427      * original operation.
428      * <p/>
429      * This is only applicable to write behind mode.
430      * <p/>
431      * Defaults to {@value #DEFAULT_RETRY_ATTEMPTS}.
432      *
433      * @param retryAttempts the number of retries for a particular element
434      */
435     public void setRetryAttempts(int retryAttempts) {
436         if (retryAttempts < 0) {
437             this.retryAttempts = 0;
438         } else {
439             this.retryAttempts = retryAttempts;
440         }
441     }
442 
443     /***
444      * @return this configuration instance
445      * @see #setRetryAttempts(int)
446      */
447     public CacheWriterConfiguration retryAttempts(int retryAttempts) {
448         setRetryAttempts(retryAttempts);
449         return this;
450     }
451 
452     /***
453      * Retrieves the number of times the write of element is retried.
454      */
455     public int getRetryAttempts() {
456         return retryAttempts;
457     }
458 
459     /***
460      * Sets the number of seconds to wait before retrying an failed operation.
461      * <p/>
462      * This is only applicable to write behind mode.
463      * <p/>
464      * Defaults to {@value #DEFAULT_RETRY_ATTEMPT_DELAY_SECONDS}.
465      *
466      * @param retryAttemptDelaySeconds the number of seconds to wait before retrying an operation
467      */
468     public void setRetryAttemptDelaySeconds(int retryAttemptDelaySeconds) {
469         if (retryAttemptDelaySeconds < 0) {
470             this.retryAttemptDelaySeconds = 0;
471         } else {
472             this.retryAttemptDelaySeconds = retryAttemptDelaySeconds;
473         }
474     }
475 
476     /***
477      * @return this configuration instance
478      * @see #setRetryAttemptDelaySeconds(int)
479      */
480     public CacheWriterConfiguration retryAttemptDelaySeconds(int retryAttemptDelaySeconds) {
481         setRetryAttemptDelaySeconds(retryAttemptDelaySeconds);
482         return this;
483     }
484 
485     /***
486      * Retrieves the number of seconds to wait before retrying an failed operation.
487      */
488     public int getRetryAttemptDelaySeconds() {
489         return retryAttemptDelaySeconds;
490     }
491 
492     /***
493      * Configuration for the CacheWriterFactoryConfiguration.
494      */
495     public static final class CacheWriterFactoryConfiguration extends FactoryConfiguration<CacheWriterFactoryConfiguration> {
496         
497         /***
498          * Overrided hashCode()
499          */
500         @Override
501         public int hashCode() {
502             return super.hashCode();
503         }
504 
505         /***
506          * Overrided equals
507          */
508         @Override
509         public boolean equals(Object obj) {
510             return super.equals(obj);
511         }
512     }
513 
514     /***
515      * Allows BeanHandler to add the CacheWriterFactory to the configuration.
516      */
517     public final void addCacheWriterFactory(CacheWriterFactoryConfiguration cacheWriterFactoryConfiguration) {
518         this.cacheWriterFactoryConfiguration = cacheWriterFactoryConfiguration;
519     }
520 
521     /***
522      * @return this configuration instance
523      * @see #addCacheWriterFactory(CacheWriterFactoryConfiguration)
524      */
525     public CacheWriterConfiguration cacheWriterFactory(CacheWriterFactoryConfiguration cacheWriterFactory) {
526         addCacheWriterFactory(cacheWriterFactory);
527         return this;
528     }
529 
530     /***
531      * Accessor
532      *
533      * @return the configuration
534      */
535     public CacheWriterFactoryConfiguration getCacheWriterFactoryConfiguration() {
536         return cacheWriterFactoryConfiguration;
537     }
538 
539     /***
540      * Configures the amount of thread/bucket pairs WriteBehind should use
541      * @param concurrency Amount of thread/bucket pairs, has to be at least 1
542      */
543     public void setWriteBehindConcurrency(int concurrency) {
544         if (concurrency < 1) {
545             this.writeBehindConcurrency = 1;
546         } else {
547             this.writeBehindConcurrency = concurrency;
548         }
549     }
550 
551     /***
552      *
553      * @param concurrency Amount of thread/bucket pairs, has to be at least 1
554      * @return this configuration instance
555      * @see #setWriteBehindConcurrency(int)
556      */
557     public CacheWriterConfiguration writeBehindConcurrency(int concurrency) {
558         this.setWriteBehindConcurrency(concurrency);
559         return this;
560     }
561 
562     /***
563      * Accessor
564      * @return the amount of bucket/thread pairs configured for this cache's write behind
565      */
566     public int getWriteBehindConcurrency() {
567         return writeBehindConcurrency;
568     }
569 
570     /***
571      * Configures the maximum amount of operations to be on the waiting queue, before it blocks
572      * @param writeBehindMaxQueueSize maximum amount of operations allowed on the waiting queue
573      */
574     public void setWriteBehindMaxQueueSize(final int writeBehindMaxQueueSize) {
575         if (writeBehindMaxQueueSize < 0) {
576             this.writeBehindMaxQueueSize = DEFAULT_WRITE_BEHIND_MAX_QUEUE_SIZE;
577         } else {
578             this.writeBehindMaxQueueSize = writeBehindMaxQueueSize;
579         }
580     }
581 
582     /***
583      * Accessor
584      * @return the maximum amount of operations allowed on the write behind queue
585      */
586     public int getWriteBehindMaxQueueSize() {
587         return writeBehindMaxQueueSize;
588     }
589 
590     /***
591      * @param writeBehindMaxQueueSize maximum amount of operations allowed on the waiting queue
592      * @return this configuration instance
593      * @see #setWriteBehindMaxQueueSize(int)
594      */
595     public CacheWriterConfiguration writeBehindMaxQueueSize(int writeBehindMaxQueueSize) {
596         this.setWriteBehindMaxQueueSize(writeBehindMaxQueueSize);
597         return this;
598     }
599 
600     /***
601      * Overrided hashCode()
602      */
603     @Override
604     public int hashCode() {
605         final int prime = 31;
606         final int primeTwo = 1231;
607         final int primeThree = 1237;
608         int result = 1;
609         result = prime * result + ((cacheWriterFactoryConfiguration == null) ? 0 : cacheWriterFactoryConfiguration.hashCode());
610         result = prime * result + maxWriteDelay;
611         result = prime * result + minWriteDelay;
612         result = prime * result + (notifyListenersOnException ? primeTwo : primeThree);
613         result = prime * result + rateLimitPerSecond;
614         result = prime * result + retryAttemptDelaySeconds;
615         result = prime * result + retryAttempts;
616         result = prime * result + writeBatchSize;
617         result = prime * result + (writeBatching ? primeTwo : primeThree);
618         result = prime * result + (writeCoalescing ? primeTwo : primeThree);
619         result = prime * result + ((writeMode == null) ? 0 : writeMode.hashCode());
620         result = prime * result + writeBehindConcurrency;
621         return result;
622     }
623 
624     /***
625      * Overrided equals()
626      */
627     @Override
628     public boolean equals(Object obj) {
629         if (this == obj) {
630             return true;
631         }
632         if (obj == null) {
633             return false;
634         }
635         if (getClass() != obj.getClass()) {
636             return false;
637         }
638         CacheWriterConfiguration other = (CacheWriterConfiguration) obj;
639         if (cacheWriterFactoryConfiguration == null) {
640             if (other.cacheWriterFactoryConfiguration != null) {
641                 return false;
642             }
643         } else if (!cacheWriterFactoryConfiguration.equals(other.cacheWriterFactoryConfiguration)) {
644             return false;
645         }
646         if (maxWriteDelay != other.maxWriteDelay) {
647             return false;
648         }
649         if (minWriteDelay != other.minWriteDelay) {
650             return false;
651         }
652         if (notifyListenersOnException != other.notifyListenersOnException) {
653             return false;
654         }
655         if (rateLimitPerSecond != other.rateLimitPerSecond) {
656             return false;
657         }
658         if (retryAttemptDelaySeconds != other.retryAttemptDelaySeconds) {
659             return false;
660         }
661         if (retryAttempts != other.retryAttempts) {
662             return false;
663         }
664         if (writeBatchSize != other.writeBatchSize) {
665             return false;
666         }
667         if (writeBatching != other.writeBatching) {
668             return false;
669         }
670         if (writeCoalescing != other.writeCoalescing) {
671             return false;
672         }
673         if (writeBehindConcurrency != other.writeBehindConcurrency) {
674             return false;
675         }
676         if (writeMode == null) {
677             if (other.writeMode != null) {
678                 return false;
679             }
680         } else if (!writeMode.equals(other.writeMode)) {
681             return false;
682         }
683         return true;
684     }
685     
686     
687 }