1 package org.apache.commons.jcs.access;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.commons.jcs.JCS;
29 import org.apache.commons.jcs.access.behavior.ICacheAccess;
30 import org.apache.commons.jcs.access.exception.CacheException;
31 import org.apache.commons.jcs.access.exception.ConfigurationException;
32 import org.apache.commons.jcs.engine.behavior.ICacheElement;
33 import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
34 import org.apache.commons.jcs.engine.behavior.IElementAttributes;
35 import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
36 import org.apache.commons.jcs.utils.props.AbstractPropertyContainer;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class PartitionedCacheAccess<K, V>
59 extends AbstractPropertyContainer
60 implements ICacheAccess<K, V>
61 {
62
63 private static final Log log = LogFactory.getLog( PartitionedCacheAccess.class );
64
65
66 private int numberOfPartitions = 1;
67
68
69
70
71
72 private String partitionRegionNamePrefix;
73
74
75 private ICacheAccess<K, V>[] partitions;
76
77
78 private boolean initialized = false;
79
80
81 public PartitionedCacheAccess()
82 {
83 setPropertiesHeading( "PartitionedCacheAccess" );
84 setPropertiesGroup( "cache" );
85 }
86
87
88
89
90
91
92
93
94 @Override
95 public void put( K key, V object )
96 throws CacheException
97 {
98 if ( key == null || object == null )
99 {
100 log.warn( "Bad input key [" + key + "]. Cannot put null into the cache." );
101 return;
102 }
103
104 if (!ensureInit())
105 {
106 return;
107 }
108
109 int partition = getPartitionNumberForKey( key );
110 try
111 {
112 partitions[partition].put( key, object );
113 }
114 catch ( CacheException e )
115 {
116 log.error( "Problem putting value for key [" + key + "] in cache [" + partitions[partition] + "]" );
117 throw e;
118 }
119 }
120
121
122
123
124
125
126
127
128 @Override
129 public void putSafe( K key, V object )
130 throws CacheException
131 {
132 if ( key == null || object == null )
133 {
134 log.warn( "Bad input key [" + key + "]. Cannot putSafe null into the cache." );
135 }
136
137 if (!ensureInit())
138 {
139 return;
140 }
141
142 int partition = getPartitionNumberForKey( key );
143 partitions[partition].putSafe( key, object );
144 }
145
146
147
148
149
150
151
152
153
154 @Override
155 public void put( K key, V object, IElementAttributes attr )
156 throws CacheException
157 {
158 if ( key == null || object == null )
159 {
160 log.warn( "Bad input key [" + key + "]. Cannot put null into the cache." );
161 return;
162 }
163
164 if (!ensureInit())
165 {
166 return;
167 }
168
169 int partition = getPartitionNumberForKey( key );
170
171 try
172 {
173 partitions[partition].put( key, object, attr );
174 }
175 catch ( CacheException e )
176 {
177 log.error( "Problem putting value for key [" + key + "] in cache [" + partitions[partition] + "]" );
178 throw e;
179 }
180 }
181
182
183
184
185
186
187
188 @Override
189 public V get( K key )
190 {
191 if ( key == null )
192 {
193 log.warn( "Input key is null." );
194 return null;
195 }
196
197 if (!ensureInit())
198 {
199 return null;
200 }
201
202 int partition = getPartitionNumberForKey( key );
203
204 return partitions[partition].get( key );
205 }
206
207
208
209
210
211
212
213 @Override
214 public ICacheElement<K, V> getCacheElement( K key )
215 {
216 if ( key == null )
217 {
218 log.warn( "Input key is null." );
219 return null;
220 }
221
222 if (!ensureInit())
223 {
224 return null;
225 }
226
227 int partition = getPartitionNumberForKey( key );
228
229 return partitions[partition].getCacheElement( key );
230 }
231
232
233
234
235
236
237
238 @Override
239 public Map<K, ICacheElement<K, V>> getCacheElements( Set<K> names )
240 {
241 if ( names == null )
242 {
243 log.warn( "Bad input names cannot be null." );
244 return Collections.emptyMap();
245 }
246
247 if (!ensureInit())
248 {
249 return Collections.emptyMap();
250 }
251
252 @SuppressWarnings("unchecked")
253 Set<K>[] dividedNames = new Set[this.getNumberOfPartitions()];
254
255 for (K key : names)
256 {
257 int partition = getPartitionNumberForKey( key );
258 if ( dividedNames[partition] == null )
259 {
260 dividedNames[partition] = new HashSet<K>();
261 }
262 dividedNames[partition].add( key );
263 }
264
265 Map<K, ICacheElement<K, V>> result = new HashMap<K, ICacheElement<K, V>>();
266 for ( int i = 0; i < partitions.length; i++ )
267 {
268 if ( dividedNames[i] != null && !dividedNames[i].isEmpty() )
269 {
270 result.putAll( partitions[i].getCacheElements( dividedNames[i] ) );
271 }
272 }
273 return result;
274 }
275
276
277
278
279
280
281
282
283
284
285 @Override
286 public Map<K, V> getMatching( String pattern )
287 {
288 if ( pattern == null )
289 {
290 log.warn( "Input pattern is null." );
291 return null;
292 }
293
294 if (!ensureInit())
295 {
296 return null;
297 }
298
299 Map<K, V> result = new HashMap<K, V>();
300 for (ICacheAccess<K, V> partition : partitions)
301 {
302 result.putAll( partition.getMatching( pattern ) );
303 }
304
305 return result;
306 }
307
308
309
310
311
312
313
314 @Override
315 public Map<K, ICacheElement<K, V>> getMatchingCacheElements( String pattern )
316 {
317 if ( pattern == null )
318 {
319 log.warn( "Input pattern is null." );
320 return null;
321 }
322
323 if (!ensureInit())
324 {
325 return null;
326 }
327
328 Map<K, ICacheElement<K, V>> result = new HashMap<K, ICacheElement<K, V>>();
329 for (ICacheAccess<K, V> partition : partitions)
330 {
331 result.putAll( partition.getMatchingCacheElements( pattern ) );
332 }
333 return result;
334 }
335
336
337
338
339
340
341
342 @Override
343 public void remove( K key )
344 throws CacheException
345 {
346 if ( key == null )
347 {
348 log.warn( "Input key is null. Cannot remove null from the cache." );
349 return;
350 }
351
352 if (!ensureInit())
353 {
354 return;
355 }
356
357 int partition = getPartitionNumberForKey( key );
358 try
359 {
360 partitions[partition].remove( key );
361 }
362 catch ( CacheException e )
363 {
364 log.error( "Problem removing value for key [" + key + "] in cache [" + partitions[partition] + "]" );
365 throw e;
366 }
367 }
368
369
370
371
372
373
374
375
376 @Override
377 public int freeMemoryElements( int numberToFree )
378 throws CacheException
379 {
380 if (!ensureInit())
381 {
382 return 0;
383 }
384
385 int count = 0;
386 for (ICacheAccess<K, V> partition : partitions)
387 {
388 count += partition.freeMemoryElements( numberToFree );
389 }
390 return count;
391 }
392
393
394
395
396 @Override
397 public ICompositeCacheAttributes getCacheAttributes()
398 {
399 if (!ensureInit())
400 {
401 return null;
402 }
403
404 if ( partitions.length == 0 )
405 {
406 return null;
407 }
408
409 return partitions[0].getCacheAttributes();
410 }
411
412
413
414
415
416 @Override
417 public IElementAttributes getDefaultElementAttributes()
418 throws CacheException
419 {
420 if (!ensureInit())
421 {
422 return null;
423 }
424
425 if ( partitions.length == 0 )
426 {
427 return null;
428 }
429
430 return partitions[0].getDefaultElementAttributes();
431 }
432
433
434
435
436
437
438
439
440 @Override
441 public IElementAttributes getElementAttributes( K key )
442 throws CacheException
443 {
444 if ( key == null )
445 {
446 log.warn( "Input key is null. Cannot getElementAttributes for null from the cache." );
447 return null;
448 }
449
450 if (!ensureInit())
451 {
452 return null;
453 }
454
455 int partition = getPartitionNumberForKey( key );
456
457 return partitions[partition].getElementAttributes( key );
458 }
459
460
461
462
463
464
465
466
467
468 @Override
469 public void resetElementAttributes( K key, IElementAttributes attributes )
470 throws CacheException
471 {
472 if ( key == null )
473 {
474 log.warn( "Input key is null. Cannot resetElementAttributes for null." );
475 return;
476 }
477
478 if (!ensureInit())
479 {
480 return;
481 }
482
483 int partition = getPartitionNumberForKey( key );
484
485 partitions[partition].resetElementAttributes( key, attributes );
486 }
487
488
489
490
491
492
493 @Override
494 public void setCacheAttributes( ICompositeCacheAttributes cattr )
495 {
496 if (!ensureInit())
497 {
498 return;
499 }
500
501 for (ICacheAccess<K, V> partition : partitions)
502 {
503 partition.setCacheAttributes( cattr );
504 }
505 }
506
507
508
509
510
511
512 @Override
513 public void clear()
514 throws CacheException
515 {
516 if (!ensureInit())
517 {
518 return;
519 }
520
521 for (ICacheAccess<K, V> partition : partitions)
522 {
523 partition.clear();
524 }
525 }
526
527
528
529
530
531
532
533
534
535
536
537 @Override
538 public void setDefaultElementAttributes( IElementAttributes attr )
539 throws CacheException
540 {
541 if (!ensureInit())
542 {
543 return;
544 }
545
546 for (ICacheAccess<K, V> partition : partitions)
547 {
548 partition.setDefaultElementAttributes(attr);
549 }
550 }
551
552
553
554
555
556
557
558
559 @Override
560 public ICacheStats getStatistics()
561 {
562 if (!ensureInit())
563 {
564 return null;
565 }
566
567 if ( partitions.length == 0 )
568 {
569 return null;
570 }
571
572 return partitions[0].getStatistics();
573 }
574
575
576
577
578 @Override
579 public String getStats()
580 {
581 if (!ensureInit())
582 {
583 return "";
584 }
585
586 StringBuilder stats = new StringBuilder();
587 for (ICacheAccess<K, V> partition : partitions)
588 {
589 stats.append(partition.getStats());
590 stats.append("\n");
591 }
592
593 return stats.toString();
594 }
595
596
597
598
599
600
601
602 @Override
603 public synchronized void dispose()
604 {
605 if (!ensureInit())
606 {
607 return;
608 }
609
610 for (ICacheAccess<K, V> partition : partitions)
611 {
612 partition.dispose();
613 }
614
615 initialized = false;
616 }
617
618
619
620
621
622
623
624
625
626
627 protected int getPartitionNumberForKey( K key )
628 {
629 if ( key == null )
630 {
631 return 0;
632 }
633
634 long keyNum = getNumericValueForKey( key );
635
636 int partition = (int) ( keyNum % getNumberOfPartitions() );
637
638 if ( log.isDebugEnabled() )
639 {
640 log.debug( "Using partition [" + partition + "] for key [" + key + "]" );
641 }
642
643 return partition;
644 }
645
646
647
648
649
650
651
652 public long getNumericValueForKey( K key )
653 {
654 String keyString = key.toString();
655 long keyNum = -1;
656 try
657 {
658 keyNum = Long.parseLong( keyString );
659 }
660 catch ( NumberFormatException e )
661 {
662
663 keyNum = key.hashCode();
664 log.warn( "Couldn't convert [" + key + "] into a number. Will use hashcode [" + keyNum + "]" );
665 }
666 return keyNum;
667 }
668
669
670
671
672
673
674 protected synchronized boolean ensureInit()
675 {
676 if ( !initialized )
677 {
678 try
679 {
680 initialize();
681 }
682 catch ( ConfigurationException e )
683 {
684 log.error( "Couldn't configure partioned access.", e );
685 return false;
686 }
687 }
688
689 return true;
690 }
691
692
693
694
695
696
697 protected synchronized void initialize()
698 throws ConfigurationException
699 {
700 ensureProperties();
701
702 @SuppressWarnings("unchecked")
703 ICacheAccess<K, V>[] tempPartitions = new ICacheAccess[this.getNumberOfPartitions()];
704 for ( int i = 0; i < this.getNumberOfPartitions(); i++ )
705 {
706 String regionName = this.getPartitionRegionNamePrefix() + "_" + i;
707 try
708 {
709 tempPartitions[i] = JCS.getInstance( regionName );
710 }
711 catch ( CacheException e )
712 {
713 log.error( "Problem getting cache for region [" + regionName + "]" );
714 }
715 }
716
717 partitions = tempPartitions;
718 initialized = true;
719 }
720
721
722
723
724
725
726
727
728
729
730
731
732 @Override
733 protected void handleProperties()
734 throws ConfigurationException
735 {
736
737 String numberOfPartitionsPropertyName = this.getPropertiesHeading() + ".numberOfPartitions";
738 String numberOfPartitionsPropertyValue = getPropertyForName( numberOfPartitionsPropertyName, true );
739 try
740 {
741 this.setNumberOfPartitions( Integer.parseInt( numberOfPartitionsPropertyValue ) );
742 }
743 catch ( NumberFormatException e )
744 {
745 String message = "Could not convert [" + numberOfPartitionsPropertyValue + "] into a number for ["
746 + numberOfPartitionsPropertyName + "]";
747 log.error( message );
748 throw new ConfigurationException( message );
749 }
750
751
752 String prefixPropertyName = this.getPropertiesHeading() + ".partitionRegionNamePrefix";
753 String prefix = getPropertyForName( prefixPropertyName, true );
754 this.setPartitionRegionNamePrefix( prefix );
755 }
756
757
758
759
760
761
762
763
764
765 protected String getPropertyForName( String propertyName, boolean required )
766 throws ConfigurationException
767 {
768 String propertyValue = null;
769 propertyValue = System.getProperty( propertyName );
770 if ( propertyValue != null )
771 {
772 if ( log.isInfoEnabled() )
773 {
774 log.info( "Found system property override: Name [" + propertyName + "] Value [" + propertyValue + "]" );
775 }
776 }
777 else
778 {
779 propertyValue = this.getProperties().getProperty( propertyName );
780 if ( required && propertyValue == null )
781 {
782 String message = "Could not find required property [" + propertyName + "] in propertiesGroup ["
783 + this.getPropertiesGroup() + "]";
784 log.error( message );
785 throw new ConfigurationException( message );
786 }
787 else
788 {
789 if ( log.isInfoEnabled() )
790 {
791 log.info( "Name [" + propertyName + "] Value [" + propertyValue + "]" );
792 }
793 }
794 }
795 return propertyValue;
796 }
797
798
799
800
801 protected void setNumberOfPartitions( int numberOfPartitions )
802 {
803 this.numberOfPartitions = numberOfPartitions;
804 }
805
806
807
808
809 protected int getNumberOfPartitions()
810 {
811 return numberOfPartitions;
812 }
813
814
815
816
817 protected void setPartitionRegionNamePrefix( String partitionRegionNamePrefix )
818 {
819 this.partitionRegionNamePrefix = partitionRegionNamePrefix;
820 }
821
822
823
824
825 protected String getPartitionRegionNamePrefix()
826 {
827 return partitionRegionNamePrefix;
828 }
829
830
831
832
833 protected void setPartitions( ICacheAccess<K, V>[] partitions )
834 {
835 this.partitions = partitions;
836 }
837
838
839
840
841 protected ICacheAccess<K, V>[] getPartitions()
842 {
843 return partitions;
844 }
845 }