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 <=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 }