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