[commons-jcs] 01/27: Imported Upstream version 2.0~beta1

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Mon Nov 16 18:51:10 UTC 2015


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to branch master
in repository commons-jcs.

commit f3e06224f8e9f2f0ac968a56ea8ce84acbebf9af
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat May 2 13:14:53 2015 +0200

    Imported Upstream version 2.0~beta1
---
 BUILDING.txt                                       |    10 +
 LICENSE.txt                                        |   202 +
 NOTICE.txt                                         |     5 +
 checkstyle.xml                                     |   175 +
 commons-jcs-core/LICENSE.txt                       |   203 +
 commons-jcs-core/NOTICE                            |     6 +
 commons-jcs-core/pom.xml                           |   187 +
 .../src/main/java/org/apache/commons/jcs/JCS.java  |   212 +
 .../commons/jcs/access/AbstractCacheAccess.java    |   203 +
 .../org/apache/commons/jcs/access/CacheAccess.java |   309 +
 .../commons/jcs/access/GroupCacheAccess.java       |   211 +
 .../commons/jcs/access/PartitionedCacheAccess.java |   846 +
 .../commons/jcs/access/behavior/ICacheAccess.java  |   167 +
 .../access/behavior/ICacheAccessManagement.java    |   111 +
 .../jcs/access/behavior/IGroupCacheAccess.java     |    89 +
 .../jcs/access/exception/CacheException.java       |    66 +
 .../access/exception/ConfigurationException.java   |    44 +
 .../access/exception/InvalidArgumentException.java |    47 +
 .../access/exception/InvalidGroupException.java    |    47 +
 .../access/exception/InvalidHandleException.java   |    48 +
 .../access/exception/ObjectExistsException.java    |    53 +
 .../access/exception/ObjectNotFoundException.java  |    51 +
 .../org/apache/commons/jcs/access/package.html     |    27 +
 .../apache/commons/jcs/admin/CacheElementInfo.java |   124 +
 .../apache/commons/jcs/admin/CacheRegionInfo.java  |   180 +
 .../jcs/admin/CountingOnlyOutputStream.java        |    84 +
 .../java/org/apache/commons/jcs/admin/JCSAdmin.jsp |   310 +
 .../org/apache/commons/jcs/admin/JCSAdminBean.java |   436 +
 .../org/apache/commons/jcs/admin/JCSJMXBean.java   |    90 +
 .../commons/jcs/admin/servlet/JCSAdminServlet.java |   181 +
 .../jcs/admin/servlet/JCSAdminServletDefault.vm    |    64 +
 .../admin/servlet/JCSAdminServletRegionDetail.vm   |    50 +
 .../jcs/auxiliary/AbstractAuxiliaryCache.java      |   210 +
 .../AbstractAuxiliaryCacheAttributes.java          |   130 +
 .../AbstractAuxiliaryCacheEventLogging.java        |   353 +
 .../commons/jcs/auxiliary/AuxiliaryCache.java      |    77 +
 .../jcs/auxiliary/AuxiliaryCacheAttributes.java    |    95 +
 .../jcs/auxiliary/AuxiliaryCacheConfigurator.java  |   128 +
 .../jcs/auxiliary/AuxiliaryCacheFactory.java       |    59 +
 .../jcs/auxiliary/AuxiliaryCacheManager.java       |    46 +
 .../jcs/auxiliary/disk/AbstractDiskCache.java      |   872 +
 .../disk/AbstractDiskCacheAttributes.java          |   202 +
 .../auxiliary/disk/AbstractDiskCacheManager.java   |    67 +
 .../commons/jcs/auxiliary/disk/LRUMapJCS.java      |    75 +
 .../jcs/auxiliary/disk/PurgatoryElement.java       |   157 +
 .../disk/behavior/IDiskCacheAttributes.java        |   105 +
 .../jcs/auxiliary/disk/block/BlockDisk.java        |   524 +
 .../jcs/auxiliary/disk/block/BlockDiskCache.java   |   738 +
 .../disk/block/BlockDiskCacheAttributes.java       |   117 +
 .../disk/block/BlockDiskCacheFactory.java          |    93 +
 .../disk/block/BlockDiskCacheManager.java          |   141 +
 .../disk/block/BlockDiskElementDescriptor.java     |   132 +
 .../auxiliary/disk/block/BlockDiskKeyStore.java    |   387 +
 .../jcs/auxiliary/disk/indexed/IndexedDisk.java    |   281 +
 .../auxiliary/disk/indexed/IndexedDiskCache.java   |  1692 +
 .../disk/indexed/IndexedDiskCacheAttributes.java   |   207 +
 .../disk/indexed/IndexedDiskCacheFactory.java      |    93 +
 .../disk/indexed/IndexedDiskCacheManager.java      |   141 +
 .../auxiliary/disk/indexed/IndexedDiskDumper.java  |    57 +
 .../disk/indexed/IndexedDiskElementDescriptor.java |   118 +
 .../jcs/auxiliary/disk/jdbc/JDBCDiskCache.java     |  1198 +
 .../disk/jdbc/JDBCDiskCacheAttributes.java         |   290 +
 .../auxiliary/disk/jdbc/JDBCDiskCacheFactory.java  |    80 +
 .../auxiliary/disk/jdbc/JDBCDiskCacheManager.java  |   148 +
 .../jdbc/JDBCDiskCacheManagerAbstractTemplate.java |   213 +
 .../disk/jdbc/JDBCDiskCachePoolAccess.java         |   271 +
 .../jdbc/JDBCDiskCachePoolAccessAttributes.java    |   179 +
 .../disk/jdbc/JDBCDiskCachePoolAccessManager.java  |   211 +
 .../jcs/auxiliary/disk/jdbc/ShrinkerThread.java    |   169 +
 .../jcs/auxiliary/disk/jdbc/TableState.java        |   114 +
 .../disk/jdbc/hsql/HSQLDiskCacheFactory.java       |   230 +
 .../auxiliary/disk/jdbc/mysql/MySQLDiskCache.java  |   168 +
 .../disk/jdbc/mysql/MySQLDiskCacheAttributes.java  |   107 +
 .../disk/jdbc/mysql/MySQLDiskCacheFactory.java     |    78 +
 .../disk/jdbc/mysql/MySQLDiskCacheManager.java     |   295 +
 .../disk/jdbc/mysql/MySQLTableOptimizer.java       |   325 +
 .../jdbc/mysql/util/ScheduleFormatException.java   |    40 +
 .../disk/jdbc/mysql/util/ScheduleParser.java       |   111 +
 .../apache/commons/jcs/auxiliary/disk/package.html |    27 +
 .../jcs/auxiliary/lateral/LateralCache.java        |   457 +
 .../lateral/LateralCacheAbstractFactory.java       |   101 +
 .../lateral/LateralCacheAbstractManager.java       |   148 +
 .../auxiliary/lateral/LateralCacheAttributes.java  |   313 +
 .../jcs/auxiliary/lateral/LateralCacheMonitor.java |   290 +
 .../jcs/auxiliary/lateral/LateralCacheNoWait.java  |   436 +
 .../lateral/LateralCacheNoWaitFacade.java          |   533 +
 .../jcs/auxiliary/lateral/LateralCacheRestore.java |   101 +
 .../lateral/LateralCacheWatchRepairable.java       |    34 +
 .../jcs/auxiliary/lateral/LateralCommand.java      |    47 +
 .../lateral/LateralElementDescriptor.java          |    83 +
 .../auxiliary/lateral/ZombieLateralCacheWatch.java |    34 +
 .../lateral/behavior/ILateralCacheAttributes.java  |   202 +
 .../lateral/behavior/ILateralCacheListener.java    |    51 +
 .../lateral/behavior/ILateralCacheManager.java     |    72 +
 .../lateral/behavior/ILateralCacheObserver.java    |    31 +
 .../commons/jcs/auxiliary/lateral/package.html     |    26 +
 .../lateral/socket/tcp/LateralTCPCacheFactory.java |   219 +
 .../lateral/socket/tcp/LateralTCPCacheManager.java |   355 +
 .../socket/tcp/LateralTCPDiscoveryListener.java    |   356 +
 .../tcp/LateralTCPDiscoveryListenerManager.java    |    90 +
 .../lateral/socket/tcp/LateralTCPListener.java     |   774 +
 .../lateral/socket/tcp/LateralTCPSender.java       |   363 +
 .../lateral/socket/tcp/LateralTCPService.java      |   547 +
 .../socket/tcp/TCPLateralCacheAttributes.java      |   399 +
 .../tcp/behavior/ITCPLateralCacheAttributes.java   |   218 +
 .../org/apache/commons/jcs/auxiliary/package.html  |    25 +
 .../remote/AbstractRemoteAuxiliaryCache.java       |   718 +
 .../remote/AbstractRemoteCacheListener.java        |   350 +
 .../remote/AbstractRemoteCacheNoWaitFacade.java    |   472 +
 .../remote/CommonRemoteCacheAttributes.java        |   323 +
 .../commons/jcs/auxiliary/remote/RemoteCache.java  |   186 +
 .../auxiliary/remote/RemoteCacheAttributes.java    |   261 +
 .../jcs/auxiliary/remote/RemoteCacheFactory.java   |   189 +
 .../remote/RemoteCacheFailoverRunner.java          |   492 +
 .../jcs/auxiliary/remote/RemoteCacheListener.java  |   121 +
 .../jcs/auxiliary/remote/RemoteCacheManager.java   |   611 +
 .../jcs/auxiliary/remote/RemoteCacheMonitor.java   |   224 +
 .../jcs/auxiliary/remote/RemoteCacheNoWait.java    |   529 +
 .../auxiliary/remote/RemoteCacheNoWaitFacade.java  |   102 +
 .../jcs/auxiliary/remote/RemoteCacheRestore.java   |   127 +
 .../remote/RemoteCacheWatchRepairable.java         |    33 +
 .../commons/jcs/auxiliary/remote/RemoteUtils.java  |   224 +
 .../auxiliary/remote/ZombieRemoteCacheWatch.java   |    34 +
 .../behavior/ICommonRemoteCacheAttributes.java     |   177 +
 .../remote/behavior/IRemoteCacheAttributes.java    |   178 +
 .../remote/behavior/IRemoteCacheClient.java        |    61 +
 .../remote/behavior/IRemoteCacheConstants.java     |    70 +
 .../remote/behavior/IRemoteCacheDispatcher.java    |    46 +
 .../remote/behavior/IRemoteCacheListener.java      |    81 +
 .../remote/behavior/IRemoteCacheObserver.java      |    34 +
 .../http/behavior/IRemoteHttpCacheConstants.java   |    31 +
 .../remote/http/client/AbstractHttpClient.java     |   193 +
 .../remote/http/client/RemoteHttpCache.java        |   116 +
 .../http/client/RemoteHttpCacheAttributes.java     |   228 +
 .../remote/http/client/RemoteHttpCacheClient.java  |   496 +
 .../http/client/RemoteHttpCacheDispatcher.java     |   231 +
 .../remote/http/client/RemoteHttpCacheFactory.java |   119 +
 .../remote/http/client/RemoteHttpCacheManager.java |   267 +
 .../remote/http/client/RemoteHttpCacheMonitor.java |   248 +
 .../http/client/RemoteHttpClientListener.java      |    53 +
 .../client/behavior/IRemoteHttpCacheClient.java    |    51 +
 .../http/server/AbstractRemoteCacheService.java    |   603 +
 .../http/server/RemoteCacheServiceAdaptor.java     |   174 +
 .../server/RemoteHttpCacheServerAttributes.java    |   115 +
 .../remote/http/server/RemoteHttpCacheService.java |   270 +
 .../remote/http/server/RemoteHttpCacheServlet.java |   264 +
 .../http/server/RemoteHttpCacheSeviceFactory.java  |    91 +
 .../commons/jcs/auxiliary/remote/package.html      |    25 +
 .../remote/server/RegistryKeepAliveRunner.java     |   189 +
 .../auxiliary/remote/server/RemoteCacheServer.java |  1707 +
 .../remote/server/RemoteCacheServerAttributes.java |   211 +
 .../remote/server/RemoteCacheServerFactory.java    |   539 +
 .../remote/server/RemoteCacheStartupServlet.java   |   216 +
 .../TimeoutConfigurableRMISocketFactory.java       |   111 +
 .../remote/server/behavior/IRemoteCacheServer.java |    38 +
 .../behavior/IRemoteCacheServerAttributes.java     |   118 +
 .../remote/server/behavior/RemoteType.java         |    32 +
 .../remote/util/RemoteCacheRequestFactory.java     |   254 +
 .../auxiliary/remote/value/RemoteCacheRequest.java |   187 +
 .../remote/value/RemoteCacheResponse.java          |   105 +
 .../auxiliary/remote/value/RemoteRequestType.java  |    53 +
 .../jcs/engine/AbstractCacheEventQueue.java        |   499 +
 .../apache/commons/jcs/engine/CacheAdaptor.java    |   143 +
 .../apache/commons/jcs/engine/CacheConstants.java  |    34 +
 .../apache/commons/jcs/engine/CacheElement.java    |   160 +
 .../commons/jcs/engine/CacheElementSerialized.java |    77 +
 .../apache/commons/jcs/engine/CacheEventQueue.java |   418 +
 .../commons/jcs/engine/CacheEventQueueFactory.java |    88 +
 .../org/apache/commons/jcs/engine/CacheGroup.java  |    59 +
 .../org/apache/commons/jcs/engine/CacheInfo.java   |    47 +
 .../apache/commons/jcs/engine/CacheListeners.java  |    82 +
 .../org/apache/commons/jcs/engine/CacheStatus.java |    37 +
 .../commons/jcs/engine/CacheWatchRepairable.java   |   200 +
 .../jcs/engine/CompositeCacheAttributes.java       |   447 +
 .../commons/jcs/engine/ElementAttributes.java      |   469 +
 .../commons/jcs/engine/PooledCacheEventQueue.java  |   228 +
 .../commons/jcs/engine/ZombieCacheService.java     |   156 +
 .../jcs/engine/ZombieCacheServiceNonLocal.java     |   313 +
 .../commons/jcs/engine/ZombieCacheWatch.java       |    73 +
 .../apache/commons/jcs/engine/behavior/ICache.java |   141 +
 .../commons/jcs/engine/behavior/ICacheElement.java |    74 +
 .../engine/behavior/ICacheElementSerialized.java   |    41 +
 .../jcs/engine/behavior/ICacheEventQueue.java      |   147 +
 .../jcs/engine/behavior/ICacheListener.java        |    86 +
 .../commons/jcs/engine/behavior/ICacheManager.java |    42 +
 .../jcs/engine/behavior/ICacheObserver.java        |    78 +
 .../commons/jcs/engine/behavior/ICacheRestore.java |    37 +
 .../commons/jcs/engine/behavior/ICacheService.java |   117 +
 .../jcs/engine/behavior/ICacheServiceAdmin.java    |    51 +
 .../jcs/engine/behavior/ICacheServiceNonLocal.java |   118 +
 .../commons/jcs/engine/behavior/ICacheType.java    |    49 +
 .../engine/behavior/ICompositeCacheAttributes.java |   246 +
 .../engine/behavior/ICompositeCacheManager.java    |    54 +
 .../jcs/engine/behavior/IElementAttributes.java    |   203 +
 .../jcs/engine/behavior/IElementSerializer.java    |    50 +
 .../jcs/engine/behavior/IProvideScheduler.java     |    38 +
 .../jcs/engine/behavior/IRequireScheduler.java     |    39 +
 .../jcs/engine/behavior/IShutdownObservable.java   |    55 +
 .../jcs/engine/behavior/IShutdownObserver.java     |    41 +
 .../commons/jcs/engine/behavior/IZombie.java       |    30 +
 .../commons/jcs/engine/behavior/package.html       |    25 +
 .../commons/jcs/engine/control/CompositeCache.java |  1848 +
 .../engine/control/CompositeCacheConfigurator.java |   648 +
 .../jcs/engine/control/CompositeCacheManager.java  |   978 +
 .../jcs/engine/control/event/ElementEvent.java     |    63 +
 .../engine/control/event/ElementEventQueue.java    |   203 +
 .../control/event/behavior/ElementEventType.java   |    54 +
 .../control/event/behavior/IElementEvent.java      |    42 +
 .../event/behavior/IElementEventHandler.java       |    40 +
 .../control/event/behavior/IElementEventQueue.java |    48 +
 .../jcs/engine/control/group/GroupAttrName.java    |   117 +
 .../commons/jcs/engine/control/group/GroupId.java  |   103 +
 .../apache/commons/jcs/engine/control/package.html |    25 +
 .../commons/jcs/engine/logging/CacheEvent.java     |   177 +
 .../logging/CacheEventLoggerDebugLogger.java       |   113 +
 .../jcs/engine/logging/behavior/ICacheEvent.java   |    77 +
 .../engine/logging/behavior/ICacheEventLogger.java |    91 +
 .../jcs/engine/match/KeyMatcherPatternImpl.java    |    66 +
 .../jcs/engine/match/behavior/IKeyMatcher.java     |    36 +
 .../AbstractDoubleLinkedListMemoryCache.java       |   810 +
 .../jcs/engine/memory/AbstractMemoryCache.java     |   339 +
 .../jcs/engine/memory/behavior/IMemoryCache.java   |   187 +
 .../jcs/engine/memory/fifo/FIFOMemoryCache.java    |    60 +
 .../jcs/engine/memory/lru/LHMLRUMemoryCache.java   |   361 +
 .../jcs/engine/memory/lru/LRUMemoryCache.java      |    67 +
 .../commons/jcs/engine/memory/lru/package.html     |    25 +
 .../jcs/engine/memory/mru/MRUMemoryCache.java      |    63 +
 .../commons/jcs/engine/memory/mru/package.html     |    26 +
 .../apache/commons/jcs/engine/memory/package.html  |    25 +
 .../engine/memory/shrinking/ShrinkerThread.java    |   222 +
 .../memory/util/MemoryElementDescriptor.java       |    47 +
 .../org/apache/commons/jcs/engine/package.html     |    25 +
 .../commons/jcs/engine/stats/CacheStats.java       |   116 +
 .../commons/jcs/engine/stats/StatElement.java      |   104 +
 .../org/apache/commons/jcs/engine/stats/Stats.java |    99 +
 .../jcs/engine/stats/behavior/ICacheStats.java     |    51 +
 .../jcs/engine/stats/behavior/IStatElement.java    |    54 +
 .../commons/jcs/engine/stats/behavior/IStats.java  |    63 +
 .../jcs/io/ObjectInputStreamClassLoaderAware.java  |    62 +
 .../main/java/org/apache/commons/jcs/package.html  |    26 +
 .../jcs/utils/access/AbstractJCSWorkerHelper.java  |    59 +
 .../apache/commons/jcs/utils/access/JCSWorker.java |   306 +
 .../commons/jcs/utils/access/JCSWorkerHelper.java  |    61 +
 .../commons/jcs/utils/config/OptionConverter.java  |   427 +
 .../commons/jcs/utils/config/PropertySetter.java   |   300 +
 .../jcs/utils/config/PropertySetterException.java  |    75 +
 .../apache/commons/jcs/utils/config/package.html   |    28 +
 .../jcs/utils/discovery/DiscoveredService.java     |   183 +
 .../jcs/utils/discovery/UDPCleanupRunner.java      |    97 +
 .../utils/discovery/UDPDiscoveryAttributes.java    |   231 +
 .../jcs/utils/discovery/UDPDiscoveryManager.java   |   114 +
 .../jcs/utils/discovery/UDPDiscoveryMessage.java   |   166 +
 .../jcs/utils/discovery/UDPDiscoveryReceiver.java  |   380 +
 .../jcs/utils/discovery/UDPDiscoverySender.java    |   292 +
 .../utils/discovery/UDPDiscoverySenderThread.java  |   199 +
 .../jcs/utils/discovery/UDPDiscoveryService.java   |   438 +
 .../discovery/behavior/IDiscoveryListener.java     |    44 +
 .../apache/commons/jcs/utils/net/HostNameUtil.java |   147 +
 .../jcs/utils/props/AbstractPropertyContainer.java |   190 +
 .../commons/jcs/utils/props/PropertiesFactory.java |    36 +
 .../jcs/utils/props/PropertiesFactoryFileImpl.java |    40 +
 .../commons/jcs/utils/props/PropertyLoader.java    |   174 +
 .../utils/serialization/CompressingSerializer.java |   126 +
 .../serialization/SerializationConversionUtil.java |   147 +
 .../utils/serialization/StandardSerializer.java    |    91 +
 .../utils/servlet/JCSServletContextListener.java   |    87 +
 .../commons/jcs/utils/struct/BoundedQueue.java     |    94 +
 .../commons/jcs/utils/struct/DoubleLinkedList.java |   303 +
 .../jcs/utils/struct/DoubleLinkedListNode.java     |    62 +
 .../jcs/utils/struct/LRUElementDescriptor.java     |    60 +
 .../apache/commons/jcs/utils/struct/LRUMap.java    |   698 +
 .../commons/jcs/utils/struct/LRUMapEntry.java      |    82 +
 .../commons/jcs/utils/struct/SingleLinkedList.java |   137 +
 .../jcs/utils/struct/SortedPreferentialArray.java  |   612 +
 .../jcs/utils/threadpool/DaemonThreadFactory.java  |    74 +
 .../jcs/utils/threadpool/PoolConfiguration.java    |   268 +
 .../jcs/utils/threadpool/ThreadPoolManager.java    |   445 +
 .../commons/jcs/utils/timing/ElapsedTimer.java     |    58 +
 .../apache/commons/jcs/utils/timing/SleepUtil.java |    50 +
 .../commons/jcs/utils/zip/CompressionUtil.java     |   203 +
 .../test/conf/JCSAdminServlet.velocity.properties  |    21 +
 .../src/test/conf/LocalStrings.properties          |    57 +
 commons-jcs-core/src/test/conf/cache.ccf           |    69 +
 commons-jcs-core/src/test/conf/cache.policy        |    41 +
 commons-jcs-core/src/test/conf/cache2.ccf          |   153 +
 commons-jcs-core/src/test/conf/cache3.ccf          |   145 +
 commons-jcs-core/src/test/conf/cacheB.ccf          |   241 +
 commons-jcs-core/src/test/conf/cacheBDB.ccf        |    55 +
 commons-jcs-core/src/test/conf/cacheD10A.ccf       |   167 +
 commons-jcs-core/src/test/conf/cacheD10B.ccf       |   167 +
 commons-jcs-core/src/test/conf/cacheID.ccf         |    65 +
 commons-jcs-core/src/test/conf/cacheJG1.ccf        |   100 +
 commons-jcs-core/src/test/conf/cacheJG2.ccf        |    81 +
 commons-jcs-core/src/test/conf/cacheJG3.ccf        |    91 +
 commons-jcs-core/src/test/conf/cacheLMD1.ccf       |   112 +
 commons-jcs-core/src/test/conf/cacheNA.ccf         |    99 +
 commons-jcs-core/src/test/conf/cacheNA2.ccf        |    99 +
 commons-jcs-core/src/test/conf/cacheNA3.ccf        |   125 +
 commons-jcs-core/src/test/conf/cacheNB.ccf         |   100 +
 commons-jcs-core/src/test/conf/cacheRC.ccf         |   246 +
 commons-jcs-core/src/test/conf/cacheRC1.ccf        |    79 +
 commons-jcs-core/src/test/conf/cacheRC2.ccf        |    79 +
 commons-jcs-core/src/test/conf/cacheRCN1.ccf       |    79 +
 commons-jcs-core/src/test/conf/cacheRCN2.ccf       |    79 +
 commons-jcs-core/src/test/conf/cacheRCSimple.ccf   |   218 +
 commons-jcs-core/src/test/conf/cacheRC_CEL.ccf     |   145 +
 commons-jcs-core/src/test/conf/cacheRHTTP.ccf      |    40 +
 commons-jcs-core/src/test/conf/cacheTCP1.ccf       |    66 +
 commons-jcs-core/src/test/conf/cacheTCP2.ccf       |    80 +
 commons-jcs-core/src/test/conf/cacheTCP3.ccf       |    81 +
 commons-jcs-core/src/test/conf/cacheTCP4.ccf       |    68 +
 commons-jcs-core/src/test/conf/jcsutils.properties |    18 +
 commons-jcs-core/src/test/conf/je.properties       |    51 +
 commons-jcs-core/src/test/conf/log4j.properties    |    69 +
 commons-jcs-core/src/test/conf/logger.properties   |    75 +
 commons-jcs-core/src/test/conf/myjetty.xml         |   124 +
 commons-jcs-core/src/test/conf/myjetty2.xml        |   124 +
 commons-jcs-core/src/test/conf/remote.cache.ccf    |    68 +
 commons-jcs-core/src/test/conf/remote.cache2.ccf   |    70 +
 commons-jcs-core/src/test/conf/remote.cache3.ccf   |    71 +
 commons-jcs-core/src/test/conf/remote.cacheCEL.ccf |    68 +
 .../src/test/conf/remote.cacheCEL_CSF.ccf          |    69 +
 commons-jcs-core/src/test/conf/remote.cacheRS1.ccf |    85 +
 commons-jcs-core/src/test/conf/remote.cacheRS2.ccf |    86 +
 commons-jcs-core/src/test/conf/remote.tomcat.xml   |   351 +
 commons-jcs-core/src/test/conf/tomcat.xml          |   351 +
 .../commons/jcs/ConcurrentRemovalLoadTest.java     |   156 +
 .../jcs/JCSCacheElementRetrievalUnitTest.java      |    54 +
 .../jcs/JCSConcurrentCacheAccessUnitTest.java      |   164 +
 .../apache/commons/jcs/JCSLightLoadUnitTest.java   |   102 +
 .../jcs/JCSRemovalSimpleConcurrentTest.java        |   192 +
 .../java/org/apache/commons/jcs/JCSThrashTest.java |   330 +
 .../java/org/apache/commons/jcs/JCSUnitTest.java   |   115 +
 .../commons/jcs/JCSvsHashtablePerformanceTest.java |   210 +
 .../org/apache/commons/jcs/RemovalTestUtil.java    |   130 +
 .../commons/jcs/TestLogConfigurationUtil.java      |    46 +
 .../apache/commons/jcs/TestTCPLateralCache.java    |   155 +
 .../apache/commons/jcs/ZeroSizeCacheUnitTest.java  |    90 +
 .../commons/jcs/access/CacheAccessUnitTest.java    |   402 +
 .../commons/jcs/access/SystemPropertyUnitTest.java |    88 +
 .../apache/commons/jcs/access/TestCacheAccess.java |   965 +
 .../commons/jcs/admin/AdminBeanUnitTest.java       |   154 +
 .../commons/jcs/admin/CountingStreamUnitTest.java  |    77 +
 .../java/org/apache/commons/jcs/admin/TestJMX.java |    38 +
 .../AuxiliaryCacheConfiguratorUnitTest.java        |   129 +
 .../commons/jcs/auxiliary/MockAuxiliaryCache.java  |   218 +
 .../auxiliary/MockAuxiliaryCacheAttributes.java    |    40 +
 .../jcs/auxiliary/MockAuxiliaryCacheFactory.java   |    70 +
 .../jcs/auxiliary/MockCacheEventLogger.java        |    98 +
 .../auxiliary/disk/AbstractDiskCacheUnitTest.java  |   304 +
 .../commons/jcs/auxiliary/disk/DiskTestObject.java |    52 +
 .../jcs/auxiliary/disk/LRUMapJCSUnitTest.java      |    76 +
 .../auxiliary/disk/PurgatoryElementUnitTest.java   |    90 +
 .../block/BlockDiskCacheConcurrentUnitTest.java    |   258 +
 .../disk/block/BlockDiskCacheKeyStoreUnitTest.java |   129 +
 .../disk/block/BlockDiskCacheManagerUnitTest.java  |    55 +
 .../BlockDiskCacheRandomConcurrentTestUtil.java    |    83 +
 ...BlockDiskCacheSameRegionConcurrentUnitTest.java |   172 +
 .../disk/block/BlockDiskCacheSteadyLoadTest.java   |   161 +
 .../disk/block/BlockDiskCacheUnitTest.java         |   340 +
 .../block/BlockDiskElementDescriptorUnitTest.java  |    89 +
 .../auxiliary/disk/block/BlockDiskUnitTest.java    |   363 +
 .../block/HugeQuantityBlockDiskCacheLoadTest.java  |   135 +
 .../auxiliary/disk/indexed/DiskTestObjectUtil.java |   144 +
 .../indexed/HugeQuantityIndDiskCacheLoadTest.java  |   124 +
 .../disk/indexed/IndexDiskCacheUnitTest.java       |  1012 +
 ...dexedDiskCacheConcurrentNoDeadLockUnitTest.java |   148 +
 .../IndexedDiskCacheConcurrentUnitTest.java        |   251 +
 .../IndexedDiskCacheDefragPerformanceTest.java     |   180 +
 .../indexed/IndexedDiskCacheKeyStoreUnitTest.java  |   149 +
 .../indexed/IndexedDiskCacheManagerUnitTest.java   |    70 +
 .../indexed/IndexedDiskCacheNoMemoryUnitTest.java  |   178 +
 .../IndexedDiskCacheOptimizationUnitTest.java      |    93 +
 .../IndexedDiskCacheRandomConcurrentTestUtil.java  |    86 +
 ...dexedDiskCacheSameRegionConcurrentUnitTest.java |   208 +
 .../indexed/IndexedDiskCacheSteadyLoadTest.java    |   153 +
 .../auxiliary/disk/jdbc/HsqlSetupTableUtil.java    |    94 +
 .../disk/jdbc/JDBCDiskCacheManagerUnitTest.java    |    56 +
 .../JDBCDiskCachePoolAccessManagerUnitTest.java    |   110 +
 .../disk/jdbc/JDBCDiskCacheRemovalUnitTest.java    |   175 +
 .../disk/jdbc/JDBCDiskCacheSharedPoolUnitTest.java |   141 +
 .../disk/jdbc/JDBCDiskCacheShrinkUnitTest.java     |   279 +
 .../auxiliary/disk/jdbc/JDBCDiskCacheUnitTest.java |   194 +
 .../jdbc/hsql/HSQLDiskCacheConcurrentUnitTest.java |   175 +
 .../disk/jdbc/hsql/HSQLDiskCacheUnitTest.java      |   189 +
 .../mysql/MySQLDiskCacheHsqlBackedUnitTest.java    |   238 +
 .../jdbc/mysql/MySQLDiskCacheManagerUnitTest.java  |    56 +
 .../disk/jdbc/mysql/MySQLDiskCacheUnitTest.java    |    69 +
 .../mysql/MySQLTableOptimizerManualTester.java     |    83 +
 .../mysql/util/ScheduleParserUtilUnitTest.java     |   128 +
 .../lateral/LateralCacheNoWaitFacadeUnitTest.java  |   143 +
 .../lateral/http/broadcast/LateralCacheTester.java |    61 +
 .../tcp/LateralTCPConcurrentRandomTestUtil.java    |   195 +
 .../tcp/LateralTCPDiscoveryListenerUnitTest.java   |   284 +
 .../LateralTCPFilterRemoveHashCodeUnitTest.java    |   192 +
 .../tcp/LateralTCPIssueRemoveOnPutUnitTest.java    |   225 +
 .../tcp/LateralTCPNoDeadLockConcurrentTest.java    |   146 +
 .../lateral/socket/tcp/MockLateralCache.java       |   125 +
 .../lateral/socket/tcp/TestTCPLateralUnitTest.java |   367 +
 .../auxiliary/remote/MockRemoteCacheClient.java    |   280 +
 .../auxiliary/remote/MockRemoteCacheListener.java  |   169 +
 .../auxiliary/remote/MockRemoteCacheService.java   |   246 +
 .../auxiliary/remote/RemoteCacheClientTester.java  |   342 +
 .../remote/RemoteCacheListenerUnitTest.java        |   123 +
 .../remote/RemoteCacheNoWaitUnitTest.java          |   214 +
 .../jcs/auxiliary/remote/RemoteCacheUnitTest.java  |   319 +
 .../jcs/auxiliary/remote/RemoteUtilsUnitTest.java  |    51 +
 .../jcs/auxiliary/remote/TestRemoteCache.java      |   135 +
 .../remote/ZombieRemoteCacheServiceUnitTest.java   |   127 +
 .../http/client/MockRemoteCacheDispatcher.java     |    53 +
 .../http/client/RemoteHttpCacheClientUnitTest.java |   275 +
 .../client/RemoteHttpCacheDispatcherUniTest.java   |    52 +
 .../client/RemoteHttpCacheManagerUnitTest.java     |    98 +
 .../http/client/RemoteHttpCacheManualTester.java   |    75 +
 .../server/RemoteCacheServiceAdaptorUnitTest.java  |   190 +
 .../server/RemoteHttpCacheServiceUnitTest.java     |   181 +
 .../RemoteHttpCacheSeviceFactoryUnitTest.java      |   115 +
 .../BasicRemoteCacheClientServerUnitTest.java      |   335 +
 .../remote/server/MockRMISocketFactory.java        |    88 +
 .../server/RegistryKeepAliveRunnerUnitTest.java    |    49 +
 .../RemoteCacheServerAttributesUnitTest.java       |    64 +
 .../server/RemoteCacheServerFactoryUnitTest.java   |   202 +
 .../server/RemoteCacheServerStartupUtil.java       |   104 +
 .../remote/server/RemoteCacheServerUnitTest.java   |   501 +
 ...imeoutConfigurableRMISocketFactoryUnitTest.java |    53 +
 .../util/RemoteCacheRequestFactoryUnitTest.java    |   144 +
 .../jcs/engine/CacheEventQueueFactoryUnitTest.java |    67 +
 .../commons/jcs/engine/ElementAttributesUtils.java |    28 +
 .../jcs/engine/EventQueueConcurrentLoadTest.java   |   388 +
 .../commons/jcs/engine/MockCacheEventQueue.java    |    35 +
 .../jcs/engine/MockCacheServiceNonLocal.java       |   246 +
 .../jcs/engine/SystemPropertyUsageUnitTest.java    |    98 +
 .../engine/ZombieCacheServiceNonLocalUnitTest.java |   125 +
 .../engine/control/CacheManagerStatsUnitTest.java  |    71 +
 .../CompositeCacheConfiguratorUnitTest.java        |    92 +
 .../control/CompositeCacheDiskUsageUnitTest.java   |   513 +
 .../jcs/engine/control/CompositeCacheUnitTest.java |   245 +
 .../engine/control/MockCompositeCacheManager.java  |   119 +
 .../jcs/engine/control/MockElementSerializer.java  |    87 +
 .../control/event/ElementEventHandlerMockImpl.java |   186 +
 .../control/event/SimpleEventHandlingUnitTest.java |   354 +
 .../CacheEventLoggerDebugLoggerUnitTest.java       |   143 +
 .../jcs/engine/logging/MockCacheEventLogger.java   |    95 +
 .../match/KeyMatcherPatternImpllUnitTest.java      |   118 +
 .../commons/jcs/engine/memory/MockMemoryCache.java |   256 +
 .../memory/fifo/FIFOMemoryCacheUnitTest.java       |   109 +
 .../lru/LHMLRUMemoryCacheConcurrentUnitTest.java   |   175 +
 .../memory/lru/LHMLRUMemoryCacheUnitTest.java      |   312 +
 .../lru/LRUMemoryCacheConcurrentUnitTest.java      |   171 +
 .../engine/memory/mru/LRUvsMRUPerformanceTest.java |   183 +
 .../engine/memory/mru/MRUMemoryCacheUnitTest.java  |   312 +
 .../memory/shrinking/ShrinkerThreadUnitTest.java   |   336 +
 .../jcs/utils/access/JCSWorkerUnitTest.java        |    71 +
 .../jcs/utils/config/PropertySetterUnitTest.java   |    61 +
 .../jcs/utils/discovery/MockDiscoveryListener.java |    56 +
 .../discovery/UDPDiscoverySenderUnitTest.java      |   154 +
 .../discovery/UDPDiscoveryServiceUnitTest.java     |   222 +
 .../jcs/utils/discovery/UDPDiscoveryUnitTest.java  |    90 +
 .../jcs/utils/net/HostNameUtilUnitTest.java        |    44 +
 .../CompressingSerializerUnitTest.java             |   119 +
 .../SerializationConversionUtilUnitTest.java       |   195 +
 .../serialization/StandardSerializerUnitTest.java  |   102 +
 .../jcs/utils/struct/BoundedQueueUnitTest.java     |    93 +
 .../jcs/utils/struct/DoubleLinkedListUnitTest.java |   189 +
 .../struct/JCSvsCommonsLRUMapPerformanceTest.java  |   214 +
 .../jcs/utils/struct/LRUMapConcurrentTest.java     |   257 +
 .../jcs/utils/struct/LRUMapConcurrentUnitTest.java |   267 +
 .../jcs/utils/struct/LRUMapPerformanceTest.java    |   204 +
 .../commons/jcs/utils/struct/LRUMapUnitTest.java   |   133 +
 .../jcs/utils/struct/SingleLinkedListUnitTest.java |   104 +
 .../jcs/utils/struct/SortedPrefArrayUnitTest.java  |   454 +
 .../threadpool/ThreadPoolManagerUnitTest.java      |   194 +
 .../jcs/utils/threadpool/ThreadPoolUnitTest.java   |    88 +
 .../jcs/utils/zip/CompressionUtilUnitTest.java     |    97 +
 .../src/test/test-conf/TestARCCache.ccf            |    33 +
 .../src/test/test-conf/TestBDBJEDiskCacheCon.ccf   |    90 +
 .../src/test/test-conf/TestBlockDiskCache.ccf      |    94 +
 .../src/test/test-conf/TestBlockDiskCacheCon.ccf   |    94 +
 .../src/test/test-conf/TestBlockDiskCacheHuge.ccf  |    61 +
 .../test-conf/TestBlockDiskCacheSteadyLoad.ccf     |    32 +
 .../src/test/test-conf/TestDiskCache.ccf           |    94 +
 .../src/test/test-conf/TestDiskCacheCon.ccf        |    96 +
 .../test-conf/TestDiskCacheDefragPerformance.ccf   |    34 +
 .../src/test/test-conf/TestDiskCacheHuge.ccf       |    62 +
 .../src/test/test-conf/TestDiskCacheNoMemory.ccf   |    93 +
 .../src/test/test-conf/TestDiskCacheSteadyLoad.ccf |    34 +
 .../test/test-conf/TestDiskCacheUsagePattern.ccf   |    85 +
 .../src/test/test-conf/TestHSQLDiskCache.ccf       |    83 +
 .../test/test-conf/TestHSQLDiskCacheConcurrent.ccf |    84 +
 commons-jcs-core/src/test/test-conf/TestJCS-73.ccf |    42 +
 .../src/test/test-conf/TestJCSvHashtablePerf.ccf   |    60 +
 .../src/test/test-conf/TestJDBCDiskCache.ccf       |    62 +
 .../test/test-conf/TestJDBCDiskCacheRemoval.ccf    |    62 +
 .../test/test-conf/TestJDBCDiskCacheSharedPool.ccf |    83 +
 .../src/test/test-conf/TestJDBCDiskCacheShrink.ccf |    85 +
 .../src/test/test-conf/TestJispDiskCache.ccf       |    40 +
 .../src/test/test-conf/TestLHMLRUCache.ccf         |    23 +
 .../src/test/test-conf/TestMRUCache.ccf            |    45 +
 .../src/test/test-conf/TestMySQLDiskCache.ccf      |    66 +
 .../test/test-conf/TestRemoteCacheClientServer.ccf |    39 +
 .../test/test-conf/TestRemoteCacheEventLogging.ccf |    47 +
 .../src/test/test-conf/TestRemoteCacheServer.ccf   |    22 +
 .../src/test/test-conf/TestRemoteClient.ccf        |    46 +
 .../src/test/test-conf/TestRemoteHttpCache.ccf     |    39 +
 .../src/test/test-conf/TestRemoteServer.ccf        |    44 +
 .../src/test/test-conf/TestRemoval.ccf             |    61 +
 .../src/test/test-conf/TestSimpleEventHandling.ccf |    65 +
 .../src/test/test-conf/TestSimpleLoad.ccf          |    28 +
 .../src/test/test-conf/TestSystemProperties.ccf    |    39 +
 .../src/test/test-conf/TestSystemPropertyUsage.ccf |    20 +
 .../src/test/test-conf/TestTCPLateralCache.ccf     |    72 +
 .../test-conf/TestTCPLateralCacheConcurrent.ccf    |    31 +
 .../test-conf/TestTCPLateralIssueRemoveCache.ccf   |    72 +
 .../test/test-conf/TestTCPLateralRemoveFilter.ccf  |    43 +
 commons-jcs-core/src/test/test-conf/TestThrash.ccf |    20 +
 .../src/test/test-conf/TestUDPDiscovery.ccf        |    90 +
 .../src/test/test-conf/TestZeroSizeCache.ccf       |    33 +
 commons-jcs-core/src/test/test-conf/cache.ccf      |    57 +
 commons-jcs-core/src/test/test-conf/cache2.ccf     |   235 +
 commons-jcs-core/src/test/test-conf/je.properties  |    51 +
 .../src/test/test-conf/log4j.properties            |    44 +
 .../src/test/test-conf/logger.properties           |    75 +
 .../src/test/test-conf/thread_pool.properties      |    72 +
 commons-jcs-dist/LICENSE.txt                       |   202 +
 commons-jcs-dist/NOTICE.txt                        |     6 +
 commons-jcs-dist/pom.xml                           |   149 +
 commons-jcs-dist/src/assembly/bin.xml              |    41 +
 commons-jcs-dist/src/assembly/src.xml              |    40 +
 commons-jcs-jcache-extras/LICENSE.txt              |   202 +
 commons-jcs-jcache-extras/NOTICE.txt               |     6 +
 commons-jcs-jcache-extras/pom.xml                  |    71 +
 .../commons/jcs/jcache/extras/cdi/AnyLiteral.java  |    33 +
 .../jcs/jcache/extras/cdi/CacheManagerBean.java    |   126 +
 .../jcs/jcache/extras/cdi/CacheProviderBean.java   |   126 +
 .../jcs/jcache/extras/cdi/DefaultLiteral.java      |    33 +
 .../jcache/extras/cdi/ExtraJCacheExtension.java    |    92 +
 .../jcs/jcache/extras/closeable/Closeables.java    |    54 +
 .../jcache/extras/loader/CacheLoaderAdapter.java   |    49 +
 .../jcache/extras/loader/CompositeCacheLoader.java |    94 +
 .../jcs/jcache/extras/web/InMemoryResponse.java    |   277 +
 .../jcs/jcache/extras/web/JCacheFilter.java        |   341 +
 .../jcs/jcache/extras/writer/AsyncCacheWriter.java |   156 +
 .../jcache/extras/writer/CacheWriterAdapter.java   |    52 +
 .../jcache/extras/writer/CompositeCacheWriter.java |   147 +
 .../services/javax.enterprise.inject.spi.Extension |     1 +
 .../jcs/jcache/extras/InternalCacheRule.java       |    88 +
 .../extras/cdi/ExtraJCacheExtensionTest.java       |    94 +
 .../extras/loader/CacheLoaderAdapterTest.java      |    78 +
 .../extras/loader/CompositeCacheLoaderTest.java    |    70 +
 .../jcs/jcache/extras/web/JCacheFilterTest.java    |   190 +
 .../extras/writer/CacheWriterAdapterTest.java      |    80 +
 .../extras/writer/CompositeCacheWriterTest.java    |    90 +
 .../src/test/resources/META-INF/beans.xml          |    25 +
 commons-jcs-jcache-openjpa/LICENSE.txt             |   202 +
 commons-jcs-jcache-openjpa/NOTICE.txt              |     6 +
 commons-jcs-jcache-openjpa/pom.xml                 |    68 +
 .../jcs/jcache/openjpa/OpenJPAJCacheDataCache.java |   160 +
 .../openjpa/OpenJPAJCacheDataCacheManager.java     |   120 +
 .../jcache/openjpa/OpenJPAJCacheDerivation.java    |    75 +
 .../jcache/openjpa/OpenJPAJCacheQueryCache.java    |   125 +
 .../org.apache.openjpa.lib.conf.ProductDerivation  |     1 +
 .../jcache/openjpa/OpenJPAJCacheDataCacheTest.java |   141 +
 commons-jcs-jcache/LICENSE.txt                     |   202 +
 commons-jcs-jcache/NOTICE.txt                      |     6 +
 commons-jcs-jcache/pom.xml                         |   112 +
 .../org/apache/commons/jcs/jcache/Asserts.java     |    36 +
 .../commons/jcs/jcache/EvictionListener.java       |    46 +
 .../commons/jcs/jcache/ImmutableIterable.java      |    39 +
 .../commons/jcs/jcache/ImmutableIterator.java      |    49 +
 .../org/apache/commons/jcs/jcache/JCSCache.java    |  1009 +
 .../commons/jcs/jcache/JCSCacheEntryEvent.java     |    75 +
 .../commons/jcs/jcache/JCSCachingManager.java      |   339 +
 .../commons/jcs/jcache/JCSCachingProvider.java     |   158 +
 .../commons/jcs/jcache/JCSConfiguration.java       |   200 +
 .../org/apache/commons/jcs/jcache/JCSEntry.java    |    55 +
 .../org/apache/commons/jcs/jcache/JCSListener.java |   127 +
 .../apache/commons/jcs/jcache/JCSMutableEntry.java |    74 +
 .../org/apache/commons/jcs/jcache/NoFilter.java    |    39 +
 .../org/apache/commons/jcs/jcache/NoLoader.java    |    51 +
 .../org/apache/commons/jcs/jcache/NoWriter.java    |    59 +
 .../org/apache/commons/jcs/jcache/Statistics.java  |   166 +
 .../commons/jcs/jcache/TempStateCacheView.java     |   331 +
 .../java/org/apache/commons/jcs/jcache/Times.java  |    37 +
 .../commons/jcs/jcache/cdi/CDIJCacheHelper.java    |   188 +
 .../jcs/jcache/cdi/CacheInvocationContextImpl.java |    70 +
 .../jcache/cdi/CacheInvocationParameterImpl.java   |    63 +
 .../jcs/jcache/cdi/CacheKeyGeneratorImpl.java      |    40 +
 .../jcache/cdi/CacheKeyInvocationContextImpl.java  |    87 +
 .../jcs/jcache/cdi/CacheMethodDetailsImpl.java     |    68 +
 .../jcs/jcache/cdi/CachePutInterceptor.java        |    85 +
 .../jcs/jcache/cdi/CacheRemoveAllInterceptor.java  |    82 +
 .../jcs/jcache/cdi/CacheRemoveInterceptor.java     |    86 +
 .../jcs/jcache/cdi/CacheResolverFactoryImpl.java   |    74 +
 .../commons/jcs/jcache/cdi/CacheResolverImpl.java  |    40 +
 .../jcs/jcache/cdi/CacheResultInterceptor.java     |   102 +
 .../jcs/jcache/cdi/GeneratedCacheKeyImpl.java      |    56 +
 .../cdi/MakeJCacheCDIInterceptorFriendly.java      |    40 +
 .../jmx/ConfigurableMBeanServerIdBuilder.java      |   170 +
 .../commons/jcs/jcache/jmx/JCSCacheMXBean.java     |   114 +
 .../jcs/jcache/jmx/JCSCacheStatisticsMXBean.java   |   125 +
 .../org/apache/commons/jcs/jcache/jmx/JMXs.java    |    78 +
 .../commons/jcs/jcache/lang/DefaultSubsitutor.java |    32 +
 .../commons/jcs/jcache/lang/Lang3Substitutor.java  |    39 +
 .../apache/commons/jcs/jcache/lang/Subsitutor.java |    53 +
 .../jcs/jcache/proxy/ClassLoaderAwareCache.java    |   493 +
 .../jcs/jcache/proxy/ExceptionWrapperHandler.java  |    77 +
 .../jcs/jcache/serialization/Serializations.java   |    36 +
 .../jcs/jcache/thread/DaemonThreadFactory.java     |    43 +
 .../src/main/resources/META-INF/beans.xml          |    31 +
 .../services/javax.cache.spi.CachingProvider       |    18 +
 .../services/javax.enterprise.inject.spi.Extension |    17 +
 commons-jcs-jcache/src/main/resources/jcache.ccf   |    32 +
 .../org/apache/commons/jcs/jcache/CacheTest.java   |   312 +
 .../commons/jcs/jcache/CachingProviderTest.java    |    44 +
 .../commons/jcs/jcache/ImmediateExpiryTest.java    |    53 +
 .../commons/jcs/jcache/NotSerializableTest.java    |    61 +
 commons-jcs-tck-tests/pom.xml                      |   240 +
 commons-jcs-tck-tests/run-tck.sh                   |    19 +
 .../jcache/EnsureCDIIsTestedWhenTCKsRunTest.java   |    46 +
 .../apache/commons/jcs/jcache/OWBBeanProvider.java |    65 +
 .../src/test/resources/ExcludeList                 |    19 +
 .../services/javax.cache.annotation.BeanProvider   |    18 +
 .../src/test/resources/log4j.properties            |    19 +
 doap_jcs.rdf                                       |    40 +
 init-git-svn.sh                                    |    21 +
 jcache-fast.sh                                     |    23 +
 maven-eclipse-codestyle.xml                        |   262 +
 pom.xml                                            |   695 +
 src/changes/changes.xml                            |   477 +
 src/changes/release-notes.vm                       |   140 +
 src/scripts/cpappend.bat                           |    18 +
 src/scripts/directory.bat                          |    17 +
 src/scripts/jgtest.bat                             |    26 +
 src/scripts/jgtest_send.bat                        |    26 +
 src/scripts/jgtest_send.sh                         |    35 +
 src/scripts/prep.bat                               |    48 +
 src/scripts/remoteCacheStats.bat                   |    28 +
 src/scripts/remoteCacheStats.sh                    |    44 +
 src/scripts/setCURDIR.bat                          |    27 +
 src/scripts/startJetty.bat                         |    25 +
 src/scripts/startRemoteCache.bat                   |    28 +
 src/scripts/startRemoteCache.sh                    |    51 +
 src/scripts/stopRemoteCache.bat                    |    25 +
 src/scripts/tester.bat                             |    27 +
 src/scripts/tester.sh                              |    35 +
 src/scripts/zipcodes.txt                           | 42195 +++++++++++++++++++
 src/site/resources/download_jcs.cgi                |    21 +
 src/site/site.xml                                  |    76 +
 xdocs/BDBJEDiskCache.ccf                           |    84 +
 xdocs/BasicJCSConfiguration.xml                    |   194 +
 xdocs/BlockDiskCache.xml                           |    92 +
 xdocs/CacheEventLogging.xml                        |    61 +
 xdocs/DownloadPage.xml                             |    87 +
 xdocs/ElementAttributes.xml                        |   184 +
 xdocs/ElementEventHandling.xml                     |   199 +
 xdocs/IndexedDiskAuxCache.xml                      |   307 +
 xdocs/IndexedDiskCacheProperties.xml               |   167 +
 xdocs/JCSPlugins.xml                               |    77 +
 xdocs/JCSShortDescription.xml                      |    71 +
 xdocs/JCSandJCACHE.xml                             |   151 +
 xdocs/JCSvsEHCache.xml                             |   507 +
 xdocs/JDBCDiskCache.xml                            |   193 +
 xdocs/JDBCDiskCacheProperties.xml                  |   250 +
 xdocs/LateralJavaGroupsAuxCache.xml                |    58 +
 xdocs/LateralTCPAuxCache.xml                       |   156 +
 xdocs/LateralTCPProperties.xml                     |   148 +
 xdocs/LateralUDPDiscovery.xml                      |    72 +
 xdocs/LocalCacheConfig.xml                         |   170 +
 xdocs/MySQLDiskCacheProperties.xml                 |   195 +
 xdocs/ProjectHistory.xml                           |    42 +
 xdocs/RegionProperties.xml                         |   268 +
 xdocs/RemoteAuxCache.xml                           |   356 +
 xdocs/RemoteCacheProperties.xml                    |   167 +
 xdocs/RemoteHttpCacheProperties.xml                |   116 +
 xdocs/UsingJCSBasicWeb.xml                         |   450 +
 xdocs/download_jcs.xml                             |   138 +
 xdocs/faq.fml                                      |   286 +
 xdocs/getting_started/intro.xml                    |   274 +
 xdocs/index.xml                                    |   180 +
 xdocs/tasks.xml                                    |    52 +
 680 files changed, 150345 insertions(+)

diff --git a/BUILDING.txt b/BUILDING.txt
new file mode 100644
index 0000000..e35f32f
--- /dev/null
+++ b/BUILDING.txt
@@ -0,0 +1,10 @@
+Default build is using:
+
+    mvn clean install
+
+Release build is ising (to get distributions - ie src/bin assemblies - and force TCKs to pass):
+
+    mvn clean install -Prelease
+
+Note: this only passes on Java 7 since TCKs have to run on it.
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..be30f70
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..304bdb4
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!--
+  Checkstyle checks configured for Maven.
+-->
+
+<module name="Checker">
+
+    <property name="localeCountry" value="US" />
+    <property name="localeLanguage" value="en" />
+
+    <!-- Checks that a package.html file exists for each package.     -->
+    <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
+    <module name="PackageHtml"/>
+
+    <!-- Checks whether files end with a new line.                        -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+    <module name="NewlineAtEndOfFile"/>
+
+    <!-- Checks that property files contain the same keys.         -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+    <module name="Translation"/>
+
+    <module name="TreeWalker">
+
+        <property name="cacheFile" value="${checkstyle.cache.file}"/>
+
+        <!-- ************************************************************** -->
+        <!-- Checks that are different from the sun coding conventions ones -->
+        <!-- ************************************************************** -->
+        <module name="Header">
+            <property name="headerFile" value="${maven.checkstyle.header.file}"/>
+            <property name="ignoreLines" value="1,2,3,4,5,6"/>
+        </module>
+        <property name="tabWidth" value="4"/>
+        <module name="LeftCurly">
+          <property name="option" value="nl"/>
+        </module>
+        <module name="RightCurly">
+          <property name="option" value="alone"/>
+        </module>
+        <module name="LineLength">
+          <property name="max" value="132"/>
+        </module>
+        <module name="MethodLength">
+          <property name="max" value="175"/>
+        </module>
+        <!-- No Paren pad check
+        <module name="ParenPad"/>
+        -->
+        <module name="ConstantName">
+          <property name="format" value="log|^[a-zA-Z][a-zA-Z0-9_]*$"/>
+        </module>
+
+        <!-- ************************************************************** -->
+        <!-- Default Sun coding conventions checks                          -->
+        <!-- ************************************************************** -->
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+        <module name="JavadocMethod"/>
+        <module name="JavadocType"/>
+        <module name="JavadocVariable"/>
+
+
+        <!-- Checks for Naming Conventions.                  -->
+        <!-- See http://checkstyle.sf.net/config_naming.html -->
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MethodName"/>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+        <module name="MemberName"/>
+
+        <!-- Checks for imports                              -->
+        <!-- See http://checkstyle.sf.net/config_import.html -->
+        <module name="AvoidStarImport"/>
+        <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+
+
+        <!-- Checks for Size Violations.                    -->
+        <!-- See http://checkstyle.sf.net/config_sizes.html -->
+        <module name="FileLength"/>
+        <module name="ParameterNumber"/>
+
+
+        <!-- Checks for whitespace                               -->
+        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+        <module name="EmptyForIteratorPad"/>
+        <module name="NoWhitespaceAfter"/>
+        <module name="NoWhitespaceBefore"/>
+        <module name="OperatorWrap"/>
+        <module name="TabCharacter"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround"/>
+
+
+        <!-- Modifier Checks                                    -->
+        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+
+
+        <!-- Checks for blocks. You know, those {}'s         -->
+        <!-- See http://checkstyle.sf.net/config_blocks.html -->
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock"/>
+
+        <module name="NeedBraces"/>
+
+
+        <!-- Checks for common coding problems               -->
+        <!-- See http://checkstyle.sf.net/config_coding.html -->
+        <module name="AvoidInlineConditionals"/>
+        <module name="DoubleCheckedLocking"/>    <!-- MY FAVOURITE -->
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="HiddenField"/>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <module name="MagicNumber"/>
+        <module name="MissingSwitchDefault"/>
+        <!--<module name="RedundantThrows"/>-->
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- Checks for class design                         -->
+        <!-- See http://checkstyle.sf.net/config_design.html -->
+        <!--<module name="DesignForExtension"/>-->
+        <module name="FinalClass"/>
+        <module name="HideUtilityClassConstructor"/>
+        <module name="InterfaceIsType"/>
+        <module name="VisibilityModifier"/>
+
+
+        <!-- Miscellaneous other checks.                   -->
+        <!-- See http://checkstyle.sf.net/config_misc.html -->
+        <module name="ArrayTypeStyle"/>
+        <!-- <module name="FinalParameters"/> -->
+        <module name="GenericIllegalRegexp">
+            <property name="format" value="\s+$"/>
+            <property name="message" value="Line has trailing spaces."/>
+        </module>
+        <module name="TodoComment"/>
+        <module name="UpperEll"/>
+
+    </module>
+
+</module>
diff --git a/commons-jcs-core/LICENSE.txt b/commons-jcs-core/LICENSE.txt
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/commons-jcs-core/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
diff --git a/commons-jcs-core/NOTICE b/commons-jcs-core/NOTICE
new file mode 100644
index 0000000..13fc80c
--- /dev/null
+++ b/commons-jcs-core/NOTICE
@@ -0,0 +1,6 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
+
diff --git a/commons-jcs-core/pom.xml b/commons-jcs-core/pom.xml
new file mode 100644
index 0000000..c282721
--- /dev/null
+++ b/commons-jcs-core/pom.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-jcs</artifactId>
+    <version>2.0-beta-1</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>commons-jcs-core</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Apache Commons JCS :: Core</name>
+
+  <properties>
+    <maven.compiler.source>1.6</maven.compiler.source>
+    <maven.compiler.target>1.6</maven.compiler.target>
+  </properties>
+
+  <dependencies>
+
+    <!--  REQUIRED FOR JCS CORE -->
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+
+    <!--  JDBC DISK CACHE -->
+    <dependency>
+      <groupId>commons-dbcp</groupId>
+      <artifactId>commons-dbcp</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <!--  JDBC DISK CACHE -->
+    <dependency>
+      <groupId>commons-pool</groupId>
+      <artifactId>commons-pool</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <!--  For comparative performance tests only -->
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-collections4</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!--  Test dependencies -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- Exclude for now -->
+    <!-- dependency>
+      <groupId>org.jgroups</groupId>
+      <artifactId>jgroups</artifactId>
+      <version>3.4.1.Final</version>
+      <optional>true</optional>
+    </dependency -->
+
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity-tools</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-httpclient</groupId>
+      <artifactId>commons-httpclient</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <testResources>
+      <testResource>
+        <directory>src/test/test-conf</directory>
+        <includes>
+          <include>**/*.properties</include>
+          <include>**/*.ccf</include>
+        </includes>
+      </testResource>
+    </testResources>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>${commons.surefire.version}</version>
+          <configuration>
+            <argLine>-Xmx256m -Djava.security.manager -Djava.security.policy=${basedir}/src/test/conf/cache.policy</argLine>
+            <forkMode>pertest</forkMode>
+            <includes>
+              <include>**/*UnitTest.java</include>
+            </includes>
+            <excludes>
+              <!--
+                  Several of the test cases rely on background tasks that are timing
+                  sensitive.
+              -->
+              <!-- You need to manually run these test cases. -->
+              <exclude>**/BlockDiskElementDescriptorUnitTest.java</exclude>
+              <exclude>**/HSQLDiskCacheConcurrentUnitTest.java</exclude>
+              <exclude>**/HSQLDiskCacheUnitTest.java</exclude>
+              <exclude>**/IndexedDiskCacheOptimizationUnitTest.java</exclude>
+              <exclude>**/TestTCPLateralUnitTest.java</exclude>
+              <exclude>**/UDPDiscoveryUnitTest.java</exclude>
+              <!-- Causes hang in Continuum -->
+              <exclude>**/UDPDiscoverySenderUnitTest.java</exclude>
+              <!-- The ones that fail may be different for you. -->
+              <!-- <exclude>**/JDBCDiskCacheRemovalUnitTest.java</exclude> -->
+              <!-- <exclude>**/JDBCDiskCacheUnitTest.java</exclude> -->
+            </excludes>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.rat</groupId>
+          <artifactId>apache-rat-plugin</artifactId>
+          <configuration>
+            <excludes combine.children="append">
+              <exclude>**/zipcodes.txt</exclude>
+            </excludes>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/JCS.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/JCS.java
new file mode 100644
index 0000000..696847d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/JCS.java
@@ -0,0 +1,212 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.GroupCacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+
+import java.util.Properties;
+
+/**
+ * Simple class for using JCS. To use JCS in your application, you can use the static methods of
+ * this class to get access objects (instances of this class) for your cache regions. One CacheAccess
+ * object should be created for each region you want to access. If you have several regions, then
+ * get instances for each. For best performance the getInstance call should be made in an
+ * initialization method.
+ */
+public abstract class JCS
+{
+    /** cache.ccf alternative. */
+    private static String configFilename = null;
+
+    /** alternative configuration properties */
+    private static Properties configProps = null;
+
+    /** Cache manager use by the various forms of defineRegion and getAccess */
+    private static CompositeCacheManager cacheMgr;
+
+    /**
+     * Define a new cache region with the given name. In the oracle specification, these attributes
+     * are global and not region specific, regional overrides is a value add each region should be
+     * able to house both cache and element attribute sets. It is more efficient to define a cache
+     * in the props file and then strictly use the get access method. Use of the define region
+     * outside of an initialization block should be avoided.
+     * <p>
+     * @param name Name that will identify the region
+     * @return CacheAccess instance for the new region
+     * @throws CacheException
+     */
+    public static <K, V> CacheAccess<K, V> defineRegion( String name )
+        throws CacheException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( name );
+        return new CacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Define a new cache region with the specified name and attributes.
+     * <p>
+     * @param name Name that will identify the region
+     * @param cattr CompositeCacheAttributes for the region
+     * @return CacheAccess instance for the new region
+     * @throws CacheException
+     */
+    public static <K, V> CacheAccess<K, V> defineRegion( String name, ICompositeCacheAttributes cattr )
+        throws CacheException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( name, cattr );
+        return new CacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Define a new cache region with the specified name and attributes and return a CacheAccess to
+     * it.
+     * <p>
+     * @param name Name that will identify the region
+     * @param cattr CompositeCacheAttributes for the region
+     * @param attr Attributes for the region
+     * @return CacheAccess instance for the new region
+     * @throws CacheException
+     */
+    public static <K, V> CacheAccess<K, V> defineRegion( String name, ICompositeCacheAttributes cattr, IElementAttributes attr )
+        throws CacheException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( name, cattr, attr );
+        return new CacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Set the filename that the cache manager will be initialized with. Only matters before the
+     * instance is initialized.
+     * <p>
+     * @param configFilename
+     */
+    public static void setConfigFilename( String configFilename )
+    {
+        JCS.configFilename = configFilename;
+    }
+
+    /**
+     * Set the properties that the cache manager will be initialized with. Only
+     * matters before the instance is initialized.
+     *
+     * @param configProps
+     */
+    public static void setConfigProperties( Properties configProps )
+    {
+        JCS.configProps = configProps;
+    }
+
+    /**
+     * Helper method which checks to make sure the cacheMgr class field is set, and if not requests
+     * an instance from CacheManagerFactory.
+     *
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    private static CompositeCacheManager getCacheManager() throws CacheException
+    {
+        synchronized ( JCS.class )
+        {
+            if ( cacheMgr == null || !cacheMgr.isInitialized())
+            {
+                if ( configProps != null )
+                {
+                    cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+                    cacheMgr.configure( configProps );
+                }
+                else if ( configFilename != null )
+                {
+                    cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+                    cacheMgr.configure( configFilename );
+                }
+                else
+                {
+                    cacheMgr = CompositeCacheManager.getInstance();
+                }
+            }
+
+            return cacheMgr;
+        }
+    }
+
+    /**
+     * Get a CacheAccess which accesses the provided region.
+     * <p>
+     * @param region Region that return CacheAccess will provide access to
+     * @return A CacheAccess which provides access to a given region.
+     * @throws CacheException
+     */
+    public static <K, V> CacheAccess<K, V> getInstance( String region )
+        throws CacheException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( region );
+        return new CacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Get a CacheAccess which accesses the provided region.
+     * <p>
+     * @param region Region that return CacheAccess will provide access to
+     * @param icca CacheAttributes for region
+     * @return A CacheAccess which provides access to a given region.
+     * @throws CacheException
+     */
+    public static <K, V> CacheAccess<K, V> getInstance( String region, ICompositeCacheAttributes icca )
+        throws CacheException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( region, icca );
+        return new CacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Get a GroupCacheAccess which accesses the provided region.
+     * <p>
+     * @param region Region that return GroupCacheAccess will provide access to
+     * @return A GroupCacheAccess which provides access to a given region.
+     * @throws CacheException
+     */
+    public static <K, V> GroupCacheAccess<K, V> getGroupCacheInstance( String region )
+        throws CacheException
+    {
+        CompositeCache<GroupAttrName<K>, V> cache = getCacheManager().getCache( region );
+        return new GroupCacheAccess<K, V>( cache );
+    }
+
+    /**
+     * Get a GroupCacheAccess which accesses the provided region.
+     * <p>
+     * @param region Region that return GroupCacheAccess will provide access to
+     * @param icca CacheAttributes for region
+     * @return A GroupCacheAccess which provides access to a given region.
+     * @throws CacheException
+     */
+    public static <K, V> GroupCacheAccess<K, V> getGroupCacheInstance( String region, ICompositeCacheAttributes icca )
+        throws CacheException
+    {
+        CompositeCache<GroupAttrName<K>, V> cache = getCacheManager().getCache( region, icca );
+        return new GroupCacheAccess<K, V>( cache );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/AbstractCacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/AbstractCacheAccess.java
new file mode 100644
index 0000000..08deda8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/AbstractCacheAccess.java
@@ -0,0 +1,203 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.behavior.ICacheAccessManagement;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+
+import java.io.IOException;
+
+/**
+ * This class provides the common methods for all types of access to the cache.
+ * <p>
+ * An instance of this class is tied to a specific cache region. Static methods are provided to get
+ * such instances.
+ * <p>
+ * Using this class you can retrieve an item, the item's wrapper, and the element's configuration.  You can also put an
+ * item in the cache, remove an item, and clear a region.
+ * <p>
+ * The JCS class is the preferred way to access these methods.
+ */
+public abstract class AbstractCacheAccess<K, V>
+    implements ICacheAccessManagement
+{
+    /**
+     * The cache that a given instance of this class provides access to.
+     * <p>
+     * TODO Should this be the interface?
+     */
+    private final CompositeCache<K, V> cacheControl;
+
+    /**
+     * Constructor for the CacheAccess object.
+     * <p>
+     * @param cacheControl The cache which the created instance accesses
+     */
+    public AbstractCacheAccess( CompositeCache<K, V> cacheControl )
+    {
+        this.cacheControl = cacheControl;
+    }
+
+    /**
+     * Removes all of the elements from a region.
+     * <p>
+     * @throws CacheException
+     */
+    @Override
+    public void clear()
+        throws CacheException
+    {
+        try
+        {
+            this.getCacheControl().removeAll();
+        }
+        catch ( IOException e )
+        {
+            throw new CacheException( e );
+        }
+    }
+
+    /**
+     * This method is does not reset the attributes for items already in the cache. It could
+     * potentially do this for items in memory, and maybe on disk (which would be slow) but not
+     * remote items. Rather than have unpredictable behavior, this method just sets the default
+     * attributes. Items subsequently put into the cache will use these defaults if they do not
+     * specify specific attributes.
+     * <p>
+     * @param attr the default attributes.
+     * @throws CacheException if something goes wrong.
+     */
+    @Override
+    public void setDefaultElementAttributes( IElementAttributes attr )
+        throws CacheException
+    {
+        this.getCacheControl().setElementAttributes( attr );
+    }
+
+    /**
+     * Retrieves A COPY OF the default element attributes used by this region. This does not provide
+     * a reference to the element attributes.
+     * <p>
+     * Each time an element is added to the cache without element attributes, the default element
+     * attributes are cloned.
+     * <p>
+     * @return the default element attributes used by this region.
+     * @throws CacheException
+     */
+    @Override
+    public IElementAttributes getDefaultElementAttributes()
+        throws CacheException
+    {
+        return this.getCacheControl().getElementAttributes();
+    }
+
+    /**
+     * This returns the ICacheStats object with information on this region and its auxiliaries.
+     * <p>
+     * This data can be formatted as needed.
+     * <p>
+     * @return ICacheStats
+     */
+    @Override
+    public ICacheStats getStatistics()
+    {
+        return this.getCacheControl().getStatistics();
+    }
+
+    /**
+     * @return A String version of the stats.
+     */
+    @Override
+    public String getStats()
+    {
+        return this.getCacheControl().getStats();
+    }
+
+    /**
+     * Dispose this region. Flushes objects to and closes auxiliary caches. This is a shutdown
+     * command!
+     * <p>
+     * To simply remove all elements from the region use clear().
+     */
+    @Override
+    public void dispose()
+    {
+        this.getCacheControl().dispose();
+    }
+
+    /**
+     * Gets the ICompositeCacheAttributes of the cache region.
+     * <p>
+     * @return ICompositeCacheAttributes, the controllers config info, defined in the top section of
+     *         a region definition.
+     */
+    @Override
+    public ICompositeCacheAttributes getCacheAttributes()
+    {
+        return this.getCacheControl().getCacheAttributes();
+    }
+
+    /**
+     * Sets the ICompositeCacheAttributes of the cache region.
+     * <p>
+     * @param cattr The new ICompositeCacheAttribute value
+     */
+    @Override
+    public void setCacheAttributes( ICompositeCacheAttributes cattr )
+    {
+        this.getCacheControl().setCacheAttributes( cattr );
+    }
+
+    /**
+     * This instructs the memory cache to remove the <i>numberToFree</i> according to its eviction
+     * policy. For example, the LRUMemoryCache will remove the <i>numberToFree</i> least recently
+     * used items. These will be spooled to disk if a disk auxiliary is available.
+     * <p>
+     * @param numberToFree
+     * @return the number that were removed. if you ask to free 5, but there are only 3, you will
+     *         get 3.
+     * @throws CacheException
+     */
+    @Override
+    public int freeMemoryElements( int numberToFree )
+        throws CacheException
+    {
+        int numFreed = -1;
+        try
+        {
+            numFreed = this.getCacheControl().getMemoryCache().freeElements( numberToFree );
+        }
+        catch ( IOException ioe )
+        {
+            String message = "Failure freeing memory elements.";
+            throw new CacheException( message, ioe );
+        }
+        return numFreed;
+    }
+
+    public CompositeCache<K, V> getCacheControl() {
+        return cacheControl;
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/CacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/CacheAccess.java
new file mode 100644
index 0000000..e34c6d1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/CacheAccess.java
@@ -0,0 +1,309 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.InvalidArgumentException;
+import org.apache.commons.jcs.access.exception.InvalidHandleException;
+import org.apache.commons.jcs.access.exception.ObjectExistsException;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class provides an interface for all types of access to the cache.
+ * <p>
+ * An instance of this class is tied to a specific cache region. Static methods are provided to get
+ * such instances.
+ * <p>
+ * Using this class you can retrieve an item, the item's wrapper, and the element's configuration.  You can also put an
+ * item in the cache, remove an item, and clear a region.
+ * <p>
+ * The JCS class is the preferred way to access these methods.
+ */
+public class CacheAccess<K, V>
+    extends AbstractCacheAccess<K, V>
+    implements ICacheAccess<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( CacheAccess.class );
+
+    /**
+     * Constructor for the CacheAccess object.
+     * <p>
+     * @param cacheControl The cache which the created instance accesses
+     */
+    public CacheAccess( CompositeCache<K, V> cacheControl )
+    {
+        super(cacheControl);
+    }
+
+    /**
+     * Retrieve an object from the cache region this instance provides access to.
+     * <p>
+     * @param name Key the object is stored as
+     * @return The object if found or null
+     */
+    @Override
+    public V get( K name )
+    {
+        ICacheElement<K, V> element = this.getCacheControl().get( name );
+
+        return ( element != null ) ? element.getVal() : null;
+    }
+
+    /**
+     * Retrieve matching objects from the cache region this instance provides access to.
+     * <p>
+     * @param pattern - a key pattern for the objects stored
+     * @return A map of key to values.  These are stripped from the wrapper.
+     */
+    @Override
+    public Map<K, V> getMatching( String pattern )
+    {
+        HashMap<K, V> unwrappedResults = new HashMap<K, V>();
+
+        Map<K, ICacheElement<K, V>> wrappedResults = this.getCacheControl().getMatching( pattern );
+        if ( wrappedResults != null )
+        {
+            for (Map.Entry<K, ICacheElement<K, V>> entry : wrappedResults.entrySet())
+            {
+                ICacheElement<K, V> element = entry.getValue();
+                if ( element != null )
+                {
+                    unwrappedResults.put( entry.getKey(), element.getVal() );
+                }
+            }
+        }
+        return unwrappedResults;
+    }
+
+    /**
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param name Key the Serializable is stored as
+     * @return The ICacheElement<K, V> if the object is found or null
+     */
+    @Override
+    public ICacheElement<K, V> getCacheElement( K name )
+    {
+        return this.getCacheControl().get( name );
+    }
+
+    /**
+     * Get multiple elements from the cache based on a set of cache keys.
+     * <p>
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param names set of Serializable cache keys
+     * @return a map of K key to ICacheElement<K, V> element, or empty map if none of the keys are present
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getCacheElements( Set<K> names )
+    {
+        return this.getCacheControl().getMultiple( names );
+    }
+
+    /**
+     * Get multiple elements from the cache based on a set of cache keys.
+     * <p>
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param pattern key search pattern
+     * @return a map of K key to ICacheElement<K, V> element, or empty map if no keys match the pattern
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatchingCacheElements( String pattern )
+    {
+        return this.getCacheControl().getMatching( pattern );
+    }
+
+    /**
+     * Place a new object in the cache, associated with key name. If there is currently an object
+     * associated with name in the region an ObjectExistsException is thrown. Names are scoped to a
+     * region so they must be unique within the region they are placed.
+     * <p>
+     * @param key Key object will be stored with
+     * @param value Object to store
+     * @throws CacheException and ObjectExistsException is thrown if the item is already in the
+     *                cache.
+     */
+    @Override
+    public void putSafe( K key, V value )
+    {
+        if ( this.getCacheControl().get( key ) != null )
+        {
+            throw new ObjectExistsException( "putSafe failed.  Object exists in the cache for key [" + key
+                + "].  Remove first or use a non-safe put to override the value." );
+        }
+        put( key, value );
+    }
+
+    /**
+     * Place a new object in the cache, associated with key name. If there is currently an object
+     * associated with name in the region it is replaced. Names are scoped to a region so they must
+     * be unique within the region they are placed.
+     * @param name Key object will be stored with
+     * @param obj Object to store
+     */
+    @Override
+    public void put( K name, V obj )
+    {
+        // Call put with a copy of the contained caches default attributes.
+        // the attributes are copied by the cacheControl
+        put( name, obj, this.getCacheControl().getElementAttributes() );
+    }
+
+    /**
+     * Constructs a cache element with these attributes, and puts it into the cache.
+     * <p>
+     * If the key or the value is null, and InvalidArgumentException is thrown.
+     * <p>
+     * @see org.apache.commons.jcs.access.behavior.ICacheAccess#put(java.io.Serializable, java.io.Serializable,
+     *      org.apache.commons.jcs.engine.behavior.IElementAttributes)
+     */
+    @Override
+    public void put( K key, V val, IElementAttributes attr )
+    {
+        if ( key == null )
+        {
+            throw new InvalidArgumentException( "Key must not be null" );
+        }
+
+        if ( val == null )
+        {
+            throw new InvalidArgumentException( "Value must not be null" );
+        }
+
+        // Create the element and update. This may throw an IOException which
+        // should be wrapped by cache access.
+        try
+        {
+            CacheElement<K, V> ce = new CacheElement<K, V>( this.getCacheControl().getCacheName(), key,
+                                                val );
+
+            ce.setElementAttributes( attr );
+
+            this.getCacheControl().update( ce );
+        }
+        catch ( IOException e )
+        {
+            throw new CacheException( e );
+        }
+    }
+
+    /**
+     * Removes a single item by name.
+     * <p>
+     * @param name the name of the item to remove.
+     */
+    @Override
+    public void remove( K name )
+    {
+        this.getCacheControl().remove( name );
+    }
+
+    /**
+     * Reset attributes for a particular element in the cache. NOTE: this method is currently not
+     * implemented.
+     * <p>
+     * @param name Key of object to reset attributes for
+     * @param attr New attributes for the object
+     * @throws InvalidHandleException if the item does not exist.
+     */
+    @Override
+    public void resetElementAttributes( K name, IElementAttributes attr )
+    {
+        ICacheElement<K, V> element = this.getCacheControl().get( name );
+
+        if ( element == null )
+        {
+            throw new InvalidHandleException( "Object for name [" + name + "] is not in the cache" );
+        }
+
+        // Although it will work currently, don't assume pass by reference here,
+        // i.e. don't do this:
+        // element.setElementAttributes( attr );
+        // Another reason to call put is to force the changes to be distributed.
+
+        put( element.getKey(), element.getVal(), attr );
+    }
+
+    /**
+     * GetElementAttributes will return an attribute object describing the current attributes
+     * associated with the object name. The name object must override the Object.equals and
+     * Object.hashCode methods.
+     * <p>
+     * @param name Key of object to get attributes for
+     * @return Attributes for the object, null if object not in cache
+     */
+    @Override
+    public IElementAttributes getElementAttributes( K name )
+    {
+        IElementAttributes attr = null;
+
+        try
+        {
+            attr = this.getCacheControl().getElementAttributes( name );
+        }
+        catch ( IOException ioe )
+        {
+            log.error( "Failure getting element attributes", ioe );
+        }
+
+        return attr;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/GroupCacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/GroupCacheAccess.java
new file mode 100644
index 0000000..958abba
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/GroupCacheAccess.java
@@ -0,0 +1,211 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.behavior.IGroupCacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.InvalidArgumentException;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Access for groups.
+ */
+public class GroupCacheAccess<K, V>
+    extends AbstractCacheAccess<GroupAttrName<K>, V>
+    implements IGroupCacheAccess<K, V>
+{
+    /**
+     * Constructor for the GroupCacheAccess object
+     * <p>
+     * @param cacheControl
+     */
+    public GroupCacheAccess( CompositeCache<GroupAttrName<K>, V> cacheControl )
+    {
+        super(cacheControl);
+    }
+
+    /**
+     * Gets an item out of the cache that is in a specified group.
+     * <p>
+     * @param name
+     *            The key name.
+     * @param group
+     *            The group name.
+     * @return The cached value, null if not found.
+     */
+    @Override
+    public V getFromGroup( K name, String group )
+    {
+        ICacheElement<GroupAttrName<K>, V> element = this.getCacheControl().get( getGroupAttrName( group, name ) );
+        return ( element != null ) ? element.getVal() : null;
+    }
+
+    /**
+     * Internal method used for group functionality.
+     * <p>
+     * @param group
+     * @param name
+     * @return GroupAttrName
+     */
+    private GroupAttrName<K> getGroupAttrName( String group, K name )
+    {
+        GroupId gid = new GroupId( this.getCacheControl().getCacheName(), group );
+        return new GroupAttrName<K>( gid, name );
+    }
+
+    /**
+     * Allows the user to put an object into a group within a particular cache
+     * region. This method sets the object's attributes to the default for the
+     * region.
+     * <p>
+     * @param name
+     *            The key name.
+     * @param groupName
+     *            The group name.
+     * @param value
+     *            The object to cache
+     * @throws CacheException
+     */
+    @Override
+    public void putInGroup( K name, String groupName, V value )
+        throws CacheException
+    {
+        putInGroup( name, groupName, value, null );
+    }
+
+    /**
+     * Allows the user to put an object into a group within a particular cache
+     * region. This method allows the object's attributes to be individually
+     * specified.
+     * <p>
+     * @param name
+     *            The key name.
+     * @param groupName
+     *            The group name.
+     * @param value
+     *            The object to cache
+     * @param attr
+     *            The objects attributes.
+     * @throws CacheException
+     */
+    @Override
+    public void putInGroup( K name, String groupName, V value, IElementAttributes attr )
+        throws CacheException
+    {
+        if ( name == null )
+        {
+            throw new InvalidArgumentException( "Key must not be null" );
+        }
+
+        if ( value == null )
+        {
+            throw new InvalidArgumentException( "Value must not be null" );
+        }
+
+        // Create the element and update. This may throw an IOException which
+        // should be wrapped by cache access.
+        try
+        {
+            GroupAttrName<K> key = getGroupAttrName( groupName, name );
+            CacheElement<GroupAttrName<K>, V> ce =
+                new CacheElement<GroupAttrName<K>, V>( this.getCacheControl().getCacheName(), key, value );
+
+            IElementAttributes attributes = (attr == null) ? this.getCacheControl().getElementAttributes() : attr;
+            ce.setElementAttributes( attributes );
+
+            this.getCacheControl().update( ce );
+        }
+        catch ( IOException e )
+        {
+            throw new CacheException( e );
+        }
+
+    }
+
+    /**
+     * @param name
+     * @param group
+     */
+    @Override
+    public void removeFromGroup( K name, String group )
+    {
+        GroupAttrName<K> key = getGroupAttrName( group, name );
+        this.getCacheControl().remove( key );
+    }
+
+    /**
+     * Gets the set of keys of objects currently in the group.
+     * <p>
+     * @param group
+     * @return A Set of keys.
+     */
+    @Override
+    public Set<K> getGroupKeys( String group )
+    {
+        Set<K> groupKeys = new HashSet<K>();
+        GroupId groupId = new GroupId( this.getCacheControl().getCacheName(), group );
+
+        for (GroupAttrName<K> gan : this.getCacheControl().getKeySet())
+        {
+            if (gan.groupId.equals( groupId ))
+            {
+                groupKeys.add( gan.attrName );
+            }
+        }
+
+        return groupKeys;
+    }
+
+    /**
+     * Gets the set of group names in the cache
+     * <p>
+     * @return A Set of group names.
+     */
+    public Set<String> getGroupNames()
+    {
+        HashSet<String> names = new HashSet<String>();
+        for (GroupAttrName<K> gan : this.getCacheControl().getKeySet())
+        {
+            names.add(gan.groupId.groupName);
+        }
+        return names;
+    }
+
+    /**
+     * Invalidates a group: remove all the group members
+     * <p>
+     * @param group
+     *            The name of the group to invalidate
+     */
+    @Override
+    public void invalidateGroup( String group )
+    {
+        this.getCacheControl().remove(getGroupAttrName(group, null));
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/PartitionedCacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/PartitionedCacheAccess.java
new file mode 100644
index 0000000..873f8d4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/PartitionedCacheAccess.java
@@ -0,0 +1,846 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.ConfigurationException;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+import org.apache.commons.jcs.utils.props.AbstractPropertyContainer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * TODO:  Add new methods that will allow you to provide a partition indicator for all major calls.  Add an interface as well.
+ * <p>
+ * This handles dividing puts and gets.
+ * <p>
+ * There are two required properties.
+ * <p>
+ * <ol>
+ * <li>.numberOfPartitions</li>
+ * <li>.partitionRegionNamePrefix</li>
+ * </ol>
+ * System properties will override values in the properties file.
+ * <p>
+ * We use a JCS region name for each partition that looks like this: partitionRegionNamePrefix + "_"
+ * + partitionNumber. The number is 0 indexed based.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class PartitionedCacheAccess<K extends Serializable, V extends Serializable>
+    extends AbstractPropertyContainer
+    implements ICacheAccess<K, V>
+{
+    /** the logger. */
+    private static final Log log = LogFactory.getLog( PartitionedCacheAccess.class );
+
+    /** The number of partitions. */
+    private int numberOfPartitions = 1;
+
+    /**
+     * We use a JCS region name for each partition that looks like this: partitionRegionNamePrefix +
+     * "_" + partitionNumber
+     */
+    private String partitionRegionNamePrefix;
+
+    /** An array of partitions built during initialization. */
+    private ICacheAccess<K, V>[] partitions;
+
+    /** Is the class initialized. */
+    private boolean initialized = false;
+
+    /** Sets default properties heading and group. */
+    public PartitionedCacheAccess()
+    {
+        setPropertiesHeading( "PartitionedCacheAccess" );
+        setPropertiesGroup( "cache" );
+    }
+
+    /**
+     * Puts the value into the appropriate cache partition.
+     * <p>
+     * @param key key
+     * @param object object
+     * @throws CacheException on configuration problem
+     */
+    @Override
+    public void put( K key, V object )
+        throws CacheException
+    {
+        if ( key == null || object == null )
+        {
+            log.warn( "Bad input key [" + key + "].  Cannot put null into the cache." );
+            return;
+        }
+
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+        try
+        {
+            partitions[partition].put( key, object );
+        }
+        catch ( CacheException e )
+        {
+            log.error( "Problem putting value for key [" + key + "] in cache [" + partitions[partition] + "]" );
+            throw e;
+        }
+    }
+
+    /**
+     * Puts in cache if an item does not exist with the name in that region.
+     * <p>
+     * @param key
+     * @param object
+     * @throws CacheException
+     */
+    @Override
+    public void putSafe( K key, V object )
+        throws CacheException
+    {
+        if ( key == null || object == null )
+        {
+            log.warn( "Bad input key [" + key + "].  Cannot putSafe null into the cache." );
+        }
+
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+        partitions[partition].putSafe( key, object );
+    }
+
+    /**
+     * Puts the value into the appropriate cache partition.
+     * <p>
+     * @param key key
+     * @param object object
+     * @param attr
+     * @throws CacheException on configuration problem
+     */
+    @Override
+    public void put( K key, V object, IElementAttributes attr )
+        throws CacheException
+    {
+        if ( key == null || object == null )
+        {
+            log.warn( "Bad input key [" + key + "].  Cannot put null into the cache." );
+            return;
+        }
+
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+
+        try
+        {
+            partitions[partition].put( key, object, attr );
+        }
+        catch ( CacheException e )
+        {
+            log.error( "Problem putting value for key [" + key + "] in cache [" + partitions[partition] + "]" );
+            throw e;
+        }
+    }
+
+    /**
+     * Gets the object for the key from the desired partition.
+     * <p>
+     * @param key key
+     * @return result, null if not found.
+     */
+    @Override
+    public V get( K key )
+    {
+        if ( key == null )
+        {
+            log.warn( "Input key is null." );
+            return null;
+        }
+
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+
+        return partitions[partition].get( key );
+    }
+
+    /**
+     * Gets the ICacheElement<K, V> (the wrapped object) for the key from the desired partition.
+     * <p>
+     * @param key key
+     * @return result, null if not found.
+     */
+    @Override
+    public ICacheElement<K, V> getCacheElement( K key )
+    {
+        if ( key == null )
+        {
+            log.warn( "Input key is null." );
+            return null;
+        }
+
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+
+        return partitions[partition].getCacheElement( key );
+    }
+
+    /**
+     * This is a getMultiple. We try to group the keys so that we make as few calls as needed.
+     * <p>
+     * @param names
+     * @return Map of keys to ICacheElement
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getCacheElements( Set<K> names )
+    {
+        if ( names == null )
+        {
+            log.warn( "Bad input names cannot be null." );
+            return Collections.emptyMap();
+        }
+
+        if (!ensureInit())
+        {
+            return Collections.emptyMap();
+        }
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        Set<K>[] dividedNames = new Set[this.getNumberOfPartitions()];
+
+        for (K key : names)
+        {
+            int partition = getPartitionNumberForKey( key );
+            if ( dividedNames[partition] == null )
+            {
+                dividedNames[partition] = new HashSet<K>();
+            }
+            dividedNames[partition].add( key );
+        }
+
+        Map<K, ICacheElement<K, V>> result = new HashMap<K, ICacheElement<K, V>>();
+        for ( int i = 0; i < partitions.length; i++ )
+        {
+            if ( dividedNames[i] != null && !dividedNames[i].isEmpty() )
+            {
+                result.putAll( partitions[i].getCacheElements( dividedNames[i] ) );
+            }
+        }
+        return result;
+    }
+
+    /**
+     * This is tricky. Do we need to get from all the partitions?
+     * <p>
+     * If this interface took an object, we could use the hashcode to determine the partition. Then
+     * we could use the toString for the pattern.
+     * <p>
+     * @param pattern
+     * @return HashMap key to value
+     */
+    @Override
+    public Map<K, V> getMatching( String pattern )
+    {
+        if ( pattern == null )
+        {
+            log.warn( "Input pattern is null." );
+            return null;
+        }
+
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        Map<K, V> result = new HashMap<K, V>();
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            result.putAll( partition.getMatching( pattern ) );
+        }
+
+        return result;
+    }
+
+    /**
+     * This is tricky. Do we need to get from all the partitions?
+     * <p>
+     * @param pattern
+     * @return HashMap key to ICacheElement
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatchingCacheElements( String pattern )
+    {
+        if ( pattern == null )
+        {
+            log.warn( "Input pattern is null." );
+            return null;
+        }
+
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        Map<K, ICacheElement<K, V>> result = new HashMap<K, ICacheElement<K, V>>();
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            result.putAll( partition.getMatchingCacheElements( pattern ) );
+        }
+        return result;
+    }
+
+    /**
+     * Removes the item from the appropriate partition.
+     * <p>
+     * @param key
+     * @throws CacheException
+     */
+    @Override
+    public void remove( K key )
+        throws CacheException
+    {
+        if ( key == null )
+        {
+            log.warn( "Input key is null. Cannot remove null from the cache." );
+            return;
+        }
+
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+        try
+        {
+            partitions[partition].remove( key );
+        }
+        catch ( CacheException e )
+        {
+            log.error( "Problem removing value for key [" + key + "] in cache [" + partitions[partition] + "]" );
+            throw e;
+        }
+    }
+
+    /**
+     * Calls free on each partition.
+     * <p>
+     * @param numberToFree
+     * @return number removed
+     * @throws CacheException
+     */
+    @Override
+    public int freeMemoryElements( int numberToFree )
+        throws CacheException
+    {
+        if (!ensureInit())
+        {
+            return 0;
+        }
+
+        int count = 0;
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            count += partition.freeMemoryElements( numberToFree );
+        }
+        return count;
+    }
+
+    /**
+     * @return ICompositeCacheAttributes from the first partition.
+     */
+    @Override
+    public ICompositeCacheAttributes getCacheAttributes()
+    {
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        if ( partitions.length == 0 )
+        {
+            return null;
+        }
+
+        return partitions[0].getCacheAttributes();
+    }
+
+    /**
+     * @return IElementAttributes from the first partition.
+     * @throws CacheException
+     */
+    @Override
+    public IElementAttributes getDefaultElementAttributes()
+        throws CacheException
+    {
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        if ( partitions.length == 0 )
+        {
+            return null;
+        }
+
+        return partitions[0].getDefaultElementAttributes();
+    }
+
+    /**
+     * This is no more efficient than simply getting the cache element.
+     * <p>
+     * @param key
+     * @return IElementAttributes
+     * @throws CacheException
+     */
+    @Override
+    public IElementAttributes getElementAttributes( K key )
+        throws CacheException
+    {
+        if ( key == null )
+        {
+            log.warn( "Input key is null. Cannot getElementAttributes for null from the cache." );
+            return null;
+        }
+
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+
+        return partitions[partition].getElementAttributes( key );
+    }
+
+    /**
+     * Resets the attributes for this item. This has the same effect as an update, in most cases.
+     * None of the auxiliaries are optimized to do this more efficiently than a simply update.
+     * <p>
+     * @param key
+     * @param attributes
+     * @throws CacheException
+     */
+    @Override
+    public void resetElementAttributes( K key, IElementAttributes attributes )
+        throws CacheException
+    {
+        if ( key == null )
+        {
+            log.warn( "Input key is null. Cannot resetElementAttributes for null." );
+            return;
+        }
+
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        int partition = getPartitionNumberForKey( key );
+
+        partitions[partition].resetElementAttributes( key, attributes );
+    }
+
+    /**
+     * Sets the attributes on all the partitions.
+     * <p>
+     * @param cattr
+     */
+    @Override
+    public void setCacheAttributes( ICompositeCacheAttributes cattr )
+    {
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            partition.setCacheAttributes( cattr );
+        }
+    }
+
+    /**
+     * Removes all of the elements from a region.
+     * <p>
+     * @throws CacheException
+     */
+    @Override
+    public void clear()
+        throws CacheException
+    {
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            partition.clear();
+        }
+    }
+
+    /**
+     * This method is does not reset the attributes for items already in the cache. It could
+     * potentially do this for items in memory, and maybe on disk (which would be slow) but not
+     * remote items. Rather than have unpredictable behavior, this method just sets the default
+     * attributes. Items subsequently put into the cache will use these defaults if they do not
+     * specify specific attributes.
+     * <p>
+     * @param attr the default attributes.
+     * @throws CacheException if something goes wrong.
+     */
+    @Override
+    public void setDefaultElementAttributes( IElementAttributes attr )
+        throws CacheException
+    {
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            partition.setDefaultElementAttributes(attr);
+        }
+    }
+
+    /**
+     * This returns the ICacheStats object with information on this region and its auxiliaries.
+     * <p>
+     * This data can be formatted as needed.
+     * <p>
+     * @return ICacheStats
+     */
+    @Override
+    public ICacheStats getStatistics()
+    {
+        if (!ensureInit())
+        {
+            return null;
+        }
+
+        if ( partitions.length == 0 )
+        {
+            return null;
+        }
+
+        return partitions[0].getStatistics();
+    }
+
+    /**
+     * @return A String version of the stats.
+     */
+    @Override
+    public String getStats()
+    {
+        if (!ensureInit())
+        {
+            return "";
+        }
+
+        StringBuilder stats = new StringBuilder();
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            stats.append(partition.getStats());
+            stats.append("\n");
+        }
+
+        return stats.toString();
+    }
+
+    /**
+     * Dispose this region. Flushes objects to and closes auxiliary caches. This is a shutdown
+     * command!
+     * <p>
+     * To simply remove all elements from the region use clear().
+     */
+    @Override
+    public synchronized void dispose()
+    {
+        if (!ensureInit())
+        {
+            return;
+        }
+
+        for (ICacheAccess<K, V> partition : partitions)
+        {
+            partition.dispose();
+        }
+
+        initialized = false;
+    }
+
+    /**
+     * This expects a numeric key. If the key cannot be converted into a number, we will return 0.
+     * TODO we could md5 it or get the hashcode.
+     * <p>
+     * We determine the partition by taking the mod of the number of partitions.
+     * <p>
+     * @param key key
+     * @return the partition number.
+     */
+    protected int getPartitionNumberForKey( K key )
+    {
+        if ( key == null )
+        {
+            return 0;
+        }
+
+        long keyNum = getNumericValueForKey( key );
+
+        int partition = (int) ( keyNum % getNumberOfPartitions() );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Using partition [" + partition + "] for key [" + key + "]" );
+        }
+
+        return partition;
+    }
+
+    /**
+     * This can be overridden for special purposes.
+     * <p>
+     * @param key key
+     * @return long
+     */
+    public long getNumericValueForKey( K key )
+    {
+        String keyString = key.toString();
+        long keyNum = -1;
+        try
+        {
+            keyNum = Long.parseLong( keyString );
+        }
+        catch ( NumberFormatException e )
+        {
+            // THIS IS UGLY, but I can't think of a better failsafe right now.
+            keyNum = key.hashCode();
+            log.warn( "Couldn't convert [" + key + "] into a number.  Will use hashcode [" + keyNum + "]" );
+        }
+        return keyNum;
+    }
+
+    /**
+     * Initialize if we haven't already.
+     * <p>
+     * @throws ConfigurationException on configuration problem
+     */
+    protected synchronized boolean ensureInit()
+    {
+        if ( !initialized )
+        {
+            try
+            {
+                initialize();
+            }
+            catch ( ConfigurationException e )
+            {
+                log.error( "Couldn't configure partioned access.", e );
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Use the partition prefix and the number of partitions to get JCS regions.
+     * <p>
+     * @throws ConfigurationException on configuration problem
+     */
+    protected synchronized void initialize()
+        throws ConfigurationException
+    {
+        ensureProperties();
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        ICacheAccess<K, V>[] tempPartitions = new ICacheAccess[this.getNumberOfPartitions()];
+        for ( int i = 0; i < this.getNumberOfPartitions(); i++ )
+        {
+            String regionName = this.getPartitionRegionNamePrefix() + "_" + i;
+            try
+            {
+                tempPartitions[i] = JCS.getInstance( regionName );
+            }
+            catch ( CacheException e )
+            {
+                log.error( "Problem getting cache for region [" + regionName + "]" );
+            }
+        }
+
+        partitions = tempPartitions;
+        initialized = true;
+    }
+
+    /**
+     * Loads in the needed configuration settings. System properties are checked first. A system
+     * property will override local property value.
+     * <p>
+     * Loads the following JCS Cache specific properties:
+     * <ul>
+     * <li>heading.numberOfPartitions</li>
+     * <li>heading.partitionRegionNamePrefix</li>
+     * </ul>
+     * @throws ConfigurationException on configuration problem
+     */
+    @Override
+    protected void handleProperties()
+        throws ConfigurationException
+    {
+        // Number of Partitions.
+        String numberOfPartitionsPropertyName = this.getPropertiesHeading() + ".numberOfPartitions";
+        String numberOfPartitionsPropertyValue = getPropertyForName( numberOfPartitionsPropertyName, true );
+        try
+        {
+            this.setNumberOfPartitions( Integer.parseInt( numberOfPartitionsPropertyValue ) );
+        }
+        catch ( NumberFormatException e )
+        {
+            String message = "Could not convert [" + numberOfPartitionsPropertyValue + "] into a number for ["
+                + numberOfPartitionsPropertyName + "]";
+            log.error( message );
+            throw new ConfigurationException( message );
+        }
+
+        // Partition Name Prefix.
+        String prefixPropertyName = this.getPropertiesHeading() + ".partitionRegionNamePrefix";
+        String prefix = getPropertyForName( prefixPropertyName, true );
+        this.setPartitionRegionNamePrefix( prefix );
+    }
+
+    /**
+     * Checks the system properties before the properties.
+     * <p>
+     * @param propertyName name
+     * @param required is it required?
+     * @return the property value if one is found
+     * @throws ConfigurationException thrown if it is required and not found.
+     */
+    protected String getPropertyForName( String propertyName, boolean required )
+        throws ConfigurationException
+    {
+        String propertyValue = null;
+        propertyValue = System.getProperty( propertyName );
+        if ( propertyValue != null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Found system property override: Name [" + propertyName + "] Value [" + propertyValue + "]" );
+            }
+        }
+        else
+        {
+            propertyValue = this.getProperties().getProperty( propertyName );
+            if ( required && propertyValue == null )
+            {
+                String message = "Could not find required property [" + propertyName + "] in propertiesGroup ["
+                    + this.getPropertiesGroup() + "]";
+                log.error( message );
+                throw new ConfigurationException( message );
+            }
+            else
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Name [" + propertyName + "] Value [" + propertyValue + "]" );
+                }
+            }
+        }
+        return propertyValue;
+    }
+
+    /**
+     * @param numberOfPartitions The numberOfPartitions to set.
+     */
+    protected void setNumberOfPartitions( int numberOfPartitions )
+    {
+        this.numberOfPartitions = numberOfPartitions;
+    }
+
+    /**
+     * @return Returns the numberOfPartitions.
+     */
+    protected int getNumberOfPartitions()
+    {
+        return numberOfPartitions;
+    }
+
+    /**
+     * @param partitionRegionNamePrefix The partitionRegionNamePrefix to set.
+     */
+    protected void setPartitionRegionNamePrefix( String partitionRegionNamePrefix )
+    {
+        this.partitionRegionNamePrefix = partitionRegionNamePrefix;
+    }
+
+    /**
+     * @return Returns the partitionRegionNamePrefix.
+     */
+    protected String getPartitionRegionNamePrefix()
+    {
+        return partitionRegionNamePrefix;
+    }
+
+    /**
+     * @param partitions The partitions to set.
+     */
+    protected void setPartitions( ICacheAccess<K, V>[] partitions )
+    {
+        this.partitions = partitions;
+    }
+
+    /**
+     * @return Returns the partitions.
+     */
+    protected ICacheAccess<K, V>[] getPartitions()
+    {
+        return partitions;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccess.java
new file mode 100644
index 0000000..6f8ac8d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccess.java
@@ -0,0 +1,167 @@
+package org.apache.commons.jcs.access.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ICacheAccess defines the behavior for client access.
+ */
+public interface ICacheAccess<K, V>
+    extends ICacheAccessManagement
+{
+    /**
+     * Basic get method.
+     * <p>
+     * @param name
+     * @return Object or null if not found.
+     */
+    V get( K name );
+
+    /**
+     * Retrieve matching objects from the cache region this instance provides access to.
+     * <p>
+     * @param pattern - a key pattern for the objects stored
+     * @return A map of key to values. These are stripped from the wrapper.
+     */
+    Map<K, V> getMatching( String pattern );
+
+    /**
+     * Puts in cache if an item does not exist with the name in that region.
+     * <p>
+     * @param name
+     * @param obj
+     * @throws CacheException
+     */
+    void putSafe( K name, V obj )
+        throws CacheException;
+
+    /**
+     * Puts and/or overrides an element with the name in that region.
+     * <p>
+     * @param name
+     * @param obj
+     * @throws CacheException
+     */
+    void put( K name, V obj )
+        throws CacheException;
+
+    /**
+     * Description of the Method
+     * <p>
+     * @param name
+     * @param obj
+     * @param attr
+     * @throws CacheException
+     */
+    void put( K name, V obj, IElementAttributes attr )
+        throws CacheException;
+
+    /**
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param name Key the object is stored as
+     * @return The ICacheElement<K, V> if the object is found or null
+     */
+    ICacheElement<K, V> getCacheElement( K name );
+
+    /**
+     * Get multiple elements from the cache based on a set of cache keys.
+     * <p>
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param names set of Object cache keys
+     * @return a map of Object key to ICacheElement<K, V> element, or empty map if none of the keys are
+     *         present
+     */
+    Map<K, ICacheElement<K, V>> getCacheElements( Set<K> names );
+
+    /**
+     * Get multiple elements from the cache based on a set of cache keys.
+     * <p>
+     * This method returns the ICacheElement<K, V> wrapper which provides access to element info and other
+     * attributes.
+     * <p>
+     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
+     * defensive copy is made.
+     * <p>
+     * This method is most useful if you want to determine things such as the how long the element
+     * has been in the cache.
+     * <p>
+     * The last access time in the ElementAttributes should be current.
+     * <p>
+     * @param pattern key search pattern
+     * @return a map of Object key to ICacheElement<K, V> element, or empty map if no keys match the
+     *         pattern
+     */
+    Map<K, ICacheElement<K, V>> getMatchingCacheElements( String pattern );
+
+    /**
+     * Remove an object for this key if one exists, else do nothing.
+     * <p>
+     * @param name
+     * @throws CacheException
+     */
+    void remove( K name )
+        throws CacheException;
+
+    /**
+     * Reset the attributes on the object matching this key name.
+     * <p>
+     * @param name
+     * @param attributes
+     * @throws CacheException
+     */
+    void resetElementAttributes( K name, IElementAttributes attributes )
+        throws CacheException;
+
+    /**
+     * Gets the elementAttributes attribute of the ICacheAccess object
+     * <p>
+     * @param name
+     * @return The elementAttributes value
+     * @throws CacheException
+     */
+    IElementAttributes getElementAttributes( K name )
+        throws CacheException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccessManagement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccessManagement.java
new file mode 100644
index 0000000..21c01f4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/ICacheAccessManagement.java
@@ -0,0 +1,111 @@
+package org.apache.commons.jcs.access.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+
+/**
+ * ICacheAccessManagement defines the methods for cache management, cleanup and shutdown.
+ */
+public interface ICacheAccessManagement
+{
+    /**
+     * Dispose this region. Flushes objects to and closes auxiliary caches. This is a shutdown
+     * command!
+     * <p>
+     * To simply remove all elements from the region use clear().
+     */
+    void dispose();
+
+    /**
+     * Removes all of the elements from a region.
+     * <p>
+     * @throws CacheException
+     */
+    void clear() throws CacheException;
+
+    /**
+     * GetElementAttributes will return an attribute object describing the current attributes
+     * associated with the object name. If no name parameter is available, the attributes for the
+     * region will be returned. The name object must override the Object.equals and Object.hashCode
+     * methods.
+     * <p>
+     * @return The elementAttributes value
+     * @throws CacheException
+     */
+    IElementAttributes getDefaultElementAttributes()
+        throws CacheException;
+
+    /**
+     * This method is does not reset the attributes for items already in the cache. It could
+     * potentially do this for items in memory, and maybe on disk (which would be slow) but not
+     * remote items. Rather than have unpredictable behavior, this method just sets the default
+     * attributes. Items subsequently put into the cache will use these defaults if they do not
+     * specify specific attributes.
+     * <p>
+     * @param attr the default attributes.
+     * @throws CacheException if something goes wrong.
+     */
+    void setDefaultElementAttributes( IElementAttributes attr ) throws CacheException;
+
+    /**
+     * Gets the ICompositeCacheAttributes of the cache region
+     * <p>
+     * @return ICompositeCacheAttributes
+     */
+    ICompositeCacheAttributes getCacheAttributes();
+
+    /**
+     * Sets the ICompositeCacheAttributes of the cache region
+     * <p>
+     * @param cattr The new ICompositeCacheAttribute value
+     */
+    void setCacheAttributes( ICompositeCacheAttributes cattr );
+
+    /**
+     * This instructs the memory cache to remove the <i>numberToFree</i> according to its eviction
+     * policy. For example, the LRUMemoryCache will remove the <i>numberToFree</i> least recently
+     * used items. These will be spooled to disk if a disk auxiliary is available.
+     * <p>
+     * @param numberToFree
+     * @return the number that were removed. if you ask to free 5, but there are only 3, you will
+     *         get 3.
+     * @throws CacheException
+     */
+    int freeMemoryElements( int numberToFree )
+        throws CacheException;
+
+    /**
+     * This returns the ICacheStats object with information on this region and its auxiliaries.
+     * <p>
+     * This data can be formatted as needed.
+     * <p>
+     * @return ICacheStats
+     */
+    ICacheStats getStatistics();
+
+    /**
+     * @return A String version of the stats.
+     */
+    String getStats();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/IGroupCacheAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/IGroupCacheAccess.java
new file mode 100644
index 0000000..4527d24
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/behavior/IGroupCacheAccess.java
@@ -0,0 +1,89 @@
+package org.apache.commons.jcs.access.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+import java.util.Set;
+
+/**
+ * IGroupCacheAccess defines group specific behavior for the client access
+ * classes.
+ */
+public interface IGroupCacheAccess<K, V>
+    extends ICacheAccessManagement
+{
+    /**
+     * Gets the g attribute of the IGroupCacheAccess object
+     * <p>
+     * @param name
+     * @param group
+     *            the name of the group to associate this with.
+     * @return The object that is keyed by the name in the group
+     */
+    V getFromGroup( K name, String group );
+
+    /**
+     * Puts an item in the cache associated with this group.
+     * <p>
+     * @param key
+     * @param group
+     * @param obj
+     * @throws CacheException
+     */
+    void putInGroup( K key, String group, V obj )
+        throws CacheException;
+
+    /**
+     * Put in the cache associated with this group using these attributes.
+     * <p>
+     * @param key
+     * @param group
+     * @param obj
+     * @param attr
+     * @throws CacheException
+     */
+    void putInGroup( K key, String group, V obj, IElementAttributes attr )
+        throws CacheException;
+
+    /**
+     * Remove the item from this group in this region by this name.
+     * <p>
+     * @param name
+     * @param group
+     */
+    void removeFromGroup( K name, String group );
+
+    /**
+     * Gets the set of keys of objects currently in the group
+     * <p>
+     * @param group
+     * @return the set of group keys.
+     */
+    Set<K> getGroupKeys( String group );
+
+    /**
+     * Invalidates a group
+     * <p>
+     * @param group
+     */
+    void invalidateGroup( String group );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/CacheException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/CacheException.java
new file mode 100644
index 0000000..78479b1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/CacheException.java
@@ -0,0 +1,66 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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 is the most general exception the cache throws.
+ */
+public class CacheException
+    extends RuntimeException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 8725795372935590265L;
+
+    /**
+     * Default
+     */
+    public CacheException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the CacheException object
+     * @param nested a nested exception
+     */
+    public CacheException( Throwable nested )
+    {
+        super(nested);
+    }
+
+    /**
+     * Constructor for the CacheException object
+     * @param message the exception message
+     */
+    public CacheException( String message )
+    {
+        super(message);
+    }
+
+    /**
+     * Constructor for the CacheException object
+     * @param message the exception message
+     * @param nested a nested exception
+     */
+    public CacheException(String message, Throwable nested)
+    {
+        super(message, nested);
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ConfigurationException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ConfigurationException.java
new file mode 100644
index 0000000..03f4890
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ConfigurationException.java
@@ -0,0 +1,44 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/** Thrown if there is some severe configuration problem that makes the cache nonfunctional. */
+public class ConfigurationException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 6881044536186097055L;
+
+    /** Constructor for the ConfigurationException object */
+    public ConfigurationException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the ConfigurationException object.
+     * <p>
+     * @param message
+     */
+    public ConfigurationException( String message )
+    {
+        super( message );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidArgumentException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidArgumentException.java
new file mode 100644
index 0000000..cfb4435
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidArgumentException.java
@@ -0,0 +1,47 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * InvalidArgumentException is thrown if an argument is passed to the cache that is invalid. For
+ * instance, null values passed to put result in this exception.
+ */
+public class InvalidArgumentException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -6058373692208755562L;
+
+    /** Constructor for the InvalidArgumentException object */
+    public InvalidArgumentException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the InvalidArgumentException object.
+     * <p>
+     * @param message
+     */
+    public InvalidArgumentException( String message )
+    {
+        super( message );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidGroupException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidGroupException.java
new file mode 100644
index 0000000..870a402
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidGroupException.java
@@ -0,0 +1,47 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * InvalidGroupException
+ */
+public class InvalidGroupException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -5219807114008843480L;
+
+    /** Constructor for the InvalidGroupException object */
+    public InvalidGroupException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the InvalidGroupException object
+     * <p>
+     * @param message
+     */
+    public InvalidGroupException( String message )
+    {
+        super( message );
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidHandleException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidHandleException.java
new file mode 100644
index 0000000..6b0e6d3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/InvalidHandleException.java
@@ -0,0 +1,48 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * InvalidHandleException is not used.
+ */
+public class InvalidHandleException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -5947822454839845924L;
+
+    /** Constructor for the InvalidHandleException object */
+    public InvalidHandleException()
+    {
+        // nothing
+        super();
+    }
+
+    /**
+     * Constructor for the InvalidHandleException object.
+     * <p>
+     * @param message
+     */
+    public InvalidHandleException( String message )
+    {
+        super( message );
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectExistsException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectExistsException.java
new file mode 100644
index 0000000..edef1a4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectExistsException.java
@@ -0,0 +1,53 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * The putSafe method on the JCS convenience class throws this exception if the object is already
+ * present in the cache.
+ * <p>
+ * I'm removing this exception from normal use.
+ * <p>
+ * The overhead of throwing exceptions and the cumbersomeness of coding around exceptions warrants
+ * removal. Exceptions like this don't make sense to throw in the course of normal operations to
+ * signify a normal and expected condition. Returning null if an object isn't found is sufficient.
+ */
+public class ObjectExistsException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -3779745827993383872L;
+
+    /** Constructor for the ObjectExistsException object */
+    public ObjectExistsException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the ObjectExistsException object
+     * @param message
+     */
+    public ObjectExistsException( String message )
+    {
+        super( message );
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectNotFoundException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectNotFoundException.java
new file mode 100644
index 0000000..8dca2de
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/exception/ObjectNotFoundException.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.access.exception;
+
+/*
+ * 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.
+ */
+
+/**
+ * ObjectNotFoundException
+ * <p>
+ * TODO see if we can remove this.
+ * <p>
+ * This is thrown from the composite cache if you as for the element attributes and the element does
+ * not exist.
+ */
+public class ObjectNotFoundException
+    extends CacheException
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 5684353421076546842L;
+
+    /** Constructor for the ObjectNotFoundException object */
+    public ObjectNotFoundException()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the ObjectNotFoundException object
+     * @param message
+     */
+    public ObjectNotFoundException( String message )
+    {
+        super( message );
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/package.html
new file mode 100644
index 0000000..477e5cc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/access/package.html
@@ -0,0 +1,27 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    Contains classes for accessing the cache. The CacheAccess interface, which
+    all classes in this package implement, provides all the methods a client
+    should need to use a Cache.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheElementInfo.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheElementInfo.java
new file mode 100644
index 0000000..1c09423
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheElementInfo.java
@@ -0,0 +1,124 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import java.beans.ConstructorProperties;
+
+
+/**
+ * Stores info on a cache element for the template
+ */
+public class CacheElementInfo
+{
+    /** element key */
+    private final String key;
+
+    /** is it eternal */
+    private final boolean eternal;
+
+    /** when it was created */
+    private final String createTime;
+
+    /** max life */
+    private final long maxLifeSeconds;
+
+    /** when it will expire */
+    private final long expiresInSeconds;
+
+    /**
+     * Parameterized constructor
+     *
+	 * @param key element key
+	 * @param eternal is it eternal
+	 * @param createTime when it was created
+	 * @param maxLifeSeconds max life
+	 * @param expiresInSeconds when it will expire
+	 */
+    @ConstructorProperties({"key", "eternal", "createTime", "maxLifeSeconds", "expiresInSeconds"})
+    public CacheElementInfo(String key, boolean eternal, String createTime,
+			long maxLifeSeconds, long expiresInSeconds)
+    {
+		super();
+		this.key = key;
+		this.eternal = eternal;
+		this.createTime = createTime;
+		this.maxLifeSeconds = maxLifeSeconds;
+		this.expiresInSeconds = expiresInSeconds;
+	}
+
+	/**
+     * @return a string representation of the key
+     */
+    public String getKey()
+    {
+        return this.key;
+    }
+
+    /**
+     * @return true if the item does not expire
+     */
+    public boolean isEternal()
+    {
+        return this.eternal;
+    }
+
+    /**
+     * @return the time the object was created
+     */
+    public String getCreateTime()
+    {
+        return this.createTime;
+    }
+
+    /**
+     * Ignored if isEternal
+     * @return the longest this object can live.
+     */
+    public long getMaxLifeSeconds()
+    {
+        return this.maxLifeSeconds;
+    }
+
+    /**
+     * Ignored if isEternal
+     * @return how many seconds until this object expires.
+     */
+    public long getExpiresInSeconds()
+    {
+        return this.expiresInSeconds;
+    }
+
+    /**
+     * @return string info on the item
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nCacheElementInfo " );
+        buf.append( "\n Key [" ).append( getKey() ).append( "]" );
+        buf.append( "\n Eternal [" ).append( isEternal() ).append( "]" );
+        buf.append( "\n CreateTime [" ).append( getCreateTime() ).append( "]" );
+        buf.append( "\n MaxLifeSeconds [" ).append( getMaxLifeSeconds() ).append( "]" );
+        buf.append( "\n ExpiresInSeconds [" ).append( getExpiresInSeconds() ).append( "]" );
+
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheRegionInfo.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheRegionInfo.java
new file mode 100644
index 0000000..963f5d4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CacheRegionInfo.java
@@ -0,0 +1,180 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import java.beans.ConstructorProperties;
+
+
+
+/**
+ * Stores info on a cache region for the template
+ */
+public class CacheRegionInfo
+{
+    /** The name of the cache region */
+    private final String cacheName;
+
+    /** The size of the cache region */
+    private final int cacheSize;
+
+    /** The status of the cache region */
+    private final String cacheStatus;
+
+    /** The statistics of the cache region */
+    private final String cacheStatistics;
+
+    /** The number of memory hits in the cache region */
+    private final int hitCountRam;
+
+    /** The number of auxiliary hits in the cache region */
+    private final int hitCountAux;
+
+    /** The number of misses in the cache region because the items were not found */
+    private final int missCountNotFound;
+
+    /** The number of misses in the cache region because the items were expired */
+    private final int missCountExpired;
+
+    /** The number of bytes counted so far, will be a total of all items */
+    private final long byteCount;
+
+    /**
+     * Parameterized constructor
+     *
+	 * @param cacheName The name of the cache region
+	 * @param cacheSize The size of the cache region
+	 * @param cacheStatus The status of the cache region
+	 * @param cacheStatistics The statistics of the cache region
+	 * @param hitCountRam The number of memory hits in the cache region
+	 * @param hitCountAux The number of auxiliary hits in the cache region
+	 * @param missCountNotFound The number of misses in the cache region because the items were not found
+	 * @param missCountExpired The number of misses in the cache region because the items were expired
+	 * @param byteCount The number of bytes counted so far, will be a total of all items
+	 */
+    @ConstructorProperties({"cacheName", "cacheSize", "cacheStatus", "cacheStatistics",
+    	"hitCountRam", "hitCountAux", "missCountNotFound", "missCountExpired", "byteCount"})
+	public CacheRegionInfo(String cacheName, int cacheSize, String cacheStatus,
+			String cacheStatistics, int hitCountRam, int hitCountAux,
+			int missCountNotFound, int missCountExpired, long byteCount)
+	{
+		super();
+		this.cacheName = cacheName;
+		this.cacheSize = cacheSize;
+		this.cacheStatus = cacheStatus;
+		this.cacheStatistics = cacheStatistics;
+		this.hitCountRam = hitCountRam;
+		this.hitCountAux = hitCountAux;
+		this.missCountNotFound = missCountNotFound;
+		this.missCountExpired = missCountExpired;
+		this.byteCount = byteCount;
+	}
+
+	/**
+	 * @return the cacheName
+	 */
+	public String getCacheName()
+	{
+		return this.cacheName;
+	}
+
+	/**
+	 * @return the cacheSize
+	 */
+	public int getCacheSize()
+	{
+		return this.cacheSize;
+	}
+
+	/**
+     * @return a status string
+     */
+    public String getCacheStatus()
+    {
+        return this.cacheStatus;
+    }
+
+    /**
+     * Return the statistics for the region.
+     * <p>
+     * @return String
+     */
+    public String getCacheStatistics()
+    {
+        return this.cacheStatistics;
+    }
+
+    /**
+	 * @return the hitCountRam
+	 */
+	public int getHitCountRam()
+	{
+		return hitCountRam;
+	}
+
+	/**
+	 * @return the hitCountAux
+	 */
+	public int getHitCountAux()
+	{
+		return hitCountAux;
+	}
+
+	/**
+	 * @return the missCountNotFound
+	 */
+	public int getMissCountNotFound()
+	{
+		return missCountNotFound;
+	}
+
+	/**
+	 * @return the missCountExpired
+	 */
+	public int getMissCountExpired()
+	{
+		return missCountExpired;
+	}
+
+	/**
+     * @return total byte count
+     */
+    public long getByteCount()
+    {
+        return this.byteCount;
+    }
+
+    /**
+     * @return string info on the region
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nCacheRegionInfo " );
+        if ( cacheName != null )
+        {
+            buf.append( "\n CacheName [" + cacheName + "]" );
+            buf.append( "\n Status [" + cacheStatus + "]" );
+        }
+        buf.append( "\n ByteCount [" + getByteCount() + "]" );
+
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CountingOnlyOutputStream.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CountingOnlyOutputStream.java
new file mode 100644
index 0000000..ea0baf8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/CountingOnlyOutputStream.java
@@ -0,0 +1,84 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Keeps track of the number of bytes written to it, but doesn't write them anywhere.
+ */
+public class CountingOnlyOutputStream
+    extends OutputStream
+{
+    /** number of bytes passed through */
+    private int count; // TODO should this be long?
+
+    /**
+     * count as we write.
+     * <p>
+     * @param b
+     * @throws IOException
+     */
+    @Override
+    public void write( byte[] b )
+        throws IOException
+    {
+        this.count += b.length;
+    }
+
+    /**
+     * count as we write.
+     * <p>
+     * @param b
+     * @param off
+     * @param len
+     * @throws IOException
+     */
+    @Override
+    public void write( byte[] b, int off, int len )
+        throws IOException
+    {
+        this.count += len;
+    }
+
+    /**
+     * count as we write.
+     * <p>
+     * @param b
+     * @throws IOException
+     */
+    @Override
+    public void write( int b )
+        throws IOException
+    {
+        this.count++;
+    }
+
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
+     * @return int
+     */
+    public int getCount()
+    {
+        return this.count;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdmin.jsp b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdmin.jsp
new file mode 100644
index 0000000..d92b0af
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdmin.jsp
@@ -0,0 +1,310 @@
+<%--
+ 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.
+--%>
+<%@page import="org.apache.commons.jcs.JCS"%>
+<%@page import="org.apache.commons.jcs.access.CacheAccess" %>
+<%@page import="org.apache.commons.jcs.admin.CacheElementInfo" %>
+<%@page import="org.apache.commons.jcs.admin.CacheRegionInfo" %>
+<%@page import="java.io.Serializable" %>
+<%@page import="java.util.HashMap" %>
+
+<jsp:useBean id="jcsBean" scope="request" class="org.apache.commons.jcs.admin.JCSAdminBean" />
+
+<html>
+<head>
+
+<SCRIPT LANGUAGE="Javascript">
+  function decision( message, url )
+  {
+    if( confirm(message) )
+    {
+      location.href = url;
+    }
+  }
+</SCRIPT>
+
+<title> JCS Admin Servlet </title>
+
+</head>
+
+<body>
+
+<%
+			String CACHE_NAME_PARAM = "cacheName";
+			String ACTION_PARAM = "action";
+		 	String CLEAR_ALL_REGIONS_ACTION = "clearAllRegions";
+		 	String CLEAR_REGION_ACTION = "clearRegion";
+		 	String REMOVE_ACTION = "remove";
+		 	String DETAIL_ACTION = "detail";
+		 	String REGION_SUMMARY_ACTION = "regionSummary";
+		 	String ITEM_ACTION = "item";
+			String KEY_PARAM = "key";
+			String SILENT_PARAM = "silent";
+
+     		String DEFAULT_TEMPLATE_NAME = "DEFAULT";
+     		String REGION_DETAIL_TEMPLATE_NAME = "DETAIL";
+     		String ITEM_TEMPLATE_NAME = "ITEM";
+     		String REGION_SUMMARY_TEMPLATE_NAME = "SUMMARY";
+
+			String templateName = DEFAULT_TEMPLATE_NAME;
+
+			HashMap<String, Object> context = new HashMap<String, Object>();
+
+			// Get cacheName for actions from request (might be null)
+			String cacheName = request.getParameter( CACHE_NAME_PARAM );
+
+			if ( cacheName != null )
+			{
+			    cacheName = cacheName.trim();
+			}
+
+			// If an action was provided, handle it
+			String action = request.getParameter( ACTION_PARAM );
+
+			if ( action != null )
+			{
+				if ( action.equals( CLEAR_ALL_REGIONS_ACTION ) )
+				{
+					jcsBean.clearAllRegions();
+				}
+				else if ( action.equals( CLEAR_REGION_ACTION ) )
+				{
+					if ( cacheName == null )
+					{
+						// Not Allowed
+					}
+					else
+					{
+						jcsBean.clearRegion( cacheName );
+					}
+				}
+				else if ( action.equals( REMOVE_ACTION ) )
+				{
+					String[] keys = request.getParameterValues( KEY_PARAM );
+
+					for ( int i = 0; i < keys.length; i++ )
+					{
+						jcsBean.removeItem( cacheName, keys[ i ] );
+					}
+
+					templateName = REGION_DETAIL_TEMPLATE_NAME;
+				}
+				else if ( action.equals( DETAIL_ACTION ) )
+				{
+					templateName = REGION_DETAIL_TEMPLATE_NAME;
+				}
+				else if ( action.equals( ITEM_ACTION ) )
+				{
+					templateName = ITEM_TEMPLATE_NAME;
+				}
+				else if ( action.equals( REGION_SUMMARY_ACTION ) )
+				{
+					templateName = REGION_SUMMARY_TEMPLATE_NAME;
+				}
+			}
+
+			if ( request.getParameter( SILENT_PARAM ) != null )
+			{
+				// If silent parameter was passed, no output should be produced.
+				//return null;
+			}
+			else
+			{
+				// Populate the context based on the template
+				if ( templateName == REGION_DETAIL_TEMPLATE_NAME )
+				{
+					//context.put( "cacheName", cacheName );
+					context.put( "elementInfoRecords", jcsBean.buildElementInfo( cacheName ) );
+				}
+				else if ( templateName == DEFAULT_TEMPLATE_NAME )
+				{
+					context.put( "cacheInfoRecords", jcsBean.buildCacheInfo() );
+				}
+			}
+
+///////////////////////////////////////////////////////////////////////////////////
+			//handle display
+
+			if ( templateName == ITEM_TEMPLATE_NAME )
+			{
+			    String key = request.getParameter( KEY_PARAM );
+
+			    if ( key != null )
+			    {
+			        key = key.trim();
+			    }
+
+			    CacheAccess<Serializable, Serializable> cache = JCS.getInstance( cacheName );
+				org.apache.commons.jcs.engine.behavior.ICacheElement<?, ?> element = cache.getCacheElement( key );
+%>
+<h1> Item for key [<%=key%>] in region [<%=cacheName%>] </h1>
+
+<a href="JCSAdmin.jsp?action=detail&cacheName=<%=cacheName%>">Region Detail</a>
+| <a href="JCSAdmin.jsp">All Regions</a>
+
+  <pre>
+	<%=element%>
+  </pre>
+<%
+			}
+			else if ( templateName == REGION_SUMMARY_TEMPLATE_NAME )
+			{
+%>
+
+<h1> Summary for region [<%=cacheName%>] </h1>
+
+<a href="JCSAdmin.jsp">All Regions</a>
+
+<%
+    CacheAccess<?, ?> cache = JCS.getInstance( cacheName );
+    String stats = cache.getStats();
+%>
+
+    <br>
+<b> Stats for region [<%=cacheName%>] </b>
+
+    <pre>
+    	<%=stats%>
+    </pre>
+
+<%
+			}
+			else if ( templateName == REGION_DETAIL_TEMPLATE_NAME )
+			{
+%>
+
+<h1> Detail for region [<%=cacheName%>] </h1>
+
+<a href="JCSAdmin.jsp">All Regions</a>
+
+<table border="1" cellpadding="5" >
+    <tr>
+        <th> Key </th>
+        <th> Eternal? </th>
+        <th> Create time </th>
+        <th> Max Life (s) </th>
+        <th> Till Expiration (s) </th>
+    </tr>
+<%
+	CacheElementInfo[] list = (CacheElementInfo[]) context.get( "elementInfoRecords" );
+    for (CacheElementInfo element : list)
+    {
+%>
+        <tr>
+            <td> <%=element.getKey()%> </td>
+            <td> <%=element.isEternal()%> </td>
+            <td> <%=element.getCreateTime()%> </td>
+            <td> <%=element.getMaxLifeSeconds()%> </td>
+            <td> <%=element.getExpiresInSeconds()%> </td>
+            <td>
+             <a href="JCSAdmin.jsp?action=item&cacheName=<%=cacheName%>&key=<%=element.getKey()%>"> View </a>
+            | <a href="JCSAdmin.jsp?action=remove&cacheName=<%=cacheName%>&key=<%=element.getKey()%>"> Remove </a>
+            </td>
+        </tr>
+<%
+    }
+
+    CacheAccess<?, ?> cache = JCS.getInstance( cacheName );
+    String stats = cache.getStats();
+%>
+    </table>
+
+    <br>
+<b> Stats for region [<%=cacheName%>] </b>
+
+    <pre>
+    	<%=stats%>
+    </pre>
+<%
+  }
+  else
+  {
+%>
+
+<h1> Cache Regions </h1>
+
+<p>
+These are the regions which are currently defined in the cache. 'Items' and
+'Bytes' refer to the elements currently in memory (not spooled). You can clear
+all items for a region by selecting 'Remove all' next to the desired region
+below. You can also <a href="javascript:decision('Clicking OK will clear all the data from all regions!','JCSAdmin.jsp?action=clearAllRegions')">Clear all regions</a>
+which empties the entire cache.
+</p>
+<p>
+	<form action="JCSAdmin.jsp">
+		<input type="hidden" name="action" value="item">
+		Retrieve (key) <input type="text" name="key">  
+		(region) <select name="cacheName">
+<%
+  CacheRegionInfo[] listSelect = (CacheRegionInfo[]) context.get( "cacheInfoRecords" );
+  for (CacheRegionInfo record : listSelect)
+  {
+	%>
+    <option value="<%=record.getCacheName()%>"><%=record.getCacheName()%></option>
+	<%
+  }
+%>
+				</select>
+		<input type="submit">
+	</form>
+</p>
+
+<table border="1" cellpadding="5" >
+    <tr>
+        <th> Cache Name </th>
+        <th> Items </th>
+        <th> Bytes </th>
+        <th> Status </th>
+        <th> Memory Hits </th>
+        <th> Aux Hits </th>
+        <th> Not Found Misses </th>
+        <th> Expired Misses </th>
+    </tr>
+
+<%
+	CacheRegionInfo[] list = (CacheRegionInfo[]) context.get( "cacheInfoRecords" );
+    for (CacheRegionInfo record : listSelect)
+    {
+%>
+        <tr>
+            <td> <%=record.getCacheName()%> </td>
+            <td> <%=record.getCacheSize()%> </td>
+            <td> <%=record.getByteCount()%> </td>
+            <td> <%=record.getCacheStatus()%> </td>
+            <td> <%=record.getHitCountRam()%> </td>
+            <td> <%=record.getHitCountAux()%> </td>
+            <td> <%=record.getMissCountNotFound()%> </td>
+            <td> <%=record.getMissCountExpired()%> </td>
+            <td>
+                <a href="JCSAdmin.jsp?action=regionSummary&cacheName=<%=record.getCacheName()%>"> Summary </a>
+                | <a href="JCSAdmin.jsp?action=detail&cacheName=<%=record.getCacheName()%>"> Detail </a>
+                | <a href="javascript:decision('Clicking OK will remove all the data from the region [<%=record.getCacheName()%>]!','JCSAdmin.jsp?action=clearRegion&cacheName=<%=record.getCacheName()%>')"> Clear </a>
+            </td>
+        </tr>
+<%
+    }
+%>
+    </table>
+<%
+  }
+%>
+
+
+</body>
+
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdminBean.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdminBean.java
new file mode 100644
index 0000000..79ae211
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSAdminBean.java
@@ -0,0 +1,436 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServer;
+import org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory;
+import org.apache.commons.jcs.engine.CacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.memory.behavior.IMemoryCache;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * A servlet which provides HTTP access to JCS. Allows a summary of regions to be viewed, and
+ * removeAll to be run on individual regions or all regions. Also provides the ability to remove
+ * items (any number of key arguments can be provided with action 'remove'). Should be initialized
+ * with a properties file that provides at least a classpath resource loader.
+ */
+public class JCSAdminBean implements JCSJMXBean
+{
+    /** The cache manager. */
+    private final CompositeCacheManager cacheHub;
+
+    /**
+     * Default constructor
+     */
+    public JCSAdminBean()
+    {
+        super();
+        try
+        {
+            this.cacheHub = CompositeCacheManager.getInstance();
+        }
+        catch (CacheException e)
+        {
+            throw new RuntimeException("Could not retrieve cache manager instance", e);
+        }
+    }
+
+    /**
+     * Parameterized constructor
+     *
+	 * @param cacheHub the cache manager instance
+	 */
+	public JCSAdminBean(CompositeCacheManager cacheHub)
+	{
+		super();
+		this.cacheHub = cacheHub;
+	}
+
+	/**
+     * Builds up info about each element in a region.
+     * <p>
+     * @param cacheName
+     * @return Array of CacheElementInfo objects
+     * @throws Exception
+     */
+    @Override
+    public CacheElementInfo[] buildElementInfo( String cacheName )
+        throws Exception
+    {
+        CompositeCache<Serializable, Serializable> cache = cacheHub.getCache( cacheName );
+
+        Serializable[] keys = cache.getMemoryCache().getKeySet().toArray(new Serializable[0]);
+
+        // Attempt to sort keys according to their natural ordering. If that
+        // fails, get the key array again and continue unsorted.
+        try
+        {
+            Arrays.sort( keys );
+        }
+        catch ( Exception e )
+        {
+            keys = cache.getMemoryCache().getKeySet().toArray(new Serializable[0]);
+        }
+
+        LinkedList<CacheElementInfo> records = new LinkedList<CacheElementInfo>();
+
+        ICacheElement<Serializable, Serializable> element;
+        IElementAttributes attributes;
+        CacheElementInfo elementInfo;
+
+        DateFormat format = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT );
+
+        long now = System.currentTimeMillis();
+
+        for (Serializable key : keys)
+        {
+            element = cache.getMemoryCache().getQuiet( key );
+
+            attributes = element.getElementAttributes();
+
+            elementInfo = new CacheElementInfo(
+            		String.valueOf( key ),
+            		attributes.getIsEternal(),
+            		format.format(new Date(attributes.getCreateTime())),
+            		attributes.getMaxLife(),
+            		(now - attributes.getCreateTime() - attributes.getMaxLife() * 1000 ) / -1000);
+
+            records.add( elementInfo );
+        }
+
+        return records.toArray(new CacheElementInfo[0]);
+    }
+
+    /**
+     * Builds up data on every region.
+     * <p>
+     * TODO we need a most light weight method that does not count bytes. The byte counting can
+     *       really swamp a server.
+     * @return list of CacheRegionInfo objects
+     * @throws Exception
+     */
+    @Override
+    public CacheRegionInfo[] buildCacheInfo()
+        throws Exception
+    {
+        String[] cacheNames = cacheHub.getCacheNames();
+
+        Arrays.sort( cacheNames );
+
+        LinkedList<CacheRegionInfo> cacheInfo = new LinkedList<CacheRegionInfo>();
+
+        CacheRegionInfo regionInfo;
+        CompositeCache<?, ?> cache;
+
+        for ( int i = 0; i < cacheNames.length; i++ )
+        {
+            cache = cacheHub.getCache( cacheNames[i] );
+
+            regionInfo = new CacheRegionInfo(
+                    cache.getCacheName(),
+                    cache.getSize(),
+                    cache.getStatus().toString(),
+                    cache.getStats(),
+                    cache.getHitCountRam(),
+                    cache.getHitCountAux(),
+                    cache.getMissCountNotFound(),
+                    cache.getMissCountExpired(),
+                    getByteCount( cache ));
+
+            cacheInfo.add( regionInfo );
+        }
+
+        return cacheInfo.toArray(new CacheRegionInfo[0]);
+    }
+
+
+	/**
+     * Tries to estimate how much data is in a region. This is expensive. If there are any non serializable objects in
+     * the region or an error occurs, suppresses exceptions and returns 0.
+     * <p/>
+     *
+     * @return int The size of the region in bytes.
+     */
+	@Override
+    public int getByteCount(String cacheName)
+	{
+		return getByteCount(cacheHub.getCache(cacheName));
+	}
+
+	/**
+     * Tries to estimate how much data is in a region. This is expensive. If there are any non serializable objects in
+     * the region or an error occurs, suppresses exceptions and returns 0.
+     * <p/>
+     *
+     * @return int The size of the region in bytes.
+     */
+    public <K, V> int getByteCount(CompositeCache<K, V> cache)
+    {
+        if (cache == null)
+        {
+            throw new IllegalArgumentException("The cache object specified was null.");
+        }
+
+        long size = 0;
+        IMemoryCache<K, V> memCache = cache.getMemoryCache();
+
+        for (K key : memCache.getKeySet())
+        {
+            ICacheElement<K, V> ice = null;
+			try
+			{
+				ice = memCache.get(key);
+			}
+			catch (IOException e)
+			{
+                throw new RuntimeException("IOException while trying to get a cached element", e);
+			}
+
+			if (ice == null)
+			{
+				continue;
+			}
+
+			if (ice instanceof CacheElementSerialized)
+            {
+                size = size + ((CacheElementSerialized<K, V>) ice).getSerializedValue().length;
+            }
+            else
+            {
+                Object element = ice.getVal();
+
+                //CountingOnlyOutputStream: Keeps track of the number of bytes written to it, but doesn't write them anywhere.
+                CountingOnlyOutputStream counter = new CountingOnlyOutputStream();
+                ObjectOutputStream out = null;
+                try
+                {
+                    out = new ObjectOutputStream(counter);
+                    out.writeObject(element);
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException("IOException while trying to measure the size of the cached element", e);
+                }
+                finally
+                {
+                	try
+                	{
+                		if (out != null)
+                		{
+                			out.close();
+                		}
+					}
+                	catch (IOException e)
+                	{
+                		// ignore
+					}
+                	try
+                	{
+						counter.close();
+					}
+                	catch (IOException e)
+                	{
+                		// ignore
+					}
+                }
+
+                // 4 bytes lost for the serialization header
+                size = size + counter.getCount() - 4;
+            }
+        }
+
+        if (size > Integer.MAX_VALUE)
+        {
+            throw new IllegalStateException("The size of cache " + cache.getCacheName() + " (" + size + " bytes) is too large to be represented as an integer.");
+        }
+
+        return (int) size;
+    }
+
+    /**
+     * Clears all regions in the cache.
+     * <p/>
+     * If this class is running within a remote cache server, clears all regions via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears all regions in the cache directly via
+     * the usual cache API.
+     */
+    @Override
+    public void clearAllRegions() throws IOException
+    {
+        if (RemoteCacheServerFactory.getRemoteCacheServer() == null)
+        {
+            // Not running in a remote cache server.
+            // Remove objects from the cache directly, as no need to broadcast removes to client machines...
+
+            String[] names = cacheHub.getCacheNames();
+
+            for (int i = 0; i < names.length; i++)
+            {
+                cacheHub.getCache(names[i]).removeAll();
+            }
+        }
+        else
+        {
+            // Running in a remote cache server.
+            // Remove objects via the RemoteCacheServer API, so that removes will be broadcast to client machines...
+            try
+            {
+                String[] cacheNames = cacheHub.getCacheNames();
+
+                // Call remoteCacheServer.removeAll(String) for each cacheName...
+                RemoteCacheServer<?, ?> remoteCacheServer = RemoteCacheServerFactory.getRemoteCacheServer();
+                for (int i = 0; i < cacheNames.length; i++)
+                {
+                    String cacheName = cacheNames[i];
+                    remoteCacheServer.removeAll(cacheName);
+                }
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("Failed to remove all elements from all cache regions: " + e, e);
+            }
+        }
+    }
+
+    /**
+     * Clears a particular cache region.
+     * <p/>
+     * If this class is running within a remote cache server, clears the region via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears the region directly via the usual
+     * cache API.
+     */
+    @Override
+    public void clearRegion(String cacheName) throws IOException
+    {
+        if (cacheName == null)
+        {
+            throw new IllegalArgumentException("The cache name specified was null.");
+        }
+        if (RemoteCacheServerFactory.getRemoteCacheServer() == null)
+        {
+            // Not running in a remote cache server.
+            // Remove objects from the cache directly, as no need to broadcast removes to client machines...
+            cacheHub.getCache(cacheName).removeAll();
+        }
+        else
+        {
+            // Running in a remote cache server.
+            // Remove objects via the RemoteCacheServer API, so that removes will be broadcast to client machines...
+            try
+            {
+                // Call remoteCacheServer.removeAll(String)...
+                RemoteCacheServer<?, ?> remoteCacheServer = RemoteCacheServerFactory.getRemoteCacheServer();
+                remoteCacheServer.removeAll(cacheName);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("Failed to remove all elements from cache region [" + cacheName + "]: " + e, e);
+            }
+        }
+    }
+
+    /**
+     * Removes a particular item from a particular region.
+     * <p/>
+     * If this class is running within a remote cache server, removes the item via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears the region directly via the usual
+     * cache API.
+     *
+     * @param cacheName
+     * @param key
+     *
+     * @throws IOException
+     */
+    @Override
+    public void removeItem(String cacheName, String key) throws IOException
+    {
+        if (cacheName == null)
+        {
+            throw new IllegalArgumentException("The cache name specified was null.");
+        }
+        if (key == null)
+        {
+            throw new IllegalArgumentException("The key specified was null.");
+        }
+        if (RemoteCacheServerFactory.getRemoteCacheServer() == null)
+        {
+            // Not running in a remote cache server.
+            // Remove objects from the cache directly, as no need to broadcast removes to client machines...
+            cacheHub.getCache(cacheName).remove(key);
+        }
+        else
+        {
+            // Running in a remote cache server.
+            // Remove objects via the RemoteCacheServer API, so that removes will be broadcast to client machines...
+            try
+            {
+                Object keyToRemove = null;
+                CompositeCache<?, ?> cache = CompositeCacheManager.getInstance().getCache(cacheName);
+
+                // A String key was supplied, but to remove elements via the RemoteCacheServer API, we need the
+                // actual key object as stored in the cache (i.e. a Serializable object). To find the key in this form,
+                // we iterate through all keys stored in the memory cache until we find one whose toString matches
+                // the string supplied...
+                Set<?> allKeysInCache = cache.getMemoryCache().getKeySet();
+                for (Object keyInCache : allKeysInCache)
+                {
+                    if (keyInCache.toString().equals(key))
+                    {
+                        if (keyToRemove == null)
+                        {
+                            keyToRemove = keyInCache;
+                        }
+                        else
+                        {
+                            // A key matching the one specified was already found...
+                            throw new IllegalStateException("Unexpectedly found duplicate keys in the cache region matching the key specified.");
+                        }
+                    }
+                }
+                if (keyToRemove == null)
+                {
+                    throw new IllegalStateException("No match for this key could be found in the set of keys retrieved from the memory cache.");
+                }
+                // At this point, we have retrieved the matching K key.
+
+                // Call remoteCacheServer.remove(String, Serializable)...
+                RemoteCacheServer<Serializable, Serializable> remoteCacheServer = RemoteCacheServerFactory.getRemoteCacheServer();
+                remoteCacheServer.remove(cacheName, key);
+            }
+            catch (Exception e)
+            {
+                throw new IllegalStateException("Failed to remove element with key [" + key + ", " + key.getClass() + "] from cache region [" + cacheName + "]: " + e, e);
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSJMXBean.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSJMXBean.java
new file mode 100644
index 0000000..863d6a4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/JCSJMXBean.java
@@ -0,0 +1,90 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import javax.management.MXBean;
+import java.io.IOException;
+
+/**
+ * A MXBean to expose the JCS statistics to JMX
+ */
+ at MXBean
+public interface JCSJMXBean
+{
+    /**
+     * Builds up info about each element in a region.
+     * <p>
+     * @param cacheName
+     * @return Array of CacheElementInfo objects
+     * @throws Exception
+     */
+    CacheElementInfo[] buildElementInfo( String cacheName ) throws Exception;
+
+    /**
+     * Builds up data on every region.
+     * <p>
+     * TODO we need a most light weight method that does not count bytes. The byte counting can
+     *       really swamp a server.
+     * @return Array of CacheRegionInfo objects
+     * @throws Exception
+     */
+    CacheRegionInfo[] buildCacheInfo() throws Exception;
+
+    /**
+     * Tries to estimate how much data is in a region. This is expensive. If there are any non serializable objects in
+     * the region or an error occurs, suppresses exceptions and returns 0.
+     * <p/>
+     *
+     * @return int The size of the region in bytes.
+     */
+    int getByteCount(String cacheName);
+
+    /**
+     * Clears all regions in the cache.
+     * <p/>
+     * If this class is running within a remote cache server, clears all regions via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears all regions in the cache directly via
+     * the usual cache API.
+     */
+    void clearAllRegions() throws IOException;
+
+    /**
+     * Clears a particular cache region.
+     * <p/>
+     * If this class is running within a remote cache server, clears the region via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears the region directly via the usual
+     * cache API.
+     */
+    void clearRegion(String cacheName) throws IOException;
+
+    /**
+     * Removes a particular item from a particular region.
+     * <p/>
+     * If this class is running within a remote cache server, removes the item via the <code>RemoteCacheServer</code>
+     * API, so that removes will be broadcast to client machines. Otherwise clears the region directly via the usual
+     * cache API.
+     *
+     * @param cacheName
+     * @param key
+     *
+     * @throws IOException
+     */
+    void removeItem(String cacheName, String key) throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServlet.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServlet.java
new file mode 100644
index 0000000..2a26c03
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServlet.java
@@ -0,0 +1,181 @@
+package org.apache.commons.jcs.admin.servlet;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.admin.JCSAdminBean;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.tools.view.VelocityViewServlet;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * A servlet which provides HTTP access to JCS. Allows a summary of regions to
+ * be viewed, and removeAll to be run on individual regions or all regions. Also
+ * provides the ability to remove items (any number of key arguments can be
+ * provided with action 'remove'). Should be initialized with a properties file
+ * that provides at least a classpath resource loader. Since this extends
+ * VelocityServlet, which uses the singleton model for velocity, it will share
+ * configuration with any other Velocity in the same JVM.
+ * <p>
+ * Initialization in a webapp will look something like this:
+ * <p>
+ *
+ * <pre>
+ *
+ *    [servlet]
+ *        [servlet-name]JCSAdminServlet[/servlet-name]
+ *        [servlet-class]org.apache.commons.jcs.admin.servlet.JCSAdminServlet[/servlet-class]
+ *        [init-param]
+ *            [param-name]properties[/param-name]
+ *            [param-value]WEB-INF/conf/JCSAdminServlet.velocity.properties[/param-value]
+ *        [/init-param]
+ *    [/servlet]
+ *
+ * </pre>
+ *
+ * <p>
+ * FIXME: It would be nice to use the VelocityEngine model so this can be truly
+ * standalone. Right now if you run it in the same container as, say, turbine,
+ * turbine must be run first to ensure it's config takes precedence.
+ * <p>
+ */
+public class JCSAdminServlet
+    extends VelocityViewServlet
+{
+    private static final long serialVersionUID = -5519844149238645275L;
+
+    private static final String DEFAULT_TEMPLATE_NAME = "/org/apache/jcs/admin/servlet/JCSAdminServletDefault.vm";
+
+    private static final String REGION_DETAIL_TEMPLATE_NAME = "/org/apache/jcs/admin/servlet/JCSAdminServletRegionDetail.vm";
+
+    // Keys for parameters
+
+    private static final String CACHE_NAME_PARAM = "cacheName";
+
+    private static final String ACTION_PARAM = "action";
+
+    private static final String KEY_PARAM = "key";
+
+    private static final String SILENT_PARAM = "silent";
+
+    // Possible values for 'action' parameter
+
+    private static final String CLEAR_ALL_REGIONS_ACTION = "clearAllRegions";
+
+    private static final String CLEAR_REGION_ACTION = "clearRegion";
+
+    private static final String REMOVE_ACTION = "remove";
+
+    private static final String DETAIL_ACTION = "detail";
+
+    /**
+     * Velocity based admin servlet.
+     * <p>
+     * @param request
+     * @param response
+     * @param context
+     * @return Template
+     * @throws Exception
+     *
+     */
+    @Override
+    protected Template handleRequest( HttpServletRequest request, HttpServletResponse response, Context context )
+    {
+        JCSAdminBean admin = new JCSAdminBean();
+
+        String templateName = DEFAULT_TEMPLATE_NAME;
+
+        // Get cacheName for actions from request (might be null)
+
+        String cacheName = request.getParameter( CACHE_NAME_PARAM );
+
+        // If an action was provided, handle it
+
+        String action = request.getParameter( ACTION_PARAM );
+
+        try
+        {
+			if ( action != null )
+			{
+			    if ( action.equals( CLEAR_ALL_REGIONS_ACTION ) )
+			    {
+			        admin.clearAllRegions();
+			    }
+			    else if ( action.equals( CLEAR_REGION_ACTION ) )
+			    {
+			        if ( cacheName != null )
+			        {
+			            admin.clearRegion( cacheName );
+			        }
+			    }
+			    else if ( action.equals( REMOVE_ACTION ) )
+			    {
+			        String[] keys = request.getParameterValues( KEY_PARAM );
+
+			        for ( int i = 0; i < keys.length; i++ )
+			        {
+			            admin.removeItem( cacheName, keys[i] );
+			        }
+
+			        templateName = REGION_DETAIL_TEMPLATE_NAME;
+			    }
+			    else if ( action.equals( DETAIL_ACTION ) )
+			    {
+			        templateName = REGION_DETAIL_TEMPLATE_NAME;
+			    }
+			}
+		}
+        catch (IOException e)
+        {
+        	getLog().error("Could not execute action.", e);
+        	return null;
+		}
+
+        if ( request.getParameter( SILENT_PARAM ) != null )
+        {
+            // If silent parameter was passed, no output should be produced.
+
+            return null;
+        }
+        // Populate the context based on the template
+
+        try
+        {
+			if ( templateName == REGION_DETAIL_TEMPLATE_NAME )
+			{
+			    context.put( "cacheName", cacheName );
+			    context.put( "elementInfoRecords", admin.buildElementInfo( cacheName ) );
+			}
+			else if ( templateName == DEFAULT_TEMPLATE_NAME )
+			{
+			    context.put( "cacheInfoRecords", admin.buildCacheInfo() );
+			}
+		}
+        catch (Exception e)
+        {
+        	getLog().error("Could not populate context.", e);
+		}
+
+        return getTemplate( templateName );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletDefault.vm b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletDefault.vm
new file mode 100644
index 0000000..1c993cb
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletDefault.vm
@@ -0,0 +1,64 @@
+## 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.
+<html>
+
+<head><title> JCS Admin Servlet </title></head>
+
+<body>
+
+<h1> Cache Regions </h1>
+
+<p>These are the regions which are currently defined in the cache. 'Items' and
+'Bytes' refer to the elements currently in memory (not spooled). You can clear
+all items for a region by selecting 'Remove all' next to the desired region
+below. You can also <a href="?action=clearAllRegions">Clear all regions</a>
+which empties the entire cache.</p>
+
+<table border="1" cellpadding="5" >
+    <tr>
+        <th> Cache Name </th>
+        <th> Items </th>
+        <th> Bytes </th>
+        <th> Status </th>
+        <th> Memory Hits </th>
+        <th> Aux Hits </th>
+        <th> Not Found Misses </th>
+        <th> Expired Misses </th>
+    </tr>
+
+    #foreach ( $record in $cacheInfoRecords )
+        <tr>
+            <td> $record.cache.cacheName </td>
+            <td> $record.cache.size </td>
+            <td> $record.byteCount </td>
+            <td> $record.status </td>
+            <td> $record.cache.hitCountRam </td>
+            <td> $record.cache.hitCountAux </td>
+            <td> $record.cache.missCountNotFound </td>
+            <td> $record.cache.missCountExpired </td>
+            <td>
+                <a href="?action=detail&cacheName=${record.cache.cacheName}"> Detail </a>
+                | <a href="?action=clearRegion&cacheName=${record.cache.cacheName}"> Remove all </a>
+            </td>
+        </tr>
+    #end
+
+</table>
+
+</body>
+
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletRegionDetail.vm b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletRegionDetail.vm
new file mode 100644
index 0000000..8bbbf06
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/admin/servlet/JCSAdminServletRegionDetail.vm
@@ -0,0 +1,50 @@
+## 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.
+<html>
+
+<head><title> JCS Admin Servlet Region Detail </title></head>
+
+<body>
+
+<h1> Keys for region: $cacheName </h1>
+
+<table border="1" cellpadding="5" >
+    <tr>
+        <th> Key </th>
+        <th> Eternal? </th>
+        <th> Create time </th>
+        <th> Max Life (s) </th>
+        <th> Till Expiration (s) </th>
+    </tr>
+
+    #foreach ( $element in $elementInfoRecords )
+
+        <tr>
+            <td> $element.key </td>
+            <td> $element.eternal </td>
+            <td> $element.createTime </td>
+            <td> $element.maxLifeSeconds </td>
+            <td> $element.expiresInSeconds </td>
+            <td> <a href="?action=remove&cacheName=${cacheName}&key=${element.key}"> Remove </a> </td>
+        </tr>
+    #end
+
+</table>
+
+</body>
+
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCache.java
new file mode 100644
index 0000000..f66d3f5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCache.java
@@ -0,0 +1,210 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.CacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.match.KeyMatcherPatternImpl;
+import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+/** This holds convenience methods used by most auxiliary caches. */
+public abstract class AbstractAuxiliaryCache<K, V>
+    implements AuxiliaryCache<K, V>
+{
+    /** An optional event logger */
+    private ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. Uses a standard serializer by default. */
+    private IElementSerializer elementSerializer = new StandardSerializer();
+
+    /** Key matcher used by the getMatching API */
+    private IKeyMatcher<K> keyMatcher = new KeyMatcherPatternImpl<K>();
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param item
+     * @param eventName
+     * @return ICacheEvent
+     */
+    protected ICacheEvent<K> createICacheEvent( ICacheElement<K, V> item, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<K>();
+        }
+        String diskLocation = getEventLoggingExtraInfo();
+        String regionName = null;
+        K key = null;
+        if ( item != null )
+        {
+            regionName = item.getCacheName();
+            key = item.getKey();
+        }
+        return cacheEventLogger.createICacheEvent( getAuxiliaryCacheAttributes().getName(), regionName, eventName,
+                                                   diskLocation, key );
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param regionName
+     * @param key
+     * @param eventName
+     * @return ICacheEvent
+     */
+    protected <T> ICacheEvent<T> createICacheEvent( String regionName, T key, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<T>();
+        }
+        String diskLocation = getEventLoggingExtraInfo();
+        return cacheEventLogger.createICacheEvent( getAuxiliaryCacheAttributes().getName(), regionName, eventName,
+                                                   diskLocation, key );
+
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param cacheEvent
+     */
+    protected <T> void logICacheEvent( ICacheEvent<T> cacheEvent )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
+        }
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param errorMessage
+     */
+    protected void logError( String source, String eventName, String errorMessage )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logError( source, eventName, errorMessage );
+        }
+    }
+
+    /**
+     * Gets the extra info for the event log.
+     * <p>
+     * @return IP, or disk location, etc.
+     */
+    public abstract String getEventLoggingExtraInfo();
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @param cacheEventLogger
+     */
+    @Override
+    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheEventLogger = cacheEventLogger;
+    }
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @return cacheEventLogger
+     */
+    public ICacheEventLogger getCacheEventLogger()
+    {
+        return this.cacheEventLogger;
+    }
+
+    /**
+     * Allows you to inject a custom serializer. A good example would be a compressing standard
+     * serializer.
+     * <p>
+     * Does not allow you to set it to null.
+     * <p>
+     * @param elementSerializer
+     */
+    @Override
+    public void setElementSerializer( IElementSerializer elementSerializer )
+    {
+        if ( elementSerializer != null )
+        {
+            this.elementSerializer = elementSerializer;
+        }
+    }
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @return elementSerializer
+     */
+    public IElementSerializer getElementSerializer()
+    {
+        return this.elementSerializer;
+    }
+
+    /**
+     * Sets the key matcher used by get matching.
+     * <p>
+     * @param keyMatcher
+     */
+    @Override
+    public void setKeyMatcher( IKeyMatcher<K> keyMatcher )
+    {
+        if ( keyMatcher != null )
+        {
+            this.keyMatcher = keyMatcher;
+        }
+    }
+
+    /**
+     * Returns the key matcher used by get matching.
+     * <p>
+     * @return keyMatcher
+     */
+    public IKeyMatcher<K> getKeyMatcher()
+    {
+        return this.keyMatcher;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java
new file mode 100644
index 0000000..dfc598b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheAttributes.java
@@ -0,0 +1,130 @@
+package org.apache.commons.jcs.auxiliary;
+
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+
+/*
+ * 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 has common attributes used by all auxiliaries.
+ */
+public abstract class AbstractAuxiliaryCacheAttributes
+    implements AuxiliaryCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -6594609334959187673L;
+
+    /** cacheName */
+    private String cacheName;
+
+    /** name */
+    private String name;
+
+    /** eventQueueType -- pooled, or single threaded */
+    private ICacheEventQueue.QueueType eventQueueType;
+
+    /** Named when pooled */
+    private String eventQueuePoolName;
+
+    /**
+     * @param name
+     */
+    @Override
+    public void setCacheName( String name )
+    {
+        this.cacheName = name;
+    }
+
+    /**
+     * Gets the cacheName attribute of the AuxiliaryCacheAttributes object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return this.cacheName;
+    }
+
+    /**
+     * This is the name of the auxiliary in configuration file.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes#setName(java.lang.String)
+     */
+    @Override
+    public void setName( String s )
+    {
+        this.name = s;
+    }
+
+    /**
+     * Gets the name attribute of the AuxiliaryCacheAttributes object
+     * <p>
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * SINGLE is the default. If you choose POOLED, the value of EventQueuePoolName will be used
+     * <p>
+     * @param queueType SINGLE or POOLED
+     */
+    @Override
+    public void setEventQueueType( ICacheEventQueue.QueueType queueType )
+    {
+        this.eventQueueType = queueType;
+    }
+
+    /**
+     * @return SINGLE or POOLED
+     */
+    @Override
+    public ICacheEventQueue.QueueType getEventQueueType()
+    {
+        return eventQueueType;
+    }
+
+    /**
+     * If you choose a POOLED event queue type, the value of EventQueuePoolName will be used. This
+     * is ignored if the pool type is SINGLE
+     * <p>
+     * @param s SINGLE or POOLED
+     */
+    @Override
+    public void setEventQueuePoolName( String s )
+    {
+        eventQueuePoolName = s;
+    }
+
+    /**
+     * Sets the pool name to use. If a pool is not found by this name, the thread pool manager will
+     * return a default configuration.
+     * <p>
+     * @return name of thread pool to use for this auxiliary
+     */
+    @Override
+    public String getEventQueuePoolName()
+    {
+        return eventQueuePoolName;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheEventLogging.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheEventLogging.java
new file mode 100644
index 0000000..44e368e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AbstractAuxiliaryCacheEventLogging.java
@@ -0,0 +1,353 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * All ICacheEvents are defined as final. Children must implement process events. These are wrapped
+ * in event log parent calls.
+ * <p>
+ * You can override the public method, but if you don't, the default will call getWithTiming.
+ */
+public abstract class AbstractAuxiliaryCacheEventLogging<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /**
+     * Puts an item into the cache.
+     * <p>
+     * @param cacheElement
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> cacheElement )
+        throws IOException
+    {
+        updateWithEventLogging( cacheElement );
+    }
+
+    /**
+     * Puts an item into the cache. Wrapped in logging.
+     * <p>
+     * @param cacheElement
+     * @throws IOException
+     */
+    protected final void updateWithEventLogging( ICacheElement<K, V> cacheElement )
+        throws IOException
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheElement, ICacheEventLogger.UPDATE_EVENT );
+        try
+        {
+            processUpdate( cacheElement );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Implementation of put.
+     * <p>
+     * @param cacheElement
+     * @throws IOException
+     */
+    protected abstract void processUpdate( ICacheElement<K, V> cacheElement )
+        throws IOException;
+
+    /**
+     * Gets the item from the cache.
+     * <p>
+     * @param key
+     * @return ICacheElement, a wrapper around the key, value, and attributes
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        return getWithEventLogging( key );
+    }
+
+    /**
+     * Gets the item from the cache. Wrapped in logging.
+     * <p>
+     * @param key
+     * @return ICacheElement, a wrapper around the key, value, and attributes
+     * @throws IOException
+     */
+    protected final ICacheElement<K, V> getWithEventLogging( K key )
+        throws IOException
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( getCacheName(), key, ICacheEventLogger.GET_EVENT );
+        try
+        {
+            return processGet( key );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Implementation of get.
+     * <p>
+     * @param key
+     * @return ICacheElement, a wrapper around the key, value, and attributes
+     * @throws IOException
+     */
+    protected abstract ICacheElement<K, V> processGet( K key )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+        throws IOException
+    {
+        return getMultipleWithEventLogging( keys );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    protected final Map<K, ICacheElement<K, V>> getMultipleWithEventLogging(Set<K> keys )
+        throws IOException
+    {
+        ICacheEvent<Serializable> cacheEvent = createICacheEvent( getCacheName(), (Serializable) keys,
+                                                    ICacheEventLogger.GETMULTIPLE_EVENT );
+        try
+        {
+            return processGetMultiple( keys );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Implementation of getMultiple.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    protected abstract Map<K, ICacheElement<K, V>> processGetMultiple(Set<K> keys)
+        throws IOException;
+
+    /**
+     * Gets items from the cache matching the given pattern. Items from memory will replace those
+     * from remote sources.
+     * <p>
+     * This only works with string keys. It's too expensive to do a toString on every key.
+     * <p>
+     * Auxiliaries will do their best to handle simple expressions. For instance, the JDBC disk
+     * cache will convert * to % and . to _
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
+        throws IOException
+    {
+        return getMatchingWithEventLogging( pattern );
+    }
+
+    /**
+     * Gets mmatching items from the cache based on the given pattern.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data matching the pattern.
+     * @throws IOException
+     */
+    protected final Map<K, ICacheElement<K, V>> getMatchingWithEventLogging( String pattern )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( getCacheName(), pattern, ICacheEventLogger.GETMATCHING_EVENT );
+        try
+        {
+            return processGetMatching( pattern );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Implementation of getMatching.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data matching the pattern.
+     * @throws IOException
+     */
+    protected abstract Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+        throws IOException;
+
+    /**
+     * Removes the item from the cache. Wraps the remove in event logs.
+     * <p>
+     * @param key
+     * @return boolean, whether or not the item was removed
+     * @throws IOException
+     */
+    @Override
+    public boolean remove( K key )
+        throws IOException
+    {
+        return removeWithEventLogging( key );
+    }
+
+    /**
+     * Removes the item from the cache. Wraps the remove in event logs.
+     * <p>
+     * @param key
+     * @return boolean, whether or not the item was removed
+     * @throws IOException
+     */
+    protected final boolean removeWithEventLogging( K key )
+        throws IOException
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( getCacheName(), key, ICacheEventLogger.REMOVE_EVENT );
+        try
+        {
+            return processRemove( key );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Specific implementation of remove.
+     * <p>
+     * @param key
+     * @return boolean, whether or not the item was removed
+     * @throws IOException
+     */
+    protected abstract boolean processRemove( K key )
+        throws IOException;
+
+    /**
+     * Removes all from the region. Wraps the removeAll in event logs.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        removeAllWithEventLogging();
+    }
+
+    /**
+     * Removes all from the region. Wraps the removeAll in event logs.
+     * <p>
+     * @throws IOException
+     */
+    protected final void removeAllWithEventLogging()
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( getCacheName(), "all", ICacheEventLogger.REMOVEALL_EVENT );
+        try
+        {
+            processRemoveAll();
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Specific implementation of removeAll.
+     * <p>
+     * @throws IOException
+     */
+    protected abstract void processRemoveAll()
+        throws IOException;
+
+    /**
+     * Synchronously dispose the remote cache; if failed, replace the remote handle with a zombie.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        disposeWithEventLogging();
+    }
+
+    /**
+     * Synchronously dispose the remote cache; if failed, replace the remote handle with a zombie.
+     * Wraps the removeAll in event logs.
+     * <p>
+     * @throws IOException
+     */
+    protected final void disposeWithEventLogging()
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( getCacheName(), "none", ICacheEventLogger.DISPOSE_EVENT );
+        try
+        {
+            processDispose();
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Specific implementation of dispose.
+     * <p>
+     * @throws IOException
+     */
+    protected abstract void processDispose()
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCache.java
new file mode 100644
index 0000000..3bc3a52
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCache.java
@@ -0,0 +1,77 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Tag interface for auxiliary caches. Currently this provides no additional methods over what is in
+ * ICache, but I anticipate that will change. For example, there will be a mechanism for determining
+ * the type (disk/lateral/remote) of the auxiliary here -- and the existing getCacheType will be
+ * removed from ICache.
+ */
+public interface AuxiliaryCache<K, V>
+    extends ICache<K, V>
+{
+    /**
+     * Get a set of the keys for all elements in the auxiliary cache.
+     * <p>
+     * @return a set of the key type
+     * TODO This should probably be done in chunks with a range passed in. This
+     *       will be a problem if someone puts a 1,000,000 or so items in a
+     *       region.
+     * @throws IOException if access to the auxiliary cache fails
+     */
+    Set<K> getKeySet() throws IOException;
+
+    /**
+     * @return the historical and statistical data for a region's auxiliary cache.
+     */
+    IStats getStatistics();
+
+    /**
+     * This returns the generic attributes for an auxiliary cache. Most implementations will cast
+     * this to a more specific type.
+     * <p>
+     * @return the attributes for the auxiliary cache
+     */
+    AuxiliaryCacheAttributes getAuxiliaryCacheAttributes();
+
+    /**
+     * Allows you to inject a custom serializer. A good example would be a compressing standard
+     * serializer.
+     * <p>
+     * @param elementSerializer
+     */
+    void setElementSerializer( IElementSerializer elementSerializer );
+
+    /**
+     * Every Auxiliary must allow for the use of an event logger.
+     * <p>
+     * @param cacheEventLogger
+     */
+    void setCacheEventLogger( ICacheEventLogger cacheEventLogger );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheAttributes.java
new file mode 100644
index 0000000..c81b29d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheAttributes.java
@@ -0,0 +1,95 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+
+import java.io.Serializable;
+
+/**
+ * This is a nominal interface that auxiliary cache attributes should implement. This allows the
+ * auxiliary mangers to share a common interface.
+ */
+public interface AuxiliaryCacheAttributes
+    extends Serializable
+{
+    /**
+     * Sets the name of the cache, referenced by the appropriate manager.
+     * <p>
+     * @param s The new cacheName value
+     */
+    void setCacheName( String s );
+
+    /**
+     * Gets the cacheName attribute of the AuxiliaryCacheAttributes object
+     * <p>
+     * @return The cacheName value
+     */
+    String getCacheName();
+
+    /**
+     * Name known by by configurator
+     * <p>
+     * @param s The new name value
+     */
+    void setName( String s );
+
+    /**
+     * Gets the name attribute of the AuxiliaryCacheAttributes object
+     * <p>
+     * @return The name value
+     */
+    String getName();
+
+    /**
+     * SINGLE is the default. If you choose POOLED, the value of EventQueuePoolName will be used
+     * <p>
+     * @param s SINGLE or POOLED
+     */
+    void setEventQueueType( ICacheEventQueue.QueueType s );
+
+    /**
+     * @return SINGLE or POOLED
+     */
+    ICacheEventQueue.QueueType getEventQueueType();
+
+    /**
+     * If you choose a POOLED event queue type, the value of EventQueuePoolName will be used. This
+     * is ignored if the pool type is SINGLE
+     * <p>
+     * @param s SINGLE or POOLED
+     */
+    void setEventQueuePoolName( String s );
+
+    /**
+     * Sets the pool name to use. If a pool is not found by this name, the thread pool manager will
+     * return a default configuration.
+     * <p>
+     * @return name of thread pool to use for this auxiliary
+     */
+    String getEventQueuePoolName();
+
+    /**
+     * Clones
+     * <p>
+     * @return a copy
+     */
+    AuxiliaryCacheAttributes copy();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfigurator.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfigurator.java
new file mode 100644
index 0000000..1a87313
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfigurator.java
@@ -0,0 +1,128 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.config.OptionConverter;
+import org.apache.commons.jcs.utils.config.PropertySetter;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Properties;
+
+/**
+ * Configuration util for auxiliary caches. I plan to move the auxiliary configuration from the
+ * composite cache configurator here.
+ */
+public class AuxiliaryCacheConfigurator
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( AuxiliaryCacheConfigurator.class );
+
+    /** .attributes */
+    public static final String ATTRIBUTE_PREFIX = ".attributes";
+
+    /**
+     * jcs.auxiliary.NAME.cacheeventlogger=CLASSNAME
+     * <p>
+     * jcs.auxiliary.NAME.cacheeventlogger.attributes.CUSTOMPROPERTY=VALUE
+     */
+    public static final String CACHE_EVENT_LOGGER_PREFIX = ".cacheeventlogger";
+
+    /**
+     * jcs.auxiliary.NAME.serializer=CLASSNAME
+     * <p>
+     * jcs.auxiliary.NAME.serializer.attributes.CUSTOMPROPERTY=VALUE
+     */
+    public static final String SERIALIZER_PREFIX = ".serializer";
+
+    /**
+     * Parses the event logger config, if there is any for the auxiliary.
+     * <p>
+     * @param props
+     * @param auxPrefix - ex. AUXILIARY_PREFIX + auxName
+     * @return cacheEventLogger
+     */
+    public static ICacheEventLogger parseCacheEventLogger( Properties props, String auxPrefix )
+    {
+        ICacheEventLogger cacheEventLogger = null;
+
+        // auxFactory was not previously initialized.
+        String eventLoggerClassName = auxPrefix + CACHE_EVENT_LOGGER_PREFIX;
+        cacheEventLogger = OptionConverter.instantiateByKey( props, eventLoggerClassName, null );
+        if ( cacheEventLogger != null )
+        {
+            String cacheEventLoggerAttributePrefix = auxPrefix + CACHE_EVENT_LOGGER_PREFIX + ATTRIBUTE_PREFIX;
+            PropertySetter.setProperties( cacheEventLogger, props, cacheEventLoggerAttributePrefix + "." );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Using custom cache event logger [" + cacheEventLogger + "] for auxiliary [" + auxPrefix
+                    + "]" );
+            }
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "No cache event logger defined for auxiliary [" + auxPrefix + "]" );
+            }
+        }
+        return cacheEventLogger;
+    }
+
+    /**
+     * Parses the element config, if there is any for the auxiliary.
+     * <p>
+     * @param props
+     * @param auxPrefix - ex. AUXILIARY_PREFIX + auxName
+     * @return cacheEventLogger
+     */
+    public static IElementSerializer parseElementSerializer( Properties props, String auxPrefix )
+    {
+        // TODO take in the entire prop key
+        IElementSerializer elementSerializer = null;
+
+        // auxFactory was not previously initialized.
+        String elementSerializerClassName = auxPrefix + SERIALIZER_PREFIX;
+        elementSerializer = OptionConverter.instantiateByKey( props, elementSerializerClassName, null );
+        if ( elementSerializer != null )
+        {
+            String attributePrefix = auxPrefix + SERIALIZER_PREFIX + ATTRIBUTE_PREFIX;
+            PropertySetter.setProperties( elementSerializer, props, attributePrefix + "." );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Using custom element serializer [" + elementSerializer + "] for auxiliary [" + auxPrefix
+                    + "]" );
+            }
+        }
+        else
+        {
+            // use the default standard serializer
+            elementSerializer = new StandardSerializer();
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Using standard serializer [" + elementSerializer + "] for auxiliary [" + auxPrefix + "]" );
+            }
+        }
+        return elementSerializer;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheFactory.java
new file mode 100644
index 0000000..689aead
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheFactory.java
@@ -0,0 +1,59 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/**
+ * All auxiliary caches must have a factory that the cache configurator can use to create instances.
+ */
+public interface AuxiliaryCacheFactory
+{
+    /**
+     * Creates an auxiliary using the supplied attributes. Adds it to the composite cache manager.
+     * <p>
+     * @param attr
+     * @param cacheMgr This allows auxiliaries to reference the manager without assuming that it is
+     *            a singleton. This will allow JCS to be a nonsingleton. Also, it makes it easier to
+     *            test.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    <K, V> AuxiliaryCache<K, V> createCache(
+            AuxiliaryCacheAttributes attr, ICompositeCacheManager cacheMgr,
+            ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer );
+
+    /**
+     * Sets the name attribute of the AuxiliaryCacheFactory object
+     * <p>
+     * @param s The new name value
+     */
+    void setName( String s );
+
+    /**
+     * Gets the name attribute of the AuxiliaryCacheFactory object
+     * <p>
+     * @return The name value
+     */
+    String getName();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheManager.java
new file mode 100644
index 0000000..5f2c1d1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheManager.java
@@ -0,0 +1,46 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+/**
+ * AuxiliaryCacheManager
+ */
+public interface AuxiliaryCacheManager
+{
+    /**
+     * Return the appropriate auxiliary cache for this region.
+     * <p>
+     * @param cacheName
+     * @return AuxiliaryCache
+     */
+    <K, V> AuxiliaryCache<K, V> getCache( String cacheName );
+
+    /**
+     * This allows the cache manager to be plugged into the auxiliary caches,
+     * rather then having them get it themselves. Cache managers can be mocked
+     * out and the auxiliaries will be easier to test.
+     * <p>
+     * @param cacheName
+     * @param cacheManager
+     * @return AuxiliaryCache
+     */
+    //AuxiliaryCache getCache( String cacheName, ICompositeCacheManager
+    // cacheManager );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCache.java
new file mode 100644
index 0000000..42dbacc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCache.java
@@ -0,0 +1,872 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheEventLogging;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.disk.behavior.IDiskCacheAttributes;
+import org.apache.commons.jcs.engine.CacheEventQueueFactory;
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Abstract class providing a base implementation of a disk cache, which can be easily extended to
+ * implement a disk cache for a specific persistence mechanism.
+ * <p>
+ * When implementing the abstract methods note that while this base class handles most things, it
+ * does not acquire or release any locks. Implementations should do so as necessary. This is mainly
+ * done to minimize the time spent in critical sections.
+ * <p>
+ * Error handling in this class needs to be addressed. Currently if an exception is thrown by the
+ * persistence mechanism, this class destroys the event queue. Should it also destroy purgatory?
+ * Should it dispose itself?
+ */
+public abstract class AbstractDiskCache<K, V>
+    extends AbstractAuxiliaryCacheEventLogging<K, V>
+    implements AuxiliaryCache<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( AbstractDiskCache.class );
+
+    /** Generic disk cache attributes */
+    private IDiskCacheAttributes diskCacheAttributes = null;
+
+    // TODO most of these fields should be made private with getters/setters as necessary
+    // Though hopefully many of them can be set at construction and made final
+
+    /**
+     * Map where elements are stored between being added to this cache and actually spooled to disk.
+     * This allows puts to the disk cache to return quickly, and the more expensive operation of
+     * serializing the elements to persistent storage queued for later.
+     * <p>
+     * If the elements are pulled into the memory cache while the are still in purgatory, writing to
+     * disk can be canceled.
+     */
+    protected Map<K, PurgatoryElement<K, V>> purgatory = new HashMap<K, PurgatoryElement<K, V>>();
+
+    /**
+     * The CacheEventQueue where changes will be queued for asynchronous updating of the persistent
+     * storage.
+     */
+    protected ICacheEventQueue<K, V> cacheEventQueue;
+
+    /**
+     * Indicates whether the cache is 'alive': initialized, but not yet disposed. Child classes must
+     * set this to true.
+     */
+    protected boolean alive = false;
+
+    /** Every cache will have a name, subclasses must set this when they are initialized. */
+    protected String cacheName;
+
+    /** DEBUG: Keeps a count of the number of purgatory hits for debug messages */
+    protected int purgHits = 0;
+
+    /**
+     * We lock here, so that we cannot get an update after a remove all. an individual removal locks
+     * the item.
+     */
+    protected final ReentrantReadWriteLock removeAllLock = new ReentrantReadWriteLock();
+
+    // ----------------------------------------------------------- constructors
+
+    /**
+     * Construct the abstract disk cache, create event queues and purgatory. Child classes should
+     * set the alive flag to true after they are initialized.
+     * <p>
+     * @param attr
+     */
+    public AbstractDiskCache( IDiskCacheAttributes attr )
+    {
+        this.diskCacheAttributes = attr;
+
+        this.cacheName = attr.getCacheName();
+
+        // create queue
+        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
+        this.cacheEventQueue = fact.createCacheEventQueue( new MyCacheListener(), CacheInfo.listenerId, cacheName,
+                                                           diskCacheAttributes.getEventQueuePoolName(),
+                                                           diskCacheAttributes.getEventQueueType() );
+
+        // create purgatory
+        initPurgatory();
+    }
+
+    /**
+     * Purgatory size of -1 means to use a HashMap with no size limit. Anything greater will use an
+     * LRU map of some sort.
+     * <p>
+     * TODO Currently setting this to 0 will cause nothing to be put to disk, since it will assume
+     *       that if an item is not in purgatory, then it must have been plucked. We should make 0
+     *       work, a way to not use purgatory.
+     */
+    private void initPurgatory()
+    {
+        // we need this so we can stop the updates from happening after a
+        // removeall
+        removeAllLock.writeLock().lock();
+
+        try
+        {
+            synchronized (this)
+            {
+                if ( diskCacheAttributes.getMaxPurgatorySize() >= 0 )
+                {
+                    purgatory = new LRUMapJCS<K, PurgatoryElement<K, V>>( diskCacheAttributes.getMaxPurgatorySize() );
+                }
+                else
+                {
+                    purgatory = new HashMap<K, PurgatoryElement<K, V>>();
+                }
+            }
+        }
+        finally
+        {
+            removeAllLock.writeLock().unlock();
+        }
+    }
+
+    // ------------------------------------------------------- interface ICache
+
+    /**
+     * Adds the provided element to the cache. Element will be added to purgatory, and then queued
+     * for later writing to the serialized storage mechanism.
+     * <p>
+     * An update results in a put event being created. The put event will call the handlePut method
+     * defined here. The handlePut method calls the implemented doPut on the child.
+     * <p>
+     * @param cacheElement
+     * @throws IOException
+     * @see org.apache.commons.jcs.engine.behavior.ICache#update
+     */
+    @Override
+    public final void update( ICacheElement<K, V> cacheElement )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Putting element in purgatory, cacheName: " + cacheName + ", key: " + cacheElement.getKey() );
+        }
+
+        try
+        {
+            // Wrap the CacheElement in a PurgatoryElement
+            PurgatoryElement<K, V> pe = new PurgatoryElement<K, V>( cacheElement );
+
+            // Indicates the the element is eligible to be spooled to disk,
+            // this will remain true unless the item is pulled back into
+            // memory.
+            pe.setSpoolable( true );
+
+            // Add the element to purgatory
+            synchronized ( purgatory )
+            {
+                purgatory.put( pe.getKey(), pe );
+            }
+
+            // Queue element for serialization
+            cacheEventQueue.addPutEvent( pe );
+        }
+        catch ( IOException ex )
+        {
+            log.error( "Problem adding put event to queue.", ex );
+
+            cacheEventQueue.destroy();
+        }
+    }
+
+    /**
+     * Check to see if the item is in purgatory. If so, return it. If not, check to see if we have
+     * it on disk.
+     * <p>
+     * @param key
+     * @return ICacheElement<K, V> or null
+     * @see AuxiliaryCache#get
+     */
+    @Override
+    public final ICacheElement<K, V> get( K key )
+    {
+        // If not alive, always return null.
+
+        if ( !alive )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "get was called, but the disk cache is not alive." );
+            }
+            return null;
+        }
+
+        PurgatoryElement<K, V> pe = null;
+        synchronized ( purgatory )
+        {
+            pe = purgatory.get( key );
+        }
+
+        // If the element was found in purgatory
+        if ( pe != null )
+        {
+            purgHits++;
+
+            if ( log.isDebugEnabled() )
+            {
+                if ( purgHits % 100 == 0 )
+                {
+                    log.debug( "Purgatory hits = " + purgHits );
+                }
+            }
+
+            // Since the element will go back to the memory cache, we could set
+            // spoolable to false, which will prevent the queue listener from
+            // serializing the element. This would not match the disk cache
+            // behavior and the behavior of other auxiliaries. Gets never remove
+            // items from auxiliaries.
+            // Beyond consistency, the items should stay in purgatory and get
+            // spooled since the mem cache may be set to 0. If an item is
+            // active, it will keep getting put into purgatory and removed. The
+            // CompositeCache now does not put an item to memory from disk if
+            // the size is 0.
+            // Do not set spoolable to false. Just let it go to disk. This
+            // will allow the memory size = 0 setting to work well.
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Found element in purgatory, cacheName: " + cacheName + ", key: " + key );
+            }
+
+            return pe.getCacheElement();
+        }
+
+        // If we reach this point, element was not found in purgatory, so get
+        // it from the cache.
+        try
+        {
+            return doGet( key );
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+
+            cacheEventQueue.destroy();
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets items from the cache matching the given pattern. Items from memory will replace those
+     * from remote sources.
+     * <p>
+     * This only works with string keys. It's too expensive to do a toString on every key.
+     * <p>
+     * Auxiliaries will do their best to handle simple expressions. For instance, the JDBC disk
+     * cache will convert * to % and . to _
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
+        throws IOException
+    {
+        // Get the keys from purgatory
+        Set<K> keyArray = null;
+
+        // this avoids locking purgatory, but it uses more memory
+        synchronized ( purgatory )
+        {
+            keyArray = new HashSet<K>(purgatory.keySet());
+        }
+
+        Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray );
+
+        // call getMultiple with the set
+        Map<K, ICacheElement<K, V>> result = processGetMultiple( matchingKeys );
+
+        // Get the keys from disk
+        Map<K, ICacheElement<K, V>> diskMatches = doGetMatching( pattern );
+
+        result.putAll( diskMatches );
+
+        return result;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMultiple(Set<K> keys)
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * The keys in the cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public abstract Set<K> getKeySet() throws IOException;
+
+    /**
+     * Removes are not queued. A call to remove is immediate.
+     * <p>
+     * @param key
+     * @return whether the item was present to be removed.
+     * @throws IOException
+     * @see org.apache.commons.jcs.engine.behavior.ICache#remove
+     */
+    @Override
+    public final boolean remove( K key )
+        throws IOException
+    {
+        PurgatoryElement<K, V> pe = null;
+
+        synchronized ( purgatory )
+        {
+            // I'm getting the object, so I can lock on the element
+            // Remove element from purgatory if it is there
+            pe = purgatory.get( key );
+        }
+
+        if ( pe != null )
+        {
+            synchronized ( pe.getCacheElement() )
+            {
+                synchronized ( purgatory )
+                {
+                    purgatory.remove( key );
+                }
+
+                // no way to remove from queue, just make sure it doesn't get on
+                // disk and then removed right afterwards
+                pe.setSpoolable( false );
+
+                // Remove from persistent store immediately
+                doRemove( key );
+            }
+        }
+        else
+        {
+            // Remove from persistent store immediately
+            doRemove( key );
+        }
+
+        return false;
+    }
+
+    /**
+     * @throws IOException
+     * @see org.apache.commons.jcs.engine.behavior.ICache#removeAll
+     */
+    @Override
+    public final void removeAll()
+        throws IOException
+    {
+        if ( this.diskCacheAttributes.isAllowRemoveAll() )
+        {
+            // Replace purgatory with a new empty hashtable
+            initPurgatory();
+
+            // Remove all from persistent store immediately
+            doRemoveAll();
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "RemoveAll was requested but the request was not fulfilled: allowRemoveAll is set to false." );
+            }
+        }
+    }
+
+    /**
+     * Adds a dispose request to the disk cache.
+     * <p>
+     * Disposal proceeds in several steps.
+     * <ol>
+     * <li>Prior to this call the Composite cache dumped the memory into the disk cache. If it is
+     * large then we need to wait for the event queue to finish.
+     * <li>Wait until the event queue is empty of until the configured ShutdownSpoolTimeLimit is
+     * reached.
+     * <li>Call doDispose on the concrete impl.
+     * </ol>
+     * @throws IOException
+     */
+    @Override
+    public final void dispose()
+        throws IOException
+    {
+        Runnable disR = new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                boolean keepGoing = true;
+                long total = 0;
+                long interval = 100;
+                while ( keepGoing )
+                {
+                    keepGoing = !cacheEventQueue.isEmpty();
+                    try
+                    {
+                        Thread.sleep( interval );
+                        total += interval;
+                        // log.info( "total = " + total );
+                    }
+                    catch ( InterruptedException e )
+                    {
+                        break;
+                    }
+                }
+                log.info( "No longer waiting for event queue to finish: " + cacheEventQueue.getStatistics() );
+            }
+        };
+        Thread t = new Thread( disR );
+        t.start();
+        // wait up to 60 seconds for dispose and then quit if not done.
+        try
+        {
+            t.join( this.diskCacheAttributes.getShutdownSpoolTimeLimit() * 1000 );
+        }
+        catch ( InterruptedException ex )
+        {
+            log.error( "The Shutdown Spool Process was interrupted.", ex );
+        }
+
+        log.info( "In dispose, destroying event queue." );
+        // This stops the processor thread.
+        cacheEventQueue.destroy();
+
+        // Invoke any implementation specific disposal code
+        // need to handle the disposal first.
+        doDispose();
+
+        alive = false;
+    }
+
+    /**
+     * @return the region name.
+     * @see ICache#getCacheName
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+
+    /**
+     * Gets basic stats for the abstract disk cache.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * Returns semi-structured data.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getStatistics()
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Abstract Disk Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Integer>( "Purgatory Hits", Integer.valueOf(purgHits) ) );
+        elems.add(new StatElement<Integer>( "Purgatory Size", Integer.valueOf(purgatory.size()) ) );
+
+        // get the stats from the event queue too
+        IStats eqStats = this.cacheEventQueue.getStatistics();
+        elems.addAll(eqStats.getStatElements());
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * @return the status -- alive or disposed from CacheConstants
+     * @see ICache#getStatus
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return ( alive ? CacheStatus.ALIVE : CacheStatus.DISPOSED );
+    }
+
+    /**
+     * Size cannot be determined without knowledge of the cache implementation, so subclasses will
+     * need to implement this method.
+     * <p>
+     * @return the number of items.
+     * @see ICache#getSize
+     */
+    @Override
+    public abstract int getSize();
+
+    /**
+     * @see org.apache.commons.jcs.engine.behavior.ICacheType#getCacheType
+     * @return Always returns DISK_CACHE since subclasses should all be of that type.
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.DISK_CACHE;
+    }
+
+    /**
+     * Cache that implements the CacheListener interface, and calls appropriate methods in its
+     * parent class.
+     */
+    protected class MyCacheListener
+        implements ICacheListener<K, V>
+    {
+        /** Id of the listener */
+        private long listenerId = 0;
+
+        /**
+         * @return cacheElement.getElementAttributes();
+         * @throws IOException
+         * @see ICacheListener#getListenerId
+         */
+        @Override
+        public long getListenerId()
+            throws IOException
+        {
+            return this.listenerId;
+        }
+
+        /**
+         * @param id
+         * @throws IOException
+         * @see ICacheListener#setListenerId
+         */
+        @Override
+        public void setListenerId( long id )
+            throws IOException
+        {
+            this.listenerId = id;
+        }
+
+        /**
+         * @param element
+         * @throws IOException
+         * @see ICacheListener#handlePut NOTE: This checks if the element is a puratory element and
+         *      behaves differently depending. However since we have control over how elements are
+         *      added to the cache event queue, that may not be needed ( they are always
+         *      PurgatoryElements ).
+         */
+        @Override
+        public void handlePut( ICacheElement<K, V> element )
+            throws IOException
+        {
+            if ( alive )
+            {
+                // If the element is a PurgatoryElement<K, V> we must check to see
+                // if it is still spoolable, and remove it from purgatory.
+                if ( element instanceof PurgatoryElement )
+                {
+                    PurgatoryElement<K, V> pe = (PurgatoryElement<K, V>) element;
+
+                    synchronized ( pe.getCacheElement() )
+                    {
+                        // TODO consider a timeout.
+                        // we need this so that we can have multiple update
+                        // threads and still have removeAll requests come in that
+                        // always win
+                        removeAllLock.readLock().lock();
+
+                        try
+                        {
+                            // TODO consider changing purgatory sync
+                            // String keyAsString = element.getKey().toString();
+                            synchronized ( purgatory )
+                            {
+                                // If the element has already been removed from
+                                // purgatory do nothing
+                                if ( !purgatory.containsKey( pe.getKey() ) )
+                                {
+                                    return;
+                                }
+
+                                element = pe.getCacheElement();
+                            }
+
+                            // I took this out of the purgatory sync block.
+                            // If the element is still eligible, spool it.
+                            if ( pe.isSpoolable() )
+                            {
+                                doUpdate( element );
+                            }
+                        }
+                        finally
+                        {
+                            removeAllLock.readLock().unlock();
+                        }
+
+                        synchronized ( purgatory )
+                        {
+                            // After the update has completed, it is safe to
+                            // remove the element from purgatory.
+                            purgatory.remove( element.getKey() );
+                        }
+                    }
+                }
+                else
+                {
+                    // call the child's implementation
+                    doUpdate( element );
+                }
+            }
+            else
+            {
+                /*
+                 * The cache is not alive, hence the element should be removed from purgatory. All
+                 * elements should be removed eventually. Perhaps, the alive check should have been
+                 * done before it went in the queue. This block handles the case where the disk
+                 * cache fails during normal operations.
+                 */
+                synchronized ( purgatory )
+                {
+                    purgatory.remove( element.getKey() );
+                }
+            }
+        }
+
+        /**
+         * @param cacheName
+         * @param key
+         * @throws IOException
+         * @see ICacheListener#handleRemove
+         */
+        @Override
+        public void handleRemove( String cacheName, K key )
+            throws IOException
+        {
+            if ( alive )
+            {
+                if ( doRemove( key ) )
+                {
+                    log.debug( "Element removed, key: " + key );
+                }
+            }
+        }
+
+        /**
+         * @param cacheName
+         * @throws IOException
+         * @see ICacheListener#handleRemoveAll
+         */
+        @Override
+        public void handleRemoveAll( String cacheName )
+            throws IOException
+        {
+            if ( alive )
+            {
+                doRemoveAll();
+            }
+        }
+
+        /**
+         * @param cacheName
+         * @throws IOException
+         * @see ICacheListener#handleDispose
+         */
+        @Override
+        public void handleDispose( String cacheName )
+            throws IOException
+        {
+            if ( alive )
+            {
+                doDispose();
+            }
+        }
+    }
+
+    /**
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *WithEventLogging method on the super. The *WithEventLogging methods call
+     * the abstract process* methods. The children implement the process methods.
+     * <p>
+     * ex. doGet calls getWithEventLogging, which calls processGet
+     */
+
+    /**
+     * Get a value from the persistent store.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @param key Key to locate value for.
+     * @return An object matching key, or null.
+     * @throws IOException
+     */
+    protected final ICacheElement<K, V> doGet( K key )
+        throws IOException
+    {
+        return super.getWithEventLogging( key );
+    }
+
+    /**
+     * Get a value from the persistent store.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @param pattern Used to match keys.
+     * @return A map of matches..
+     * @throws IOException
+     */
+    protected final Map<K, ICacheElement<K, V>> doGetMatching( String pattern )
+        throws IOException
+    {
+        return super.getMatchingWithEventLogging( pattern );
+    }
+
+    /**
+     * Add a cache element to the persistent store.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @param cacheElement
+     * @throws IOException
+     */
+    protected final void doUpdate( ICacheElement<K, V> cacheElement )
+        throws IOException
+    {
+        super.updateWithEventLogging( cacheElement );
+    }
+
+    /**
+     * Remove an object from the persistent store if found.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @param key Key of object to remove.
+     * @return whether or no the item was present when removed
+     * @throws IOException
+     */
+    protected final boolean doRemove( K key )
+        throws IOException
+    {
+        return super.removeWithEventLogging( key );
+    }
+
+    /**
+     * Remove all objects from the persistent store.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @throws IOException
+     */
+    protected final void doRemoveAll()
+        throws IOException
+    {
+        super.removeAllWithEventLogging();
+    }
+
+    /**
+     * Dispose of the persistent store. Note that disposal of purgatory and setting alive to false
+     * does NOT need to be done by this method.
+     * <p>
+     * Before the event logging layer, the subclasses implemented the do* methods. Now the do*
+     * methods call the *EventLogging method on the super. The *WithEventLogging methods call the
+     * abstract process* methods. The children implement the process methods.
+     * <p>
+     * @throws IOException
+     */
+    protected final void doDispose()
+        throws IOException
+    {
+        super.disposeWithEventLogging();
+    }
+
+    /**
+     * Gets the extra info for the event log.
+     * <p>
+     * @return disk location
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return getDiskLocation();
+    }
+
+    /**
+     * This is used by the event logging.
+     * <p>
+     * @return the location of the disk, either path or ip.
+     */
+    protected abstract String getDiskLocation();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheAttributes.java
new file mode 100644
index 0000000..a9d016e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheAttributes.java
@@ -0,0 +1,202 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.behavior.IDiskCacheAttributes;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+
+/**
+ * This has common attributes that any conceivable disk cache would need.
+ */
+public abstract class AbstractDiskCacheAttributes
+    extends AbstractAuxiliaryCacheAttributes
+    implements IDiskCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 8306631920391711229L;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( AbstractDiskCacheAttributes.class );
+
+    /** path to disk */
+    private File diskPath;
+
+    /** if this is false, we will not execute remove all */
+    private boolean allowRemoveAll = true;
+
+    /** default to 5000 */
+    private int maxPurgatorySize = MAX_PURGATORY_SIZE_DEFAULT;
+
+    /** Default amount of time to allow for key persistence on shutdown */
+    private static final int DEFAULT_shutdownSpoolTimeLimit = 60;
+
+    /**
+     * This default determines how long the shutdown will wait for the key spool and data defrag to
+     * finish.
+     */
+    private int shutdownSpoolTimeLimit = DEFAULT_shutdownSpoolTimeLimit;
+
+    /**
+     * Sets the diskPath attribute of the DiskCacheAttributes object
+     * <p>
+     * @param path The new diskPath value
+     */
+    @Override
+    public void setDiskPath( String path )
+    {
+        setDiskPath( new File( path ) );
+    }
+
+    /**
+     * Sets the diskPath attribute of the DiskCacheAttributes object
+     * <p>
+     * @param diskPath The new diskPath value
+     */
+    public void setDiskPath( File diskPath )
+    {
+        this.diskPath = diskPath;
+        boolean result = this.diskPath.mkdirs();
+
+        if (!result)
+        {
+            log.error("Failed to create directory " + diskPath);
+        }
+    }
+
+    /**
+     * Gets the diskPath attribute of the attributes object
+     * <p>
+     * @return The diskPath value
+     */
+    @Override
+    public File getDiskPath()
+    {
+        return this.diskPath;
+    }
+
+    /**
+     * Gets the maxKeySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @return The maxPurgatorySize value
+     */
+    @Override
+    public int getMaxPurgatorySize()
+    {
+        return maxPurgatorySize;
+    }
+
+    /**
+     * Sets the maxPurgatorySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @param maxPurgatorySize The new maxPurgatorySize value
+     */
+    @Override
+    public void setMaxPurgatorySize( int maxPurgatorySize )
+    {
+        this.maxPurgatorySize = maxPurgatorySize;
+    }
+
+    /**
+     * Get the amount of time in seconds we will wait for elements to move to disk during shutdown
+     * for a particular region.
+     * <p>
+     * @return the time in seconds.
+     */
+    @Override
+    public int getShutdownSpoolTimeLimit()
+    {
+        return this.shutdownSpoolTimeLimit;
+    }
+
+    /**
+     * Sets the amount of time in seconds we will wait for elements to move to disk during shutdown
+     * for a particular region.
+     * <p>
+     * This is how long we give the event queue to empty.
+     * <p>
+     * The default is 60 seconds.
+     * <p>
+     * @param shutdownSpoolTimeLimit the time in seconds
+     */
+    @Override
+    public void setShutdownSpoolTimeLimit( int shutdownSpoolTimeLimit )
+    {
+        this.shutdownSpoolTimeLimit = shutdownSpoolTimeLimit;
+    }
+
+    /**
+     * Simple clone.
+     * <p>
+     * @return AuxiliaryCacheAttributes
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            // swallow
+        }
+        return this;
+    }
+
+    /**
+     * @param allowRemoveAll The allowRemoveAll to set.
+     */
+    @Override
+    public void setAllowRemoveAll( boolean allowRemoveAll )
+    {
+        this.allowRemoveAll = allowRemoveAll;
+    }
+
+    /**
+     * @return Returns the allowRemoveAll.
+     */
+    @Override
+    public boolean isAllowRemoveAll()
+    {
+        return allowRemoveAll;
+    }
+
+    /**
+     * Includes the common attributes for a debug message.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append( "AbstractDiskCacheAttributes " );
+        str.append( "\n diskPath = " + getDiskPath() );
+        str.append( "\n maxPurgatorySize   = " + getMaxPurgatorySize() );
+        str.append( "\n allowRemoveAll   = " + isAllowRemoveAll() );
+        str.append( "\n ShutdownSpoolTimeLimit   = " + getShutdownSpoolTimeLimit() );
+        return str.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheManager.java
new file mode 100644
index 0000000..3c2fabe
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheManager.java
@@ -0,0 +1,67 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** Common disk cache methods and properties. */
+public abstract class AbstractDiskCacheManager
+    implements AuxiliaryCacheManager
+{
+    /** The event logger. */
+    private ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    private IElementSerializer elementSerializer;
+
+    /**
+     * @param cacheEventLogger the cacheEventLogger to set
+     */
+    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheEventLogger = cacheEventLogger;
+    }
+
+    /**
+     * @return the cacheEventLogger
+     */
+    public ICacheEventLogger getCacheEventLogger()
+    {
+        return cacheEventLogger;
+    }
+
+    /**
+     * @param elementSerializer the elementSerializer to set
+     */
+    public void setElementSerializer( IElementSerializer elementSerializer )
+    {
+        this.elementSerializer = elementSerializer;
+    }
+
+    /**
+     * @return the elementSerializer
+     */
+    public IElementSerializer getElementSerializer()
+    {
+        return elementSerializer;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCS.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCS.java
new file mode 100644
index 0000000..c6b1801
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCS.java
@@ -0,0 +1,75 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.utils.struct.LRUMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Extension of LRUMap for logging of removals. Can switch this back to a HashMap easily. This
+ * provides some abstraction. It also makes it easy to log overflow.
+ */
+public class LRUMapJCS<K, V>
+    extends LRUMap<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LRUMapJCS.class );
+
+    /**
+     * This creates an unbounded version.
+     */
+    public LRUMapJCS()
+    {
+        super();
+    }
+
+    /**
+     * This creates a list bounded by the max key size argument. The Boundary is enforces by an LRU
+     * eviction policy.
+     * <p>
+     * This is used in the Disk cache to store keys and purgatory elements if a boundary is
+     * requested.
+     * <p>
+     * The LRU memory cache uses its own LRU implementation.
+     * <p>
+     * @param maxKeySize
+     */
+    public LRUMapJCS( int maxKeySize )
+    {
+        super( maxKeySize );
+    }
+
+    /**
+     * This is called when an item is removed from the LRU. We just log some information.
+     * <p>
+     * @param key
+     * @param value
+     */
+    @Override
+    protected void processRemovedLRU(K key, V value)
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Removing key [" + key + "] from key store, value [" + value + "]" );
+            log.debug( "Key store size [" + this.size() + "]" );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElement.java
new file mode 100644
index 0000000..7dc89fe
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElement.java
@@ -0,0 +1,157 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+/**
+ * Implementation of cache elements in purgatory.
+ * <p>
+ * Elements are stored in purgatory when they are spooled to the auxiliary cache, but have not yet
+ * been written to disk.
+ */
+public class PurgatoryElement<K, V>
+    extends CacheElement<K, V>
+    implements ICacheElement<K, V>
+{
+    /** Don't change */
+    private static final long serialVersionUID = -8152034342684135628L;
+
+    /** Is the element ready to be spooled? */
+    private boolean spoolable = false;
+
+    /** Wrapped cache Element */
+    private ICacheElement<K, V> cacheElement;
+
+    /**
+     * Constructor for the PurgatoryElement<K, V> object
+     * <p>
+     * @param cacheElement CacheElement
+     */
+    public PurgatoryElement( ICacheElement<K, V> cacheElement )
+    {
+        super(cacheElement.getCacheName(),
+                cacheElement.getKey(), cacheElement.getVal(),
+                cacheElement.getElementAttributes());
+        this.cacheElement = cacheElement;
+    }
+
+    /**
+     * Gets the spoolable property.
+     * <p>
+     * @return The spoolable value
+     */
+    public boolean isSpoolable()
+    {
+        return spoolable;
+    }
+
+    /**
+     * Sets the spoolable property.
+     * <p>
+     * @param spoolable The new spoolable value
+     */
+    public void setSpoolable( boolean spoolable )
+    {
+        this.spoolable = spoolable;
+    }
+
+    /**
+     * Get the wrapped cache element.
+     * <p>
+     * @return ICacheElement
+     */
+    public ICacheElement<K, V> getCacheElement()
+    {
+        return cacheElement;
+    }
+
+    // ------------------------------------------------ interface ICacheElement
+
+    /**
+     * @return cacheElement.getCacheName();
+     * @see ICacheElement#getCacheName
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cacheElement.getCacheName();
+    }
+
+    /**
+     * @return cacheElement.getKey();
+     * @see ICacheElement#getKey
+     */
+    @Override
+    public K getKey()
+    {
+        return cacheElement.getKey();
+    }
+
+    /**
+     * @return cacheElement.getVal();
+     * @see ICacheElement#getVal
+     */
+    @Override
+    public V getVal()
+    {
+        return cacheElement.getVal();
+    }
+
+    /**
+     * @return cacheElement.getElementAttributes();
+     * @see ICacheElement#getElementAttributes
+     */
+    @Override
+    public IElementAttributes getElementAttributes()
+    {
+        return cacheElement.getElementAttributes();
+    }
+
+    /**
+     * @param attr
+     * @see ICacheElement#setElementAttributes
+     */
+    @Override
+    public void setElementAttributes( IElementAttributes attr )
+    {
+        cacheElement.setElementAttributes( attr );
+    }
+
+    /**
+     * @return debug string
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "[PurgatoryElement: " );
+        buf.append( " isSpoolable = " + isSpoolable() );
+        buf.append( " CacheElement = " + getCacheElement() );
+        buf.append( " CacheName = " + getCacheName() );
+        buf.append( " Key = " + getKey() );
+        buf.append( " Value = " + getVal() );
+        buf.append( " ElementAttributes = " + getElementAttributes() );
+        buf.append( "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/behavior/IDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/behavior/IDiskCacheAttributes.java
new file mode 100644
index 0000000..2d10cb6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/behavior/IDiskCacheAttributes.java
@@ -0,0 +1,105 @@
+package org.apache.commons.jcs.auxiliary.disk.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+
+import java.io.File;
+
+/**
+ * Common disk cache attributes.
+ */
+public interface IDiskCacheAttributes
+    extends AuxiliaryCacheAttributes
+{
+    /**
+     * This is the default purgatory size limit. Purgatory is the area where
+     * items to be spooled are temporarily stored. It basically provides access
+     * to items on the to-be-spooled queue.
+     */
+    int MAX_PURGATORY_SIZE_DEFAULT = 5000;
+
+    /**
+     * Sets the diskPath attribute of the IJISPCacheAttributes object
+     * <p>
+     * @param path
+     *            The new diskPath value
+     */
+    void setDiskPath( String path );
+
+    /**
+     * Gets the diskPath attribute of the attributes object
+     * <p>
+     * @return The diskPath value
+     */
+    File getDiskPath();
+
+    /**
+     * Gets the maxKeySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @return The maxPurgatorySize value
+     */
+    int getMaxPurgatorySize();
+
+    /**
+     * Sets the maxPurgatorySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @param maxPurgatorySize
+     *            The new maxPurgatorySize value
+     */
+    void setMaxPurgatorySize( int maxPurgatorySize );
+
+    /**
+     * Get the amount of time in seconds we will wait for elements to move to
+     * disk during shutdown for a particular region.
+     * <p>
+     * @return the time in seconds.
+     */
+    int getShutdownSpoolTimeLimit();
+
+    /**
+     * Sets the amount of time in seconds we will wait for elements to move to
+     * disk during shutdown for a particular region.
+     * <p>
+     * This is how long we give the event queue to empty.
+     * <p>
+     * The default is 60 seconds.
+     * <p>
+     * @param shutdownSpoolTimeLimit
+     *            the time in seconds
+     */
+    void setShutdownSpoolTimeLimit( int shutdownSpoolTimeLimit );
+
+    /**
+     * If this is true then remove all is not prohibited.
+     * <p>
+     * @return boolean
+     */
+    boolean isAllowRemoveAll();
+
+    /**
+     * If this is false, then remove all requests will not be honored.
+     * <p>
+     * This provides a safety mechanism for the persistent store.
+     * <p>
+     * @param allowRemoveAll
+     */
+    void setAllowRemoveAll( boolean allowRemoveAll );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDisk.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDisk.java
new file mode 100644
index 0000000..070826e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDisk.java
@@ -0,0 +1,524 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.jcs.utils.struct.SingleLinkedList;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * This class manages reading an writing data to disk. When asked to write a value, it returns a
+ * block array. It can read an object from the block numbers in a byte array.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDisk
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( BlockDisk.class );
+
+    /** The size of the header that indicates the amount of data stored in an occupied block. */
+    public static final byte HEADER_SIZE_BYTES = 4;
+    // N.B. 4 bytes is the size used for ByteBuffer.putInt(int value) and ByteBuffer.getInt()
+
+    /** defaults to 4kb */
+    private static final int DEFAULT_BLOCK_SIZE_BYTES = 4 * 1024;
+
+    /** Size of the blocks */
+    private final int blockSizeBytes;
+
+    /**
+     * the total number of blocks that have been used. If there are no free, we will use this to
+     * calculate the position of the next block.
+     */
+    private final AtomicInteger numberOfBlocks = new AtomicInteger(0);
+
+    /** Empty blocks that can be reused. */
+    private final SingleLinkedList<Integer> emptyBlocks = new SingleLinkedList<Integer>();
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /** Location of the spot on disk */
+    private final String filepath;
+
+    /** File channel for multiple concurrent reads and writes */
+    private final FileChannel fc;
+
+    /** How many bytes have we put to disk */
+    private final AtomicLong putBytes = new AtomicLong(0);
+
+    /** How many items have we put to disk */
+    private final AtomicLong putCount = new AtomicLong(0);
+
+    /**
+     * Constructor for the Disk object
+     * <p>
+     * @param file
+     * @param elementSerializer
+     * @throws FileNotFoundException
+     */
+    public BlockDisk( File file, IElementSerializer elementSerializer )
+        throws FileNotFoundException
+    {
+        this( file, DEFAULT_BLOCK_SIZE_BYTES, elementSerializer );
+    }
+
+    /**
+     * Creates the file and set the block size in bytes.
+     * <p>
+     * @param file
+     * @param blockSizeBytes
+     * @throws FileNotFoundException
+     */
+    public BlockDisk( File file, int blockSizeBytes )
+        throws FileNotFoundException
+    {
+        this( file, blockSizeBytes, new StandardSerializer() );
+    }
+
+    /**
+     * Creates the file and set the block size in bytes.
+     * <p>
+     * @param file
+     * @param blockSizeBytes
+     * @param elementSerializer
+     * @throws FileNotFoundException
+     */
+    public BlockDisk( File file, int blockSizeBytes, IElementSerializer elementSerializer )
+        throws FileNotFoundException
+    {
+        this.filepath = file.getAbsolutePath();
+        RandomAccessFile raf = new RandomAccessFile( filepath, "rw" );
+        this.fc = raf.getChannel();
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Constructing BlockDisk, blockSizeBytes [" + blockSizeBytes + "]" );
+        }
+
+        this.blockSizeBytes = blockSizeBytes;
+        this.elementSerializer = elementSerializer;
+    }
+
+    /**
+     * Allocate a given number of blocks from the available set
+     *
+     * @param numBlocksNeeded
+     * @return an array of allocated blocks
+     */
+    private int[] allocateBlocks(int numBlocksNeeded)
+    {
+        assert numBlocksNeeded >= 1;
+
+        int[] blocks = new int[numBlocksNeeded];
+        // get them from the empty list or take the next one
+        for (int i = 0; i < numBlocksNeeded; i++)
+        {
+            Integer emptyBlock = emptyBlocks.takeFirst();
+            if (emptyBlock == null)
+            {
+                emptyBlock = Integer.valueOf(numberOfBlocks.getAndIncrement());
+            }
+            blocks[i] = emptyBlock.intValue();
+        }
+
+        return blocks;
+    }
+
+    /**
+     * This writes an object to disk and returns the blocks it was stored in.
+     * <p>
+     * The program flow is as follows:
+     * <ol>
+     * <li>Serialize the object.</li>
+     * <li>Determine the number of blocks needed.</li>
+     * <li>Look for free blocks in the emptyBlock list.</li>
+     * <li>If there were not enough in the empty list. Take the nextBlock and increment it.</li>
+     * <li>If the data will not fit in one block, create sub arrays.</li>
+     * <li>Write the subarrays to disk.</li>
+     * <li>If the process fails we should decrement the block count if we took from it.</li>
+     * </ol>
+     * @param object
+     * @return the blocks we used.
+     * @throws IOException
+     */
+    protected int[] write( Serializable object )
+        throws IOException
+    {
+        // serialize the object
+        byte[] data = elementSerializer.serialize(object);
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "write, total pre-chunking data.length = " + data.length );
+        }
+
+        this.putBytes.addAndGet(data.length);
+        this.putCount.incrementAndGet();
+
+        // figure out how many blocks we need.
+        int numBlocksNeeded = calculateTheNumberOfBlocksNeeded(data);
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "numBlocksNeeded = " + numBlocksNeeded );
+        }
+
+        // allocate blocks
+        int[] blocks = allocateBlocks(numBlocksNeeded);
+
+        int offset = 0;
+        final int maxChunkSize = blockSizeBytes - HEADER_SIZE_BYTES;
+        ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_SIZE_BYTES);
+
+        for (int i = 0; i < numBlocksNeeded; i++)
+        {
+            headerBuffer.clear();
+            int length = Math.min(maxChunkSize, data.length - offset);
+            headerBuffer.putInt(length);
+
+            ByteBuffer dataBuffer = ByteBuffer.wrap(data, offset, length);
+
+            long position = calculateByteOffsetForBlock(blocks[i]);
+            // write the header
+            headerBuffer.flip();
+            int written = fc.write(headerBuffer, position);
+            assert written == HEADER_SIZE_BYTES;
+
+            //write the data
+            written = fc.write(dataBuffer, position + HEADER_SIZE_BYTES);
+            assert written == length;
+
+            offset += length;
+        }
+
+        fc.force(false);
+
+        return blocks;
+    }
+
+    /**
+     * Return the amount to put in each block. Fill them all the way, minus the header.
+     * <p>
+     * @param complete
+     * @param numBlocksNeeded
+     * @return byte[][]
+     */
+    protected byte[][] getBlockChunks( byte[] complete, int numBlocksNeeded )
+    {
+        byte[][] chunks = new byte[numBlocksNeeded][];
+
+        if ( numBlocksNeeded == 1 )
+        {
+            chunks[0] = complete;
+        }
+        else
+        {
+            int maxChunkSize = this.blockSizeBytes - HEADER_SIZE_BYTES;
+            int totalBytes = complete.length;
+            int totalUsed = 0;
+            for ( short i = 0; i < numBlocksNeeded; i++ )
+            {
+                // use the max that can be written to a block or whatever is left in the original
+                // array
+                int chunkSize = Math.min( maxChunkSize, totalBytes - totalUsed );
+                byte[] chunk = new byte[chunkSize];
+                // copy from the used position to the chunk size on the complete array to the chunk
+                // array.
+                System.arraycopy( complete, totalUsed, chunk, 0, chunkSize );
+                chunks[i] = chunk;
+                totalUsed += chunkSize;
+            }
+        }
+
+        return chunks;
+    }
+
+    /**
+     * Reads an object that is located in the specified blocks.
+     * <p>
+     * @param blockNumbers
+     * @return Serializable
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    protected <T extends Serializable> T read( int[] blockNumbers )
+        throws IOException, ClassNotFoundException
+    {
+        byte[] data = null;
+
+        if ( blockNumbers.length == 1 )
+        {
+            data = readBlock( blockNumbers[0] );
+        }
+        else
+        {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream(getBlockSizeBytes());
+            // get all the blocks into data
+            for ( short i = 0; i < blockNumbers.length; i++ )
+            {
+                byte[] chunk = readBlock( blockNumbers[i] );
+                baos.write(chunk);
+            }
+
+            data = baos.toByteArray();
+            baos.close();
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "read, total post combination data.length = " + data.length );
+        }
+
+        return elementSerializer.deSerialize( data, null );
+    }
+
+    /**
+     * This reads the occupied data in a block.
+     * <p>
+     * The first four bytes of the record should tell us how long it is. The data is read into a
+     * byte array and then an object is constructed from the byte array.
+     * <p>
+     * @return byte[]
+     * @param block
+     * @throws IOException
+     */
+    private byte[] readBlock( int block )
+        throws IOException
+    {
+        int datalen = 0;
+
+        String message = null;
+        boolean corrupted = false;
+        long fileLength = fc.size();
+
+        int position = calculateByteOffsetForBlock( block );
+//        if ( position > fileLength )
+//        {
+//            corrupted = true;
+//            message = "Record " + position + " starts past EOF.";
+//        }
+//        else
+        {
+            ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
+            fc.read(datalength, position);
+            datalength.flip();
+            datalen = datalength.getInt();
+            if ( position + datalen > fileLength )
+            {
+                corrupted = true;
+                message = "Record " + position + " exceeds file length.";
+            }
+        }
+
+        if ( corrupted )
+        {
+            log.warn( "\n The file is corrupt: " + "\n " + message );
+            throw new IOException( "The File Is Corrupt, need to reset" );
+        }
+
+        ByteBuffer data = ByteBuffer.allocate(datalen);
+        fc.read(data, position + HEADER_SIZE_BYTES);
+        data.flip();
+
+        return data.array();
+    }
+
+    /**
+     * Add these blocks to the emptyBlock list.
+     * <p>
+     * @param blocksToFree
+     */
+    protected void freeBlocks( int[] blocksToFree )
+    {
+        if ( blocksToFree != null )
+        {
+            for ( short i = 0; i < blocksToFree.length; i++ )
+            {
+                emptyBlocks.addLast( Integer.valueOf( blocksToFree[i] ) );
+            }
+        }
+    }
+
+    /**
+     * Calculates the file offset for a particular block.
+     * <p>
+     * @param block
+     * @return the offset for this block
+     */
+    protected int calculateByteOffsetForBlock( int block )
+    {
+        return block * blockSizeBytes;
+    }
+
+    /**
+     * The number of blocks needed.
+     * <p>
+     * @param data
+     * @return the number of blocks needed to store the byte array
+     */
+    protected int calculateTheNumberOfBlocksNeeded( byte[] data )
+    {
+        int dataLength = data.length;
+
+        int oneBlock = blockSizeBytes - HEADER_SIZE_BYTES;
+
+        // takes care of 0 = HEADER_SIZE_BYTES + blockSizeBytes
+        if ( dataLength <= oneBlock )
+        {
+            return 1;
+        }
+
+        int dividend = dataLength / oneBlock;
+
+        if ( dataLength % oneBlock != 0 )
+        {
+            dividend++;
+        }
+        return dividend;
+    }
+
+    /**
+     * Returns the file length.
+     * <p>
+     * @return the size of the file.
+     * @throws IOException
+     */
+    protected long length()
+        throws IOException
+    {
+        return fc.size();
+    }
+
+    /**
+     * Closes the file.
+     * <p>
+     * @throws IOException
+     */
+    protected void close()
+        throws IOException
+    {
+        fc.close();
+    }
+
+    /**
+     * Resets the file.
+     * <p>
+     * @throws IOException
+     */
+    protected synchronized void reset()
+        throws IOException
+    {
+        this.numberOfBlocks.set(0);
+        this.emptyBlocks.clear();
+        fc.truncate(0);
+        fc.force(true);
+    }
+
+    /**
+     * @return Returns the numberOfBlocks.
+     */
+    protected int getNumberOfBlocks()
+    {
+        return numberOfBlocks.get();
+    }
+
+    /**
+     * @return Returns the blockSizeBytes.
+     */
+    protected int getBlockSizeBytes()
+    {
+        return blockSizeBytes;
+    }
+
+    /**
+     * @return Returns the average size of the an element inserted.
+     */
+    protected long getAveragePutSizeBytes()
+    {
+        long count = this.putCount.get();
+
+        if (count == 0 )
+        {
+            return 0;
+        }
+        return this.putBytes.get() / count;
+    }
+
+    /**
+     * @return Returns the number of empty blocks.
+     */
+    protected int getEmptyBlocks()
+    {
+        return this.emptyBlocks.size();
+    }
+
+    /**
+     * For debugging only.
+     * <p>
+     * @return String with details.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nBlock Disk " );
+        buf.append( "\n  Filepath [" + filepath + "]" );
+        buf.append( "\n  NumberOfBlocks [" + this.numberOfBlocks.get() + "]" );
+        buf.append( "\n  BlockSizeBytes [" + this.blockSizeBytes + "]" );
+        buf.append( "\n  Put Bytes [" + this.putBytes + "]" );
+        buf.append( "\n  Put Count [" + this.putCount + "]" );
+        buf.append( "\n  Average Size [" + getAveragePutSizeBytes() + "]" );
+        buf.append( "\n  Empty Blocks [" + this.getEmptyBlocks() + "]" );
+        try
+        {
+            buf.append( "\n  Length [" + length() + "]" );
+        }
+        catch ( IOException e )
+        {
+            // swallow
+        }
+        return buf.toString();
+    }
+
+    /**
+     * This is used for debugging.
+     * <p>
+     * @return the file path.
+     */
+    protected String getFilePath()
+    {
+        return filepath;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
new file mode 100644
index 0000000..865ed2f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
@@ -0,0 +1,738 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache;
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * There is one BlockDiskCache per region. It manages the key and data store.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskCache<K, V>
+    extends AbstractDiskCache<K, V>
+    implements IRequireScheduler
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( BlockDiskCache.class );
+
+    /** The name to prefix all log messages with. */
+    private final String logCacheName;
+
+    /** The name of the file to store data. */
+    private final String fileName;
+
+    /** The data access object */
+    private BlockDisk dataFile;
+
+    /** Attributes governing the behavior of the block disk cache. */
+    private final BlockDiskCacheAttributes blockDiskCacheAttributes;
+
+    /** The root directory for keys and data. */
+    private final File rootDirectory;
+
+    /** Store, loads, and persists the keys */
+    private BlockDiskKeyStore<K> keyStore;
+
+    /**
+     * Use this lock to synchronize reads and writes to the underlying storage mechanism. We don't
+     * need a reentrant lock, since we only lock one level.
+     */
+    private final ReentrantReadWriteLock storageLock = new ReentrantReadWriteLock();
+
+    /**
+     * Constructs the BlockDisk after setting up the root directory.
+     * <p>
+     * @param cacheAttributes
+     */
+    public BlockDiskCache( BlockDiskCacheAttributes cacheAttributes )
+    {
+        this( cacheAttributes, null );
+    }
+
+    /**
+     * Constructs the BlockDisk after setting up the root directory.
+     * <p>
+     * @param cacheAttributes
+     * @param elementSerializer used if supplied, the super's super will not set a null
+     */
+    public BlockDiskCache( BlockDiskCacheAttributes cacheAttributes, IElementSerializer elementSerializer )
+    {
+        super( cacheAttributes );
+        setElementSerializer( elementSerializer );
+
+        this.blockDiskCacheAttributes = cacheAttributes;
+        this.logCacheName = "Region [" + getCacheName() + "] ";
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Constructing BlockDiskCache with attributes " + cacheAttributes );
+        }
+
+        // Make a clean file name
+        this.fileName = getCacheName().replaceAll("[^a-zA-Z0-9-_\\.]", "_");
+        this.rootDirectory = cacheAttributes.getDiskPath();
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Cache file root directory: [" + rootDirectory + "]");
+        }
+
+        try
+        {
+            if ( this.blockDiskCacheAttributes.getBlockSizeBytes() > 0 )
+            {
+                this.dataFile = new BlockDisk( new File( rootDirectory, fileName + ".data" ),
+                                               this.blockDiskCacheAttributes.getBlockSizeBytes() );
+            }
+            else
+            {
+                this.dataFile = new BlockDisk( new File( rootDirectory, fileName + ".data" ), getElementSerializer() );
+            }
+
+            keyStore = new BlockDiskKeyStore<K>( this.blockDiskCacheAttributes, this );
+
+            boolean alright = verifyDisk();
+
+            if ( keyStore.size() == 0 || !alright )
+            {
+                this.reset();
+            }
+
+            // Initialization finished successfully, so set alive to true.
+            alive = true;
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Block Disk Cache is alive." );
+            }
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Failure initializing for fileName: " + fileName + " and root directory: "
+                + rootDirectory, e );
+        }
+    }
+
+    /**
+     * @see org.apache.commons.jcs.engine.behavior.IRequireScheduler#setScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
+     */
+    @Override
+    public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutor)
+    {
+        // add this region to the persistence thread.
+        // TODO we might need to stagger this a bit.
+        if ( this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds() > 0 )
+        {
+            scheduledExecutor.scheduleAtFixedRate(
+                    new Runnable()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            keyStore.saveKeys();
+                        }
+                    },
+                    this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds(),
+                    this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds(),
+                    TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * We need to verify that the file on disk uses the same block size and that the file is the
+     * proper size.
+     * <p>
+     * @return true if it looks ok
+     */
+    protected boolean verifyDisk()
+    {
+        boolean alright = false;
+        // simply try to read a few. If it works, then the file is probably ok.
+        // TODO add more.
+
+        storageLock.readLock().lock();
+
+        try
+        {
+            int maxToTest = 100;
+            int count = 0;
+            Iterator<Map.Entry<K, int[]>> it = this.keyStore.entrySet().iterator();
+            while ( it.hasNext() && count < maxToTest )
+            {
+                count++;
+                Map.Entry<K, int[]> entry = it.next();
+                Object data = this.dataFile.read( entry.getValue() );
+                if ( data == null )
+                {
+                    throw new Exception( logCacheName + "Couldn't find data for key [" + entry.getKey() + "]" );
+                }
+            }
+            alright = true;
+        }
+        catch ( Exception e )
+        {
+            log.warn( logCacheName + "Problem verifying disk.  Message [" + e.getMessage() + "]" );
+            alright = false;
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        return alright;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        HashSet<K> keys = new HashSet<K>();
+
+        storageLock.readLock().lock();
+
+        try
+        {
+            keys.addAll(this.keyStore.keySet());
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        return keys;
+    }
+
+    /**
+     * Gets matching items from the cache.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        Set<K> keyArray = null;
+        storageLock.readLock().lock();
+        try
+        {
+            keyArray = new HashSet<K>(keyStore.keySet());
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray );
+
+        for (K key : matchingKeys)
+        {
+            ICacheElement<K, V> element = processGet( key );
+            if ( element != null )
+            {
+                elements.put( key, element );
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Returns the number of keys.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getSize()
+     */
+    @Override
+    public int getSize()
+    {
+        return this.keyStore.size();
+    }
+
+    /**
+     * Gets the ICacheElement<K, V> for the key if it is in the cache. The program flow is as follows:
+     * <ol>
+     * <li>Make sure the disk cache is alive.</li> <li>Get a read lock.</li> <li>See if the key is
+     * in the key store.</li> <li>If we found a key, ask the BlockDisk for the object at the
+     * blocks..</li> <li>Release the lock.</li>
+     * </ol>
+     * (non-Javadoc)
+     * @param key
+     * @return ICacheElement
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#doGet(java.io.Serializable)
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+    {
+        if ( !alive )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "No longer alive so returning null for key = " + key );
+            }
+            return null;
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Trying to get from disk: " + key );
+        }
+
+        ICacheElement<K, V> object = null;
+        storageLock.readLock().lock();
+
+        try
+        {
+            int[] ded = this.keyStore.get( key );
+            if ( ded != null )
+            {
+                object = this.dataFile.read( ded );
+            }
+        }
+        catch ( IOException ioe )
+        {
+            log.error( logCacheName + "Failure getting from disk--IOException, key = " + key, ioe );
+            reset();
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Failure getting from disk, key = " + key, e );
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        return object;
+    }
+
+    /**
+     * Writes an element to disk. The program flow is as follows:
+     * <ol>
+     * <li>Acquire write lock.</li> <li>See id an item exists for this key.</li> <li>If an item
+     * already exists, add its blocks to the remove list.</li> <li>Have the Block disk write the
+     * item.</li> <li>Create a descriptor and add it to the key map.</li> <li>Release the write
+     * lock.</li>
+     * </ol>
+     * @param element
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#doUpdate(org.apache.commons.jcs.engine.behavior.ICacheElement)
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> element )
+    {
+        if ( !alive )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "No longer alive; aborting put of key = " + element.getKey() );
+            }
+            return;
+        }
+
+        int[] old = null;
+
+        // make sure this only locks for one particular cache region
+        storageLock.writeLock().lock();
+
+        try
+        {
+            old = this.keyStore.get( element.getKey() );
+
+            if ( old != null )
+            {
+                this.dataFile.freeBlocks( old );
+            }
+
+            int[] blocks = this.dataFile.write( element );
+
+            this.keyStore.put( element.getKey(), blocks );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Put to file [" + fileName + "] key [" + element.getKey() + "]" );
+            }
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Failure updating element, key: " + element.getKey() + " old: " + Arrays.toString(old), e );
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Storing element on disk, key: " + element.getKey() );
+        }
+    }
+
+    /**
+     * Returns true if the removal was successful; or false if there is nothing to remove. Current
+     * implementation always result in a disk orphan.
+     * <p>
+     * (non-Javadoc)
+     * @param key
+     * @return true if removed anything
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#doRemove(java.io.Serializable)
+     */
+    @Override
+    protected boolean processRemove( K key )
+    {
+        if ( !alive )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "No longer alive so returning false for key = " + key );
+            }
+            return false;
+        }
+
+        boolean reset = false;
+        boolean removed = false;
+
+        storageLock.writeLock().lock();
+
+        try
+        {
+            if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+            {
+                // remove all keys of the same name group.
+                Iterator<Map.Entry<K, int[]>> iter = this.keyStore.entrySet().iterator();
+
+                while ( iter.hasNext() )
+                {
+                    Map.Entry<K, int[]> entry = iter.next();
+                    K k = entry.getKey();
+
+                    if ( k instanceof String && k.toString().startsWith( key.toString() ) )
+                    {
+                        int[] ded = this.keyStore.get( key );
+                        this.dataFile.freeBlocks( ded );
+                        iter.remove();
+                        removed = true;
+                        // TODO this needs to update the remove count separately
+                    }
+                }
+            }
+            else if ( key instanceof GroupAttrName && ((GroupAttrName<?>)key).attrName == null )
+            {
+                // remove all keys of the same name hierarchy.
+                Iterator<Map.Entry<K, int[]>> iter = this.keyStore.entrySet().iterator();
+                while ( iter.hasNext() )
+                {
+                    Map.Entry<K, int[]> entry = iter.next();
+                    K k = entry.getKey();
+
+                    if ( k instanceof GroupAttrName &&
+                        ((GroupAttrName<?>)k).groupId.equals(((GroupAttrName<?>)key).groupId))
+                    {
+                        int[] ded = this.keyStore.get( key );
+                        this.dataFile.freeBlocks( ded );
+                        iter.remove();
+                        removed = true;
+                    }
+                }
+            }
+            else
+            {
+                // remove single item.
+                int[] ded = this.keyStore.remove( key );
+                removed = ded != null;
+                if ( removed )
+                {
+                    this.dataFile.freeBlocks( ded );
+                }
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( logCacheName + "Disk removal: Removed from key hash, key [" + key + "] removed = "
+                        + removed );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem removing element.", e );
+            reset = true;
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+
+        if ( reset )
+        {
+            reset();
+        }
+
+        return removed;
+    }
+
+    /**
+     * Resets the keyfile, the disk file, and the memory key map.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#doRemoveAll()
+     */
+    @Override
+    protected void processRemoveAll()
+    {
+        reset();
+    }
+
+    /**
+     * Dispose of the disk cache in a background thread. Joins against this thread to put a cap on
+     * the disposal time.
+     * <p>
+     * TODO make dispose window configurable.
+     */
+    @Override
+    public void processDispose()
+    {
+        Runnable disR = new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    disposeInternal();
+                }
+                catch ( InterruptedException e )
+                {
+                    log.warn( "Interrupted while diposing." );
+                }
+            }
+        };
+        Thread t = new Thread( disR, "BlockDiskCache-DisposalThread" );
+        t.start();
+        // wait up to 60 seconds for dispose and then quit if not done.
+        try
+        {
+            t.join( 60 * 1000 );
+        }
+        catch ( InterruptedException ex )
+        {
+            log.error( logCacheName + "Interrupted while waiting for disposal thread to finish.", ex );
+        }
+    }
+
+    /**
+     * Internal method that handles the disposal.
+     * @throws InterruptedException
+     */
+    protected void disposeInternal()
+        throws InterruptedException
+    {
+        if ( !alive )
+        {
+            log.error( logCacheName + "Not alive and dispose was called, filename: " + fileName );
+            return;
+        }
+        storageLock.writeLock().lock();
+        try
+        {
+            // Prevents any interaction with the cache while we're shutting down.
+            alive = false;
+
+            this.keyStore.saveKeys();
+
+            try
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( logCacheName + "Closing files, base filename: " + fileName );
+                }
+                dataFile.close();
+                // dataFile = null;
+
+                // TOD make a close
+                // keyFile.close();
+                // keyFile = null;
+            }
+            catch ( IOException e )
+            {
+                log.error( logCacheName + "Failure closing files in dispose, filename: " + fileName, e );
+            }
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Shutdown complete." );
+        }
+    }
+
+    /**
+     * Returns the attributes.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getAuxiliaryCacheAttributes()
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return this.blockDiskCacheAttributes;
+    }
+
+    /**
+     * Reset effectively clears the disk cache, creating new files, recyclebins, and keymaps.
+     * <p>
+     * It can be used to handle errors by last resort, force content update, or removeall.
+     */
+    private void reset()
+    {
+        if ( log.isWarnEnabled() )
+        {
+            log.warn( logCacheName + "Resetting cache" );
+        }
+
+        try
+        {
+            storageLock.writeLock().lock();
+
+            this.keyStore.reset();
+
+            if ( dataFile != null )
+            {
+                dataFile.reset();
+            }
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Failure resetting state", e );
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Add these blocks to the emptyBlock list.
+     * <p>
+     * @param blocksToFree
+     */
+    protected void freeBlocks( int[] blocksToFree )
+    {
+        this.dataFile.freeBlocks( blocksToFree );
+    }
+
+    /**
+     * Gets basic stats for the disk cache.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * Returns info about the disk cache.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getStatistics()
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Block Disk Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Boolean>( "Is Alive", Boolean.valueOf(alive) ) );
+        elems.add(new StatElement<Integer>( "Key Map Size", Integer.valueOf(this.keyStore.size()) ) );
+
+        try
+        {
+            elems.add(new StatElement<Long>( "Data File Length",
+                    Long.valueOf(this.dataFile != null ? this.dataFile.length() : -1L) ) );
+        }
+        catch ( IOException e )
+        {
+            log.error( e );
+        }
+
+        elems.add(new StatElement<Integer>( "Block Size Bytes",
+                Integer.valueOf(this.dataFile.getBlockSizeBytes()) ) );
+        elems.add(new StatElement<Integer>( "Number Of Blocks",
+                Integer.valueOf(this.dataFile.getNumberOfBlocks()) ) );
+        elems.add(new StatElement<Long>( "Average Put Size Bytes",
+                Long.valueOf(this.dataFile.getAveragePutSizeBytes()) ) );
+        elems.add(new StatElement<Integer>( "Empty Blocks",
+                Integer.valueOf(this.dataFile.getEmptyBlocks()) ) );
+
+        // get the stats from the super too
+        IStats sStats = super.getStatistics();
+        elems.addAll(sStats.getStatElements());
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * This is used by the event logging.
+     * <p>
+     * @return the location of the disk, either path or ip.
+     */
+    @Override
+    protected String getDiskLocation()
+    {
+        return dataFile.getFilePath();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheAttributes.java
new file mode 100644
index 0000000..c3f742e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheAttributes.java
@@ -0,0 +1,117 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheAttributes;
+
+/**
+ * This holds attributes for Block Disk Cache configuration.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskCacheAttributes
+    extends AbstractDiskCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = 6568840097657265989L;
+
+    /** The size per block in bytes. */
+    private int blockSizeBytes;
+
+    /** Maximum number of keys to be kept in memory */
+    private static final int DEFAULT_MAX_KEY_SIZE = 5000;
+
+    /** -1 means no limit. */
+    private int maxKeySize = DEFAULT_MAX_KEY_SIZE;
+
+    /** How often should we persist the keys. */
+    private static final long DEFAULT_KEY_PERSISTENCE_INTERVAL_SECONDS = 5 * 60;
+
+    /** The keys will be persisted at this interval.  -1 mean never. */
+    private long keyPersistenceIntervalSeconds = DEFAULT_KEY_PERSISTENCE_INTERVAL_SECONDS;
+
+    /**
+     * The size of the blocks. All blocks are the same size.
+     * <p>
+     * @param blockSizeBytes The blockSizeBytes to set.
+     */
+    public void setBlockSizeBytes( int blockSizeBytes )
+    {
+        this.blockSizeBytes = blockSizeBytes;
+    }
+
+    /**
+     * @return Returns the blockSizeBytes.
+     */
+    public int getBlockSizeBytes()
+    {
+        return blockSizeBytes;
+    }
+
+    /**
+     * @param maxKeySize The maxKeySize to set.
+     */
+    public void setMaxKeySize( int maxKeySize )
+    {
+        this.maxKeySize = maxKeySize;
+    }
+
+    /**
+     * @return Returns the maxKeySize.
+     */
+    public int getMaxKeySize()
+    {
+        return maxKeySize;
+    }
+
+    /**
+     * @param keyPersistenceIntervalSeconds The keyPersistenceIntervalSeconds to set.
+     */
+    public void setKeyPersistenceIntervalSeconds( long keyPersistenceIntervalSeconds )
+    {
+        this.keyPersistenceIntervalSeconds = keyPersistenceIntervalSeconds;
+    }
+
+    /**
+     * @return Returns the keyPersistenceIntervalSeconds.
+     */
+    public long getKeyPersistenceIntervalSeconds()
+    {
+        return keyPersistenceIntervalSeconds;
+    }
+
+    /**
+     * Write out the values for debugging purposes.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append( "\nBlockDiskAttributes " );
+        str.append( "\n DiskPath [" + this.getDiskPath() + "]" );
+        str.append( "\n MaxKeySize [" + this.getMaxKeySize() + "]" );
+        str.append( "\n MaxPurgatorySize [" + this.getMaxPurgatorySize() + "]" );
+        str.append( "\n BlockSizeBytes [" + this.getBlockSizeBytes() + "]" );
+        str.append( "\n KeyPersistenceIntervalSeconds [" + this.getKeyPersistenceIntervalSeconds() + "]" );
+        return str.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheFactory.java
new file mode 100644
index 0000000..0a6399a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheFactory.java
@@ -0,0 +1,93 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Creates disk cache instances.
+ */
+public class BlockDiskCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( BlockDiskCacheFactory.class );
+
+    /** The auxiliary name. The composite cache manager keeps this in a map, keyed by name. */
+    private String name;
+
+    /**
+     * Get an instance of the BlockDiskCacheManager for the attributes and then get an
+     * IndexedDiskCache from the manager.
+     * <p>
+     * The manager is a singleton.
+     * <p>
+     * One disk cache is returned per region from the manager.
+     * <p>
+     * @param iaca
+     * @param cacheMgr This allows auxiliaries to reference the manager without assuming that it is
+     *            a singleton. This will allow JCS to be a non-singleton. Also, it makes it easier
+     *            to test.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
+                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        BlockDiskCacheAttributes idca = (BlockDiskCacheAttributes) iaca;
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Creating DiskCache for attributes = " + idca );
+        }
+        BlockDiskCacheManager dcm = BlockDiskCacheManager.getInstance( idca, cacheEventLogger, elementSerializer );
+        return dcm.getCache( idca );
+    }
+
+    /**
+     * Gets the name attribute of the DiskCacheFactory object
+     * <p>
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * Sets the name attribute of the DiskCacheFactory object
+     * <p>
+     * @param name The new name value
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManager.java
new file mode 100644
index 0000000..c6da28e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManager.java
@@ -0,0 +1,141 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Cache manager for BlockDiskCaches. This manages the instances of the disk cache.
+ */
+public class BlockDiskCacheManager
+    extends AbstractDiskCacheManager
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( BlockDiskCacheManager.class );
+
+    /** The singleton instance */
+    private static BlockDiskCacheManager instance;
+
+    /** block disks for a region. */
+    private final Map<String, BlockDiskCache<?, ?>> caches =
+        new ConcurrentHashMap<String, BlockDiskCache<?, ?>>();
+
+    /** Attributes. */
+    private final BlockDiskCacheAttributes defaultCacheAttributes;
+
+    /**
+     * Constructor for the BlockDiskCacheManager object
+     * <p>
+     * @param defaultCacheAttributes Default attributes for caches managed by the instance.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private BlockDiskCacheManager( BlockDiskCacheAttributes defaultCacheAttributes, ICacheEventLogger cacheEventLogger,
+                                   IElementSerializer elementSerializer )
+    {
+        this.defaultCacheAttributes = defaultCacheAttributes;
+        setElementSerializer( elementSerializer );
+        setCacheEventLogger( cacheEventLogger );
+    }
+
+    /**
+     * Gets the singleton instance of the manager
+     * <p>
+     * @param defaultCacheAttributes If the instance has not yet been created, it will be
+     *            initialized with this set of default attributes.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public static BlockDiskCacheManager getInstance( BlockDiskCacheAttributes defaultCacheAttributes,
+                                                     ICacheEventLogger cacheEventLogger,
+                                                     IElementSerializer elementSerializer )
+    {
+        synchronized ( BlockDiskCacheManager.class )
+        {
+            if ( instance == null )
+            {
+                instance = new BlockDiskCacheManager( defaultCacheAttributes, cacheEventLogger, elementSerializer );
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * Gets an BlockDiskCache for the supplied name using the default attributes.
+     * <p>
+     * @param cacheName Name that will be used when creating attributes.
+     * @return A cache.
+     */
+    @Override
+    public <K, V> BlockDiskCache<K, V> getCache( String cacheName )
+    {
+        BlockDiskCacheAttributes cacheAttributes = (BlockDiskCacheAttributes) defaultCacheAttributes.copy();
+
+        cacheAttributes.setCacheName( cacheName );
+
+        return getCache( cacheAttributes );
+    }
+
+    /**
+     * Get an BlockDiskCache for the supplied attributes. Will provide an existing cache for the
+     * name attribute if one has been created, or will create a new cache.
+     * <p>
+     * @param cacheAttributes Attributes the cache should have.
+     * @return A cache, either from the existing set or newly created.
+     */
+    public <K, V> BlockDiskCache<K, V> getCache( BlockDiskCacheAttributes cacheAttributes )
+    {
+        BlockDiskCache<K, V> cache = null;
+
+        String cacheName = cacheAttributes.getCacheName();
+
+        log.debug( "Getting cache named: " + cacheName );
+
+        synchronized ( caches )
+        {
+            // Try to load the cache from the set that have already been
+            // created. This only looks at the name attribute.
+
+            @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+            BlockDiskCache<K, V> blockDiskCache = (BlockDiskCache<K, V>) caches.get( cacheName );
+            cache = blockDiskCache;
+
+            // If it was not found, create a new one using the supplied
+            // attributes
+            if ( cache == null )
+            {
+                cache = new BlockDiskCache<K, V>( cacheAttributes );
+                cache.setCacheEventLogger( getCacheEventLogger() );
+                cache.setElementSerializer( getElementSerializer() );
+                caches.put( cacheName, cache );
+            }
+        }
+
+        return cache;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
new file mode 100644
index 0000000..73fd657
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
@@ -0,0 +1,132 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+
+/**
+ * This represents an element on disk. This is used when we persist the keys. We only store the
+ * block addresses in memory. We don't need the length here, since all the blocks are the same size
+ * receyle bin.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskElementDescriptor<K>
+    implements Serializable, Externalizable
+{
+    /** Don't change */
+    private static final long serialVersionUID = -1400659301208101411L;
+
+    /** The key */
+    private K key;
+
+    /** The array of block numbers */
+    private int[] blocks;
+
+    /**
+     * @param key The key to set.
+     */
+    public void setKey( K key )
+    {
+        this.key = key;
+    }
+
+    /**
+     * @return Returns the key.
+     */
+    public K getKey()
+    {
+        return key;
+    }
+
+    /**
+     * @param blocks The blocks to set.
+     */
+    public void setBlocks( int[] blocks )
+    {
+        this.blocks = blocks;
+    }
+
+    /**
+     * This holds the block numbers. An item my be dispersed between multiple blocks.
+     * <p>
+     * @return Returns the blocks.
+     */
+    public int[] getBlocks()
+    {
+        return blocks;
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @return Info on the descriptor.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nBlockDiskElementDescriptor" );
+        buf.append( "\n key [" + this.getKey() + "]" );
+        buf.append( "\n blocks [" );
+        if ( this.getBlocks() != null )
+        {
+            for ( int i = 0; i < blocks.length; i++ )
+            {
+                buf.append( this.getBlocks()[i] );
+            }
+        }
+        buf.append( "]" );
+        return buf.toString();
+    }
+
+    /**
+     * Saves on reflection.
+     * <p>
+     * (non-Javadoc)
+     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Need cast to K
+    public void readExternal( ObjectInput input )
+        throws IOException, ClassNotFoundException
+    {
+        this.key = (K) input.readObject();
+        this.blocks = (int[]) input.readObject();
+    }
+
+    /**
+     * Saves on reflection.
+     * <p>
+     * (non-Javadoc)
+     * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+     */
+    @Override
+    public void writeExternal( ObjectOutput output )
+        throws IOException
+    {
+        output.writeObject( this.key );
+        output.writeObject( this.blocks );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskKeyStore.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
new file mode 100644
index 0000000..d597d61
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
@@ -0,0 +1,387 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.LRUMapJCS;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is responsible for storing the keys.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskKeyStore<K>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( BlockDiskKeyStore.class );
+
+    /** Attributes governing the behavior of the block disk cache. */
+    private final BlockDiskCacheAttributes blockDiskCacheAttributes;
+
+    /** The key to block map */
+    private Map<K, int[]> keyHash;
+
+    /** The file where we persist the keys */
+    private final File keyFile;
+
+    /** The name to prefix log messages with. */
+    protected final String logCacheName;
+
+    /** Name of the file where we persist the keys */
+    private final String fileName;
+
+    /** The maximum number of keys to store in memory */
+    private final int maxKeySize;
+
+    /** we need this so we can communicate free blocks to the data store when keys fall off the LRU */
+    protected final BlockDiskCache<K, ?> blockDiskCache;
+
+    /**
+     * Set the configuration options.
+     * <p>
+     * @param cacheAttributes
+     * @param blockDiskCache used for freeing
+     */
+    public BlockDiskKeyStore( BlockDiskCacheAttributes cacheAttributes,
+            BlockDiskCache<K, ?> blockDiskCache)
+    {
+        this.blockDiskCacheAttributes = cacheAttributes;
+        this.logCacheName = "Region [" + this.blockDiskCacheAttributes.getCacheName() + "] ";
+        this.fileName = this.blockDiskCacheAttributes.getCacheName();
+        this.maxKeySize = cacheAttributes.getMaxKeySize();
+        this.blockDiskCache = blockDiskCache;
+
+        File rootDirectory = cacheAttributes.getDiskPath();
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Cache file root directory [" + rootDirectory + "]" );
+        }
+
+        this.keyFile = new File( rootDirectory, fileName + ".key" );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Key File [" + this.keyFile.getAbsolutePath() + "]" );
+        }
+
+        if ( keyFile.length() > 0 )
+        {
+            loadKeys();
+            // TODO verify somehow
+        }
+        else
+        {
+            initKeyMap();
+        }
+    }
+
+    /**
+     * Saves key file to disk. This gets the LRUMap entry set and write the entries out one by one
+     * after putting them in a wrapper.
+     */
+    protected void saveKeys()
+    {
+        try
+        {
+            ElapsedTimer timer = new ElapsedTimer();
+            int numKeys = keyHash.size();
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Saving keys to [" + this.keyFile.getAbsolutePath() + "], key count ["
+                    + numKeys + "]" );
+            }
+
+            synchronized (keyFile)
+            {
+                FileOutputStream fos = new FileOutputStream( keyFile );
+                BufferedOutputStream bos = new BufferedOutputStream( fos, 65536 );
+                ObjectOutputStream oos = new ObjectOutputStream( bos );
+                try
+                {
+                    // don't need to synchronize, since the underlying collection makes a copy
+                    for (Map.Entry<K, int[]> entry : keyHash.entrySet())
+                    {
+                        BlockDiskElementDescriptor<K> descriptor = new BlockDiskElementDescriptor<K>();
+                        descriptor.setKey( entry.getKey() );
+                        descriptor.setBlocks( entry.getValue() );
+                        // stream these out in the loop.
+                        oos.writeObject( descriptor );
+                    }
+                }
+                finally
+                {
+                    oos.flush();
+                    oos.close();
+                }
+            }
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Finished saving keys. It took " + timer.getElapsedTimeString() + " to store "
+                    + numKeys + " keys.  Key file length [" + keyFile.length() + "]" );
+            }
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Problem storing keys.", e );
+        }
+    }
+
+    /**
+     * Resets the file and creates a new key map.
+     */
+    protected void reset()
+    {
+        synchronized (keyFile)
+        {
+            clearMemoryMap();
+            saveKeys();
+        }
+
+    }
+
+    /**
+     * This is mainly used for testing. It leave the disk in tact, and just clears memory.
+     */
+    protected void clearMemoryMap()
+    {
+        this.keyHash.clear();
+    }
+
+    /**
+     * Create the map for keys that contain the index position on disk.
+     */
+    private void initKeyMap()
+    {
+        keyHash = null;
+        if ( maxKeySize >= 0 )
+        {
+            keyHash = new LRUMap( maxKeySize );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Set maxKeySize to: '" + maxKeySize + "'" );
+            }
+        }
+        else
+        {
+            // If no max size, use a plain map for memory and processing efficiency.
+            keyHash = new HashMap<K, int[]>();
+            // keyHash = Collections.synchronizedMap( new HashMap() );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Set maxKeySize to unlimited'" );
+            }
+        }
+    }
+
+    /**
+     * Loads the keys from the .key file. The keys are stored individually on disk. They are added
+     * one by one to an LRUMap..
+     */
+    protected void loadKeys()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Loading keys for " + keyFile.toString() );
+        }
+
+        try
+        {
+            // create a key map to use.
+            initKeyMap();
+
+            HashMap<K, int[]> keys = new HashMap<K, int[]>();
+
+            synchronized (keyFile)
+            {
+                FileInputStream fis = new FileInputStream( keyFile );
+                BufferedInputStream bis = new BufferedInputStream( fis );
+                ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( bis , null);
+                try
+                {
+                    while ( true )
+                    {
+                        @SuppressWarnings("unchecked") // Need to cast from Object
+                        BlockDiskElementDescriptor<K> descriptor = (BlockDiskElementDescriptor<K>) ois.readObject();
+                        if ( descriptor != null )
+                        {
+                            keys.put( descriptor.getKey(), descriptor.getBlocks() );
+                        }
+                    }
+                }
+                catch ( EOFException eof )
+                {
+                    // nothing
+                }
+                finally
+                {
+                    ois.close();
+                }
+            }
+
+            if ( !keys.isEmpty() )
+            {
+                keyHash.putAll( keys );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( logCacheName + "Found " + keys.size() + " in keys file." );
+                }
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( logCacheName + "Loaded keys from [" + fileName + "], key count: " + keyHash.size()
+                        + "; up to " + maxKeySize + " will be available." );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem loading keys for file " + fileName, e );
+        }
+    }
+
+    /**
+     * Gets the entry set.
+     * <p>
+     * @return entry set.
+     */
+    public Set<Map.Entry<K, int[]>> entrySet()
+    {
+        return this.keyHash.entrySet();
+    }
+
+    /**
+     * Gets the key set.
+     * <p>
+     * @return key set.
+     */
+    public Set<K> keySet()
+    {
+        return this.keyHash.keySet();
+    }
+
+    /**
+     * Gets the size of the key hash.
+     * <p>
+     * @return the number of keys.
+     */
+    public int size()
+    {
+        return this.keyHash.size();
+    }
+
+    /**
+     * gets the object for the key.
+     * <p>
+     * @param key
+     * @return Object
+     */
+    public int[] get( K key )
+    {
+        return this.keyHash.get( key );
+    }
+
+    /**
+     * Puts a int[] in the keyStore.
+     * <p>
+     * @param key
+     * @param value
+     */
+    public void put( K key, int[] value )
+    {
+        this.keyHash.put( key, value );
+    }
+
+    /**
+     * Remove by key.
+     * <p>
+     * @param key
+     * @return BlockDiskElementDescriptor if it was present, else null
+     */
+    public int[] remove( K key )
+    {
+        return this.keyHash.remove( key );
+    }
+
+    /**
+     * Class for recycling and lru. This implements the LRU overflow callback, so we can mark the
+     * blocks as free.
+     */
+    public class LRUMap
+        extends LRUMapJCS<K, int[]>
+    {
+        /**
+         * <code>tag</code> tells us which map we are working on.
+         */
+        public String tag = "orig";
+
+        /**
+         * Default
+         */
+        public LRUMap()
+        {
+            super();
+        }
+
+        /**
+         * @param maxKeySize
+         */
+        public LRUMap( int maxKeySize )
+        {
+            super( maxKeySize );
+        }
+
+        /**
+         * This is called when the may key size is reached. The least recently used item will be
+         * passed here. We will store the position and size of the spot on disk in the recycle bin.
+         * <p>
+         * @param key
+         * @param value
+         */
+        @Override
+        protected void processRemovedLRU( K key, int[] value )
+        {
+            blockDiskCache.freeBlocks( value );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Removing key: [" + key + "] from key store." );
+                log.debug( logCacheName + "Key store size: [" + super.size() + "]." );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDisk.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDisk.java
new file mode 100644
index 0000000..f7c1fec
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDisk.java
@@ -0,0 +1,281 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/** Provides thread safe access to the underlying random access file. */
+class IndexedDisk
+{
+    /** The size of the header that indicates the amount of data stored in an occupied block. */
+    public static final byte HEADER_SIZE_BYTES = 4;
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( IndexedDisk.class );
+
+    /** The path to the log directory. */
+    private final String filepath;
+
+    /** The data file. */
+    private final FileChannel fc;
+
+    /**
+     * Constructor for the Disk object
+     * <p>
+     * @param file
+     * @param elementSerializer
+     * @throws FileNotFoundException
+     */
+    public IndexedDisk( File file, IElementSerializer elementSerializer )
+        throws FileNotFoundException
+    {
+        this.filepath = file.getAbsolutePath();
+        this.elementSerializer = elementSerializer;
+        RandomAccessFile raf = new RandomAccessFile( filepath, "rw" );
+        this.fc = raf.getChannel();
+    }
+
+    /**
+     * This reads an object from the given starting position on the file.
+     * <p>
+     * The first four bytes of the record should tell us how long it is. The data is read into a byte
+     * array and then an object is constructed from the byte array.
+     * <p>
+     * @return Serializable
+     * @param ded
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    protected <T extends Serializable> T readObject( IndexedDiskElementDescriptor ded )
+        throws IOException, ClassNotFoundException
+    {
+        String message = null;
+        boolean corrupted = false;
+        long fileLength = fc.size();
+        if ( ded.pos > fileLength )
+        {
+            corrupted = true;
+            message = "Record " + ded + " starts past EOF.";
+        }
+        else
+        {
+            ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
+            fc.read(datalength, ded.pos);
+            datalength.flip();
+            int datalen = datalength.getInt();
+            if ( ded.len != datalen )
+            {
+                corrupted = true;
+                message = "Record " + ded + " does not match data length on disk (" + datalen + ")";
+            }
+            else if ( ded.pos + ded.len > fileLength )
+            {
+                corrupted = true;
+                message = "Record " + ded + " exceeds file length.";
+            }
+        }
+
+        if ( corrupted )
+        {
+            log.warn( "\n The file is corrupt: " + "\n " + message );
+            throw new IOException( "The File Is Corrupt, need to reset" );
+        }
+
+        ByteBuffer data = ByteBuffer.allocate(ded.len);
+        fc.read(data, ded.pos + HEADER_SIZE_BYTES);
+        data.flip();
+
+        return elementSerializer.deSerialize( data.array(), null );
+    }
+
+    /**
+     * Moves the data stored from one position to another. The descriptor's position is updated.
+     * <p>
+     * @param ded
+     * @param newPosition
+     * @throws IOException
+     */
+    protected void move( final IndexedDiskElementDescriptor ded, final long newPosition )
+        throws IOException
+    {
+        ByteBuffer datalength = ByteBuffer.allocate(HEADER_SIZE_BYTES);
+        fc.read(datalength, ded.pos);
+        datalength.flip();
+        int length = datalength.getInt();
+
+        if ( length != ded.len )
+        {
+            throw new IOException( "Mismatched memory and disk length (" + length + ") for " + ded );
+        }
+
+        // TODO: more checks?
+
+        long readPos = ded.pos;
+        long writePos = newPosition;
+
+        // header len + data len
+        int remaining = HEADER_SIZE_BYTES + length;
+        ByteBuffer buffer = ByteBuffer.allocate(16384);
+
+        while ( remaining > 0 )
+        {
+            // chunk it
+            int chunkSize = Math.min( remaining, buffer.capacity() );
+            fc.read(buffer, readPos);
+            buffer.flip();
+            fc.write(buffer, writePos);
+            buffer.clear();
+
+            writePos += chunkSize;
+            readPos += chunkSize;
+            remaining -= chunkSize;
+        }
+
+        ded.pos = newPosition;
+    }
+
+    /**
+     * Writes the given byte array to the Disk at the specified position.
+     * <p>
+     * @param data
+     * @param ded
+     * @return true if we wrote successfully
+     * @throws IOException
+     */
+    protected boolean write( IndexedDiskElementDescriptor ded, byte[] data )
+        throws IOException
+    {
+        long pos = ded.pos;
+        if ( log.isTraceEnabled() )
+        {
+            log.trace( "write> pos=" + pos );
+            log.trace( fc + " -- data.length = " + data.length );
+        }
+
+        if ( data.length != ded.len )
+        {
+            throw new IOException( "Mismatched descriptor and data lengths" );
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE_BYTES + data.length);
+        buffer.putInt(data.length);
+        buffer.put(data);
+        buffer.flip();
+        int written = fc.write(buffer, pos);
+        fc.force(true);
+
+        return written == data.length;
+    }
+
+    /**
+     * Serializes the object and write it out to the given position.
+     * <p>
+     * TODO: make this take a ded as well.
+     * @return true unless error
+     * @param obj
+     * @param pos
+     * @throws IOException
+     */
+    protected boolean writeObject( Serializable obj, long pos )
+        throws IOException
+    {
+        byte[] data = elementSerializer.serialize( obj );
+        write( new IndexedDiskElementDescriptor( pos, data.length ), data );
+        return true;
+    }
+
+    /**
+     * Returns the raf length.
+     * <p>
+     * @return the length of the file.
+     * @throws IOException
+     */
+    protected long length()
+        throws IOException
+    {
+        return fc.size();
+    }
+
+    /**
+     * Closes the raf.
+     * <p>
+     * @throws IOException
+     */
+    protected void close()
+        throws IOException
+    {
+        fc.close();
+    }
+
+    /**
+     * Sets the raf to empty.
+     * <p>
+     * @throws IOException
+     */
+    protected synchronized void reset()
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Resetting Indexed File [" + filepath + "]" );
+        }
+        fc.truncate(0);
+        fc.force(true);
+    }
+
+    /**
+     * Truncates the file to a given length.
+     * <p>
+     * @param length the new length of the file
+     * @throws IOException
+     */
+    protected void truncate( long length )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Truncating file [" + filepath + "] to " + length );
+        }
+        fc.truncate( length );
+    }
+
+    /**
+     * This is used for debugging.
+     * <p>
+     * @return the file path.
+     */
+    protected String getFilePath()
+    {
+        return filepath;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCache.java
new file mode 100644
index 0000000..eadb533
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCache.java
@@ -0,0 +1,1692 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache;
+import org.apache.commons.jcs.auxiliary.disk.LRUMapJCS;
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.jcs.utils.struct.SortedPreferentialArray;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Disk cache that uses a RandomAccessFile with keys stored in memory. The maximum number of keys
+ * stored in memory is configurable. The disk cache tries to recycle spots on disk to limit file
+ * expansion.
+ */
+public class IndexedDiskCache<K, V>
+    extends AbstractDiskCache<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( IndexedDiskCache.class );
+
+    /** Cache name used in log messages */
+    protected final String logCacheName;
+
+    /** The name of the file where the data is stored */
+    private final String fileName;
+
+    /** The IndexedDisk manages reads and writes to the data file. */
+    private IndexedDisk dataFile;
+
+    /** The IndexedDisk manages reads and writes to the key file. */
+    private IndexedDisk keyFile;
+
+    /** Map containing the keys and disk offsets. */
+    private Map<K, IndexedDiskElementDescriptor> keyHash;
+
+    /** The maximum number of keys that we will keep in memory. */
+    private final int maxKeySize;
+
+    /** A handle on the data file. */
+    private File rafDir;
+
+    /** Should we keep adding to the recycle bin. False during optimization. */
+    boolean doRecycle = true;
+
+    /** Should we optimize real time */
+    boolean isRealTimeOptimizationEnabled = true;
+
+    /** Should we optimize on shutdown. */
+    boolean isShutdownOptimizationEnabled = true;
+
+    /** are we currently optimizing the files */
+    boolean isOptimizing = false;
+
+    /** The number of times the file has been optimized. */
+    private int timesOptimized = 0;
+
+    /** The thread optimizing the file. */
+    private volatile Thread currentOptimizationThread;
+
+    /** used for counting the number of requests */
+    private int removeCount = 0;
+
+    /** Should we queue puts. True when optimizing. We write the queue post optimization. */
+    private boolean queueInput = false;
+
+    /** list where puts made during optimization are made */
+    private final LinkedList<IndexedDiskElementDescriptor> queuedPutList =
+        new LinkedList<IndexedDiskElementDescriptor>();
+
+    /** RECYLCE BIN -- array of empty spots */
+    private SortedPreferentialArray<IndexedDiskElementDescriptor> recycle;
+
+    /** User configurable parameters */
+    private final IndexedDiskCacheAttributes cattr;
+
+    /** How many slots have we recycled. */
+    private int recycleCnt = 0;
+
+    /** How many items were there on startup. */
+    private int startupSize = 0;
+
+    /** the number of bytes free on disk. */
+    private long bytesFree = 0;
+
+    /** simple stat */
+    private AtomicInteger hitCount = new AtomicInteger(0);
+
+    /**
+     * Use this lock to synchronize reads and writes to the underlying storage mechanism.
+     */
+    protected ReentrantReadWriteLock storageLock = new ReentrantReadWriteLock();
+
+    /**
+     * Constructor for the DiskCache object.
+     * <p>
+     * @param cacheAttributes
+     */
+    public IndexedDiskCache( IndexedDiskCacheAttributes cacheAttributes )
+    {
+        this( cacheAttributes, null );
+    }
+
+    /**
+     * Constructor for the DiskCache object.
+     * <p>
+     * @param cattr
+     * @param elementSerializer used if supplied, the super's super will not set a null
+     */
+    public IndexedDiskCache( IndexedDiskCacheAttributes cattr, IElementSerializer elementSerializer )
+    {
+        super( cattr );
+
+        setElementSerializer( elementSerializer );
+
+        this.cattr = cattr;
+        this.maxKeySize = cattr.getMaxKeySize();
+        this.isRealTimeOptimizationEnabled = cattr.getOptimizeAtRemoveCount() > 0;
+        this.isShutdownOptimizationEnabled = cattr.isOptimizeOnShutdown();
+        this.logCacheName = "Region [" + getCacheName() + "] ";
+        // Make a clean file name
+        this.fileName = getCacheName().replaceAll("[^a-zA-Z0-9-_\\.]", "_");
+
+        try
+        {
+            initializeFileSystem( cattr );
+
+            initializeKeysAndData( cattr );
+
+            initializeRecycleBin();
+
+            // Initialization finished successfully, so set alive to true.
+            alive = true;
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Indexed Disk Cache is alive." );
+            }
+
+            // TODO: Should we improve detection of whether or not the file should be optimized.
+            if ( isRealTimeOptimizationEnabled && keyHash.size() > 0 )
+            {
+                // Kick off a real time optimization, in case we didn't do a final optimization.
+                doOptimizeRealTime();
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Failure initializing for fileName: " + fileName + " and directory: "
+                + this.rafDir.getAbsolutePath(), e );
+        }
+    }
+
+    /**
+     * Tries to create the root directory if it does not already exist.
+     * <p>
+     * @param cattr
+     */
+    private void initializeFileSystem( IndexedDiskCacheAttributes cattr )
+    {
+        this.rafDir = cattr.getDiskPath();
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Cache file root directory: " + rafDir );
+        }
+    }
+
+    /**
+     * Creates the key and data disk caches.
+     * <p>
+     * Loads any keys if they are present and ClearDiskOnStartup is false.
+     * <p>
+     * @param cattr
+     * @throws FileNotFoundException
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    private void initializeKeysAndData( IndexedDiskCacheAttributes cattr )
+        throws FileNotFoundException, IOException, InterruptedException
+    {
+        this.dataFile = new IndexedDisk( new File( rafDir, fileName + ".data" ), getElementSerializer() );
+        this.keyFile = new IndexedDisk( new File( rafDir, fileName + ".key" ), getElementSerializer() );
+
+        if ( cattr.isClearDiskOnStartup() )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "ClearDiskOnStartup is set to true.  Ingnoring any persisted data." );
+            }
+            initializeEmptyStore();
+        }
+        else if ( keyFile.length() > 0 )
+        {
+            // If the key file has contents, try to initialize the keys
+            // from it. In no keys are loaded reset the data file.
+            initializeStoreFromPersistedData();
+        }
+        else
+        {
+            // Otherwise start with a new empty map for the keys, and reset
+            // the data file if it has contents.
+            initializeEmptyStore();
+        }
+    }
+
+    /**
+     * Initializes an empty disk cache.
+     * <p>
+     * @throws IOException
+     */
+    private void initializeEmptyStore()
+        throws IOException
+    {
+        initializeKeyMap();
+
+        if ( dataFile.length() > 0 )
+        {
+            dataFile.reset();
+        }
+    }
+
+    /**
+     * Loads any persisted data and checks for consistency. If there is a consistency issue, the
+     * files are cleared.
+     * <p>
+     * @throws InterruptedException
+     * @throws IOException
+     */
+    private void initializeStoreFromPersistedData()
+        throws InterruptedException, IOException
+    {
+        loadKeys();
+
+        if ( keyHash.size() == 0 )
+        {
+            dataFile.reset();
+        }
+        else
+        {
+            boolean isOk = checkKeyDataConsistency( false );
+            if ( !isOk )
+            {
+                keyHash.clear();
+                keyFile.reset();
+                dataFile.reset();
+                log.warn( logCacheName + "Corruption detected.  Reseting data and keys files." );
+            }
+            else
+            {
+                synchronized (this)
+                {
+                    startupSize = keyHash.size();
+                }
+            }
+        }
+    }
+
+    /**
+     * Loads the keys from the .key file. The keys are stored in a HashMap on disk. This is
+     * converted into a LRUMap.
+     * <p>
+     * @throws InterruptedException
+     */
+    protected void loadKeys()
+        throws InterruptedException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Loading keys for " + keyFile.toString() );
+        }
+
+        storageLock.writeLock().lock();
+
+        try
+        {
+            // create a key map to use.
+            initializeKeyMap();
+
+            HashMap<K, IndexedDiskElementDescriptor> keys =
+                keyFile.readObject( new IndexedDiskElementDescriptor( 0, (int) keyFile.length()
+                - IndexedDisk.HEADER_SIZE_BYTES ) );
+
+            if ( keys != null )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( logCacheName + "Found " + keys.size() + " in keys file." );
+                }
+
+                keyHash.putAll( keys );
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( logCacheName + "Loaded keys from [" + fileName + "], key count: " + keyHash.size()
+                        + "; up to " + maxKeySize + " will be available." );
+                }
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                dump( false );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem loading keys for file " + fileName, e );
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Check for minimal consistency between the keys and the datafile. Makes sure no starting
+     * positions in the keys exceed the file length.
+     * <p>
+     * The caller should take the appropriate action if the keys and data are not consistent.
+     * @param checkForDedOverlaps if <code>true</code>, do a more thorough check by checking for
+     *            data overlap
+     * @return <code>true</code> if the test passes
+     */
+    private boolean checkKeyDataConsistency( boolean checkForDedOverlaps )
+    {
+        ElapsedTimer timer = new ElapsedTimer();
+        log.debug( logCacheName + "Performing inital consistency check" );
+
+        boolean isOk = true;
+        long fileLength = 0;
+        try
+        {
+            fileLength = dataFile.length();
+
+            for (Map.Entry<K, IndexedDiskElementDescriptor> e : keyHash.entrySet())
+            {
+                IndexedDiskElementDescriptor ded = e.getValue();
+
+                isOk = ded.pos + IndexedDisk.HEADER_SIZE_BYTES + ded.len <= fileLength;
+
+                if ( !isOk )
+                {
+                    log.warn( logCacheName + "The dataFile is corrupted!" + "\n raf.length() = " + fileLength
+                        + "\n ded.pos = " + ded.pos );
+                    break;
+                }
+            }
+
+            if ( isOk && checkForDedOverlaps )
+            {
+                isOk = checkForDedOverlaps( createPositionSortedDescriptorList() );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+            isOk = false;
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Finished inital consistency check, isOk = " + isOk + " in "
+                + timer.getElapsedTimeString() );
+        }
+
+        return isOk;
+    }
+
+    /**
+     * Detects any overlapping elements. This expects a sorted list.
+     * <p>
+     * The total length of an item is IndexedDisk.RECORD_HEADER + ded.len.
+     * <p>
+     * @param sortedDescriptors
+     * @return false if there are overlaps.
+     */
+    protected boolean checkForDedOverlaps( IndexedDiskElementDescriptor[] sortedDescriptors )
+    {
+        long start = System.currentTimeMillis();
+        boolean isOk = true;
+        long expectedNextPos = 0;
+        for ( int i = 0; i < sortedDescriptors.length; i++ )
+        {
+            IndexedDiskElementDescriptor ded = sortedDescriptors[i];
+            if ( expectedNextPos > ded.pos )
+            {
+                log.error( logCacheName + "Corrupt file: overlapping deds " + ded );
+                isOk = false;
+                break;
+            }
+            else
+            {
+                expectedNextPos = ded.pos + IndexedDisk.HEADER_SIZE_BYTES + ded.len;
+            }
+        }
+        long end = System.currentTimeMillis();
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Check for DED overlaps took " + ( end - start ) + " ms." );
+        }
+
+        return isOk;
+    }
+
+    /**
+     * Saves key file to disk. This converts the LRUMap to a HashMap for deserialization.
+     */
+    protected void saveKeys()
+    {
+        try
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Saving keys to: " + fileName + ", key count: " + keyHash.size() );
+            }
+
+            keyFile.reset();
+
+            HashMap<K, IndexedDiskElementDescriptor> keys =
+                new HashMap<K, IndexedDiskElementDescriptor>();
+            keys.putAll( keyHash );
+
+            if ( keys.size() > 0 )
+            {
+                keyFile.writeObject( keys, 0 );
+            }
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Finished saving keys." );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem storing keys.", e );
+        }
+    }
+
+    /**
+     * Update the disk cache. Called from the Queue. Makes sure the Item has not been retrieved from
+     * purgatory while in queue for disk. Remove items from purgatory when they go to disk.
+     * <p>
+     * @param ce The ICacheElement<K, V> to put to disk.
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> ce )
+    {
+        if ( !alive )
+        {
+            log.error( logCacheName + "No longer alive; aborting put of key = " + ce.getKey() );
+            return;
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Storing element on disk, key: " + ce.getKey() );
+        }
+
+        IndexedDiskElementDescriptor ded = null;
+
+        // old element with same key
+        IndexedDiskElementDescriptor old = null;
+
+        try
+        {
+            byte[] data = getElementSerializer().serialize( ce );
+
+            // make sure this only locks for one particular cache region
+            storageLock.writeLock().lock();
+            try
+            {
+                old = keyHash.get( ce.getKey() );
+
+                // Item with the same key already exists in file.
+                // Try to reuse the location if possible.
+                if ( old != null && data.length <= old.len )
+                {
+                    // Reuse the old ded. The defrag relies on ded updates by reference, not
+                    // replacement.
+                    ded = old;
+                    ded.len = data.length;
+                }
+                else
+                {
+                    // we need this to compare in the recycle bin
+                    ded = new IndexedDiskElementDescriptor( dataFile.length(), data.length );
+
+                    if ( doRecycle )
+                    {
+                        IndexedDiskElementDescriptor rep = recycle
+                            .takeNearestLargerOrEqual( ded );
+                        if ( rep != null )
+                        {
+                            ded = rep;
+                            ded.len = data.length;
+                            recycleCnt++;
+                            this.adjustBytesFree( ded, false );
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( logCacheName + "using recycled ded " + ded.pos + " rep.len = " + rep.len
+                                    + " ded.len = " + ded.len );
+                            }
+                        }
+                    }
+
+                    // Put it in the map
+                    keyHash.put( ce.getKey(), ded );
+
+                    if ( queueInput )
+                    {
+                        queuedPutList.add( ded );
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( logCacheName + "added to queued put list." + queuedPutList.size() );
+                        }
+                    }
+
+                    // add the old slot to the recycle bin
+                    if ( old != null )
+                    {
+                        addToRecycleBin( old );
+                    }
+                }
+
+                dataFile.write( ded, data );
+            }
+            finally
+            {
+                storageLock.writeLock().unlock();
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Put to file: " + fileName + ", key: " + ce.getKey() + ", position: "
+                    + ded.pos + ", size: " + ded.len );
+            }
+        }
+        catch ( ConcurrentModificationException cme )
+        {
+            // do nothing, this means it has gone back to memory mid
+            // serialization
+            if ( log.isDebugEnabled() )
+            {
+                // this shouldn't be possible
+                log.debug( logCacheName + "Caught ConcurrentModificationException." + cme );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Failure updating element, key: " + ce.getKey() + " old: " + old, e );
+        }
+    }
+
+    /**
+     * Gets the key, then goes to disk to get the object.
+     * <p>
+     * @param key
+     * @return ICacheElement<K, V> or null
+     * @see AbstractDiskCache#doGet
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+    {
+        if ( !alive )
+        {
+            log.error( logCacheName + "No longer alive so returning null for key = " + key );
+            return null;
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Trying to get from disk: " + key );
+        }
+
+        ICacheElement<K, V> object = null;
+        try
+        {
+            storageLock.readLock().lock();
+            try
+            {
+                object = readElement( key );
+            }
+            finally
+            {
+                storageLock.readLock().unlock();
+            }
+
+            if ( object != null )
+            {
+                hitCount.incrementAndGet();
+            }
+        }
+        catch ( IOException ioe )
+        {
+            log.error( logCacheName + "Failure getting from disk, key = " + key, ioe );
+            reset();
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Failure getting from disk, key = " + key, e );
+        }
+        return object;
+    }
+
+    /**
+     * Gets matching items from the cache.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+        try
+        {
+            Set<K> keyArray = null;
+            storageLock.readLock().lock();
+            try
+            {
+                keyArray = new HashSet<K>(keyHash.keySet());
+            }
+            finally
+            {
+                storageLock.readLock().unlock();
+            }
+
+            Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray );
+
+            for (K key : matchingKeys)
+            {
+                ICacheElement<K, V> element = processGet( key );
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Failure getting matching from disk, pattern = " + pattern, e );
+        }
+        return elements;
+    }
+
+    /**
+     * Reads the item from disk.
+     * <p>
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    private ICacheElement<K, V> readElement( K key )
+        throws IOException
+    {
+        ICacheElement<K, V> object = null;
+
+        IndexedDiskElementDescriptor ded = keyHash.get( key );
+
+        if ( ded != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Found on disk, key: " + key );
+            }
+            try
+            {
+                ICacheElement<K, V> readObject = dataFile.readObject( ded );
+                object = readObject;
+                // TODO consider checking key equality and throwing if there is a failure
+            }
+            catch ( IOException e )
+            {
+                log.error( logCacheName + "IO Exception, Problem reading object from file", e );
+                throw e;
+            }
+            catch ( Exception e )
+            {
+                log.error( logCacheName + "Exception, Problem reading object from file", e );
+                throw new IOException( logCacheName + "Problem reading object from disk. " + e.getMessage() );
+            }
+        }
+
+        return object;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        HashSet<K> keys = new HashSet<K>();
+
+        storageLock.readLock().lock();
+
+        try
+        {
+            keys.addAll(this.keyHash.keySet());
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        return keys;
+    }
+
+    /**
+     * Returns true if the removal was successful; or false if there is nothing to remove. Current
+     * implementation always result in a disk orphan.
+     * <p>
+     * @return true if at least one item was removed.
+     * @param key
+     */
+    @Override
+    protected boolean processRemove( K key )
+    {
+        if ( !alive )
+        {
+            log.error( logCacheName + "No longer alive so returning false for key = " + key );
+            return false;
+        }
+
+        if ( key == null )
+        {
+            return false;
+        }
+
+        boolean reset = false;
+        boolean removed = false;
+        try
+        {
+            storageLock.writeLock().lock();
+
+            if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+            {
+                removed = performPartialKeyRemoval( (String) key );
+            }
+            else if ( key instanceof GroupAttrName && ((GroupAttrName<?>)key).attrName == null )
+            {
+                removed = performGroupRemoval( ((GroupAttrName<?>)key).groupId );
+            }
+            else
+            {
+                removed = performSingleKeyRemoval( key );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem removing element.", e );
+            reset = true;
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+
+        if ( reset )
+        {
+            reset();
+        }
+
+        // this increments the remove count.
+        // there is no reason to call this if an item was not removed.
+        if ( removed )
+        {
+            doOptimizeRealTime();
+        }
+
+        return removed;
+    }
+
+    /**
+     * Iterates over the keyset. Builds a list of matches. Removes all the keys in the list. Does
+     * not remove via the iterator, since the map impl may not support it.
+     * <p>
+     * This operates under a lock obtained in doRemove().
+     * <p>
+     * @param key
+     * @return true if there was a match
+     */
+    private boolean performPartialKeyRemoval( String key )
+    {
+        boolean removed = false;
+
+        // remove all keys of the same name hierarchy.
+        List<K> itemsToRemove = new LinkedList<K>();
+
+        for (K k : keyHash.keySet())
+        {
+            if ( k instanceof String && k.toString().startsWith( key.toString() ) )
+            {
+                itemsToRemove.add( k );
+            }
+        }
+
+        // remove matches.
+        for (K fullKey : itemsToRemove)
+        {
+            // Don't add to recycle bin here
+            // https://issues.apache.org/jira/browse/JCS-67
+            performSingleKeyRemoval( fullKey );
+            removed = true;
+            // TODO this needs to update the remove count separately
+        }
+
+        return removed;
+    }
+
+    /**
+     * Remove all elements from the group. This does not use the iterator to remove. It builds a
+     * list of group elements and then removes them one by one.
+     * <p>
+     * This operates under a lock obtained in doRemove().
+     * <p>
+     * @param key
+     * @return true if an element was removed
+     */
+    private boolean performGroupRemoval( GroupId key )
+    {
+        boolean removed = false;
+
+        // remove all keys of the same name group.
+        List<K> itemsToRemove = new LinkedList<K>();
+
+        // remove all keys of the same name hierarchy.
+        for (K k : keyHash.keySet())
+        {
+            if ( k instanceof GroupAttrName && ( (GroupAttrName<?>) k ).groupId.equals( key ) )
+            {
+                itemsToRemove.add( k );
+            }
+        }
+
+        // remove matches.
+        for (K fullKey : itemsToRemove)
+        {
+            // Don't add to recycle bin here
+            // https://issues.apache.org/jira/browse/JCS-67
+            performSingleKeyRemoval( fullKey );
+            removed = true;
+            // TODO this needs to update the remove count separately
+        }
+
+        return removed;
+    }
+
+    /**
+     * Removes an individual key from the cache.
+     * <p>
+     * This operates under a lock obtained in doRemove().
+     * <p>
+     * @param key
+     * @return true if an item was removed.
+     */
+    private boolean performSingleKeyRemoval( K key )
+    {
+        boolean removed;
+        // remove single item.
+        IndexedDiskElementDescriptor ded = keyHash.remove( key );
+        removed = ded != null;
+        addToRecycleBin( ded );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Disk removal: Removed from key hash, key [" + key + "] removed = " + removed );
+        }
+        return removed;
+    }
+
+    /**
+     * Remove all the items from the disk cache by reseting everything.
+     */
+    @Override
+    public void processRemoveAll()
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "all", ICacheEventLogger.REMOVEALL_EVENT );
+        try
+        {
+            reset();
+        }
+        catch ( Exception e )
+        {
+            log.error( logCacheName + "Problem removing all.", e );
+            reset();
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Reset effectively clears the disk cache, creating new files, recyclebins, and keymaps.
+     * <p>
+     * It can be used to handle errors by last resort, force content update, or removeall.
+     */
+    private void reset()
+    {
+        if ( log.isWarnEnabled() )
+        {
+            log.warn( logCacheName + "Reseting cache" );
+        }
+
+        try
+        {
+            storageLock.writeLock().lock();
+
+            if ( dataFile != null )
+            {
+                dataFile.close();
+            }
+            File dataFileTemp = new File( rafDir, fileName + ".data" );
+            boolean result = dataFileTemp.delete();
+            if (!result && log.isDebugEnabled())
+            {
+                log.debug("Could not delete file " + dataFileTemp);
+            }
+
+            if ( keyFile != null )
+            {
+                keyFile.close();
+            }
+            File keyFileTemp = new File( rafDir, fileName + ".key" );
+            result = keyFileTemp.delete();
+            if (!result && log.isDebugEnabled())
+            {
+                log.debug("Could not delete file " + keyFileTemp);
+            }
+
+            dataFile = new IndexedDisk( new File( rafDir, fileName + ".data" ), getElementSerializer() );
+            keyFile = new IndexedDisk( new File( rafDir, fileName + ".key" ), getElementSerializer() );
+
+            initializeRecycleBin();
+
+            initializeKeyMap();
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Failure reseting state", e );
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * If the maxKeySize is < 0, use 5000, no way to have an unlimited recycle bin right now, or one
+     * less than the mazKeySize.
+     */
+    private void initializeRecycleBin()
+    {
+        int recycleBinSize = cattr.getMaxRecycleBinSize() >= 0 ? cattr.getMaxRecycleBinSize() : 0;
+        recycle = new SortedPreferentialArray<IndexedDiskElementDescriptor>( recycleBinSize );
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "Set recycle max Size to MaxRecycleBinSize: '" + recycleBinSize + "'" );
+        }
+    }
+
+    /**
+     * Create the map for keys that contain the index position on disk.
+     */
+    private void initializeKeyMap()
+    {
+        keyHash = null;
+        if ( maxKeySize >= 0 )
+        {
+            keyHash = new LRUMap( maxKeySize );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Set maxKeySize to: '" + maxKeySize + "'" );
+            }
+        }
+        else
+        {
+            // If no max size, use a plain map for memory and processing efficiency.
+            keyHash = new HashMap<K, IndexedDiskElementDescriptor>();
+            // keyHash = Collections.synchronizedMap( new HashMap() );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Set maxKeySize to unlimited'" );
+            }
+        }
+    }
+
+    /**
+     * Dispose of the disk cache in a background thread. Joins against this thread to put a cap on
+     * the disposal time.
+     * <p>
+     * TODO make dispose window configurable.
+     */
+    @Override
+    public void processDispose()
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "none", ICacheEventLogger.DISPOSE_EVENT );
+        try
+        {
+            Runnable disR = new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    disposeInternal();
+                }
+            };
+            Thread t = new Thread( disR, "IndexedDiskCache-DisposalThread" );
+            t.start();
+            // wait up to 60 seconds for dispose and then quit if not done.
+            try
+            {
+                t.join( 60 * 1000 );
+            }
+            catch ( InterruptedException ex )
+            {
+                log.error( logCacheName + "Interrupted while waiting for disposal thread to finish.", ex );
+            }
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Internal method that handles the disposal.
+     */
+    protected void disposeInternal()
+    {
+        if ( !alive )
+        {
+            log.error( logCacheName + "Not alive and dispose was called, filename: " + fileName );
+            return;
+        }
+
+        // Prevents any interaction with the cache while we're shutting down.
+        alive = false;
+
+        Thread optimizationThread = currentOptimizationThread;
+        if ( isRealTimeOptimizationEnabled && optimizationThread != null )
+        {
+            // Join with the current optimization thread.
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "In dispose, optimization already " + "in progress; waiting for completion." );
+            }
+            try
+            {
+                optimizationThread.join();
+            }
+            catch ( InterruptedException e )
+            {
+                log.error( logCacheName + "Unable to join current optimization thread.", e );
+            }
+        }
+        else if ( isShutdownOptimizationEnabled && this.getBytesFree() > 0 )
+        {
+            optimizeFile();
+        }
+
+        saveKeys();
+
+        try
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Closing files, base filename: " + fileName );
+            }
+            dataFile.close();
+            dataFile = null;
+            keyFile.close();
+            keyFile = null;
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Failure closing files in dispose, filename: " + fileName, e );
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Shutdown complete." );
+        }
+    }
+
+    /**
+     * Add descriptor to recycle bin if it is not null. Adds the length of the item to the bytes
+     * free.
+     * <p>
+     * This is called in three places: (1) When an item is removed. All item removals funnel down to
+     * the removeSingleItem method. (2) When an item on disk is updated with a value that will not
+     * fit in the previous slot. (3) When the max key size is reached, the freed slot will be added.
+     * <p>
+     * The recylebin is not a set. If a slot it added twice, it will result in the wrong data being
+     * returned.
+     * <p>
+     * @param ded
+     */
+    protected void addToRecycleBin( IndexedDiskElementDescriptor ded )
+    {
+        // reuse the spot
+        if ( ded != null )
+        {
+            this.adjustBytesFree( ded, true );
+
+            if ( doRecycle )
+            {
+                recycle.add( ded );
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( logCacheName + "recycled ded" + ded );
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Performs the check for optimization, and if it is required, do it.
+     */
+    protected void doOptimizeRealTime()
+    {
+        if ( isRealTimeOptimizationEnabled && !isOptimizing && removeCount++ >= cattr.getOptimizeAtRemoveCount() )
+        {
+            isOptimizing = true;
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Optimizing file. removeCount [" + removeCount + "] OptimizeAtRemoveCount ["
+                    + cattr.getOptimizeAtRemoveCount() + "]" );
+            }
+
+            if ( currentOptimizationThread == null )
+            {
+                storageLock.writeLock().lock();
+
+                try
+                {
+                    if ( currentOptimizationThread == null )
+                    {
+                        currentOptimizationThread = new Thread( new Runnable()
+                        {
+                            @Override
+                            public void run()
+                            {
+                                optimizeFile();
+
+                                currentOptimizationThread = null;
+                            }
+                        }, "IndexedDiskCache-OptimizationThread" );
+                    }
+                }
+                finally
+                {
+                    storageLock.writeLock().unlock();
+                }
+
+                if ( currentOptimizationThread != null )
+                {
+                    currentOptimizationThread.start();
+                }
+            }
+        }
+    }
+
+    /**
+     * File optimization is handled by this method. It works as follows:
+     * <ol>
+     * <li>Shutdown recycling and turn on queuing of puts. </li> <li>Take a snapshot of the current
+     * descriptors. If there are any removes, ignore them, as they will be compacted during the next
+     * optimization.</li> <li>Optimize the snapshot. For each descriptor:
+     * <ol>
+     * <li>Obtain the write-lock.</li> <li>Shift the element on the disk, in order to compact out
+     * the free space. </li> <li>Release the write-lock. This allows elements to still be accessible
+     * during optimization.</li>
+     * </ol>
+     * </li> <li>Obtain the write-lock.</li> <li>All queued puts are made at the end of the file.
+     * Optimize these under a single write-lock.</li> <li>Truncate the file.</li> <li>Release the
+     * write-lock. </li> <li>Restore system to standard operation.</li>
+     * </ol>
+     */
+    protected void optimizeFile()
+    {
+        ElapsedTimer timer = new ElapsedTimer();
+        timesOptimized++;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Beginning Optimization #" + timesOptimized );
+        }
+
+        // CREATE SNAPSHOT
+        IndexedDiskElementDescriptor[] defragList = null;
+
+        storageLock.writeLock().lock();
+
+        try
+        {
+            queueInput = true;
+            // shut off recycle while we're optimizing,
+            doRecycle = false;
+            defragList = createPositionSortedDescriptorList();
+        }
+        finally
+        {
+            // Release if I acquired.
+            storageLock.writeLock().unlock();
+        }
+
+        // Defrag the file outside of the write lock. This allows a move to be made,
+        // and yet have the element still accessible for reading or writing.
+        long expectedNextPos = defragFile( defragList, 0 );
+
+        // ADD THE QUEUED ITEMS to the end and then truncate
+        storageLock.writeLock().lock();
+
+        try
+        {
+            try
+            {
+                if ( !queuedPutList.isEmpty() )
+                {
+                    // This is perhaps unnecessary, but the list might not be as sorted as we think.
+                    defragList = new IndexedDiskElementDescriptor[queuedPutList.size()];
+                    queuedPutList.toArray( defragList );
+                    Arrays.sort( defragList, new PositionComparator() );
+
+                    // pack them at the end
+                    expectedNextPos = defragFile( defragList, expectedNextPos );
+                }
+                // TRUNCATE THE FILE
+                dataFile.truncate( expectedNextPos );
+            }
+            catch ( Exception e )
+            {
+                log.error( logCacheName + "Error optimizing queued puts.", e );
+            }
+
+            // RESTORE NORMAL OPERATION
+            removeCount = 0;
+            resetBytesFree();
+            initializeRecycleBin();
+            queuedPutList.clear();
+            queueInput = false;
+            // turn recycle back on.
+            doRecycle = true;
+            isOptimizing = false;
+        }
+        finally
+        {
+            storageLock.writeLock().unlock();
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( logCacheName + "Finished #" + timesOptimized + " Optimization took "
+                + timer.getElapsedTimeString() );
+        }
+    }
+
+    /**
+     * Defragments the file in place by compacting out the free space (i.e., moving records
+     * forward). If there were no gaps the resulting file would be the same size as the previous
+     * file. This must be supplied an ordered defragList.
+     * <p>
+     * @param defragList sorted list of descriptors for optimization
+     * @param startingPos the start position in the file
+     * @return this is the potential new file end
+     */
+    private long defragFile( IndexedDiskElementDescriptor[] defragList, long startingPos )
+    {
+        ElapsedTimer timer = new ElapsedTimer();
+        long preFileSize = 0;
+        long postFileSize = 0;
+        long expectedNextPos = 0;
+        try
+        {
+            preFileSize = this.dataFile.length();
+            // find the first gap in the disk and start defragging.
+            expectedNextPos = startingPos;
+            for ( int i = 0; i < defragList.length; i++ )
+            {
+                storageLock.writeLock().lock();
+                try
+                {
+                    if ( expectedNextPos != defragList[i].pos )
+                    {
+                        dataFile.move( defragList[i], expectedNextPos );
+                    }
+                    expectedNextPos = defragList[i].pos + IndexedDisk.HEADER_SIZE_BYTES + defragList[i].len;
+                }
+                finally
+                {
+                    storageLock.writeLock().unlock();
+                }
+            }
+
+            postFileSize = this.dataFile.length();
+
+            // this is the potential new file end
+            return expectedNextPos;
+        }
+        catch ( IOException e )
+        {
+            log.error( logCacheName + "Error occurred during defragmentation.", e );
+        }
+        finally
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( logCacheName + "Defragmentation took " + timer.getElapsedTimeString()
+                    + ". File Size (before=" + preFileSize + ") (after=" + postFileSize + ") (truncating to "
+                    + expectedNextPos + ")" );
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * Creates a snapshot of the IndexedDiskElementDescriptors in the keyHash and returns them
+     * sorted by position in the dataFile.
+     * <p>
+     * TODO fix values() method on the LRU map.
+     * <p>
+     * @return IndexedDiskElementDescriptor[]
+     */
+    private IndexedDiskElementDescriptor[] createPositionSortedDescriptorList()
+    {
+        IndexedDiskElementDescriptor[] defragList = new IndexedDiskElementDescriptor[keyHash.size()];
+        Iterator<Map.Entry<K, IndexedDiskElementDescriptor>> iterator = keyHash.entrySet().iterator();
+        for ( int i = 0; iterator.hasNext(); i++ )
+        {
+            Map.Entry<K, IndexedDiskElementDescriptor> next = iterator.next();
+            defragList[i] = next.getValue();
+        }
+
+        Arrays.sort( defragList, new PositionComparator() );
+
+        return defragList;
+    }
+
+    /**
+     * Returns the current cache size.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return keyHash.size();
+    }
+
+    /**
+     * Returns the size of the recyclebin in number of elements.
+     * <p>
+     * @return The number of items in the bin.
+     */
+    protected int getRecyleBinSize()
+    {
+        return this.recycle.size();
+    }
+
+    /**
+     * Returns the number of times we have used spots from the recycle bin.
+     * <p>
+     * @return The number of spots used.
+     */
+    protected int getRecyleCount()
+    {
+        return this.recycleCnt;
+    }
+
+    /**
+     * Returns the number of bytes that are free. When an item is removed, its length is recorded.
+     * When a spot is used form the recycle bin, the length of the item stored is recorded.
+     * <p>
+     * @return The number bytes free on the disk file.
+     */
+    protected synchronized long getBytesFree()
+    {
+        return this.bytesFree;
+    }
+
+    /**
+     * Resets the number of bytes that are free.
+     */
+    private synchronized void resetBytesFree()
+    {
+        this.bytesFree = 0;
+    }
+
+    /**
+     * To subtract you can pass in false for add..
+     * <p>
+     * @param ded
+     * @param add
+     */
+    private synchronized void adjustBytesFree( IndexedDiskElementDescriptor ded, boolean add )
+    {
+        if ( ded != null )
+        {
+            int amount = ded.len + IndexedDisk.HEADER_SIZE_BYTES;
+
+            if ( add )
+            {
+                this.bytesFree += amount;
+            }
+            else
+            {
+                this.bytesFree -= amount;
+            }
+        }
+    }
+
+    /**
+     * This is for debugging and testing.
+     * <p>
+     * @return the length of the data file.
+     * @throws IOException
+     */
+    protected long getDataFileSize()
+        throws IOException
+    {
+        long size = 0;
+
+        storageLock.readLock().lock();
+
+        try
+        {
+            if ( dataFile != null )
+            {
+                size = dataFile.length();
+            }
+        }
+        finally
+        {
+            storageLock.readLock().unlock();
+        }
+
+        return size;
+    }
+
+    /**
+     * For debugging. This dumps the values by default.
+     */
+    public void dump()
+    {
+        dump( true );
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @param dumpValues A boolean indicating if values should be dumped.
+     */
+    public void dump( boolean dumpValues )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( logCacheName + "[dump] Number of keys: " + keyHash.size() );
+
+            for (Map.Entry<K, IndexedDiskElementDescriptor> e : keyHash.entrySet())
+            {
+                K key = e.getKey();
+                IndexedDiskElementDescriptor ded = e.getValue();
+
+                log.debug( logCacheName + "[dump] Disk element, key: " + key + ", pos: " + ded.pos + ", ded.len"
+                    + ded.len + ( dumpValues ? ", val: " + get( key ) : "" ) );
+            }
+        }
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return this.cattr;
+    }
+
+    /**
+     * Gets basic stats for the disk cache.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * Returns info about the disk cache.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getStatistics()
+     */
+    @Override
+    public synchronized IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Indexed Disk Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Boolean>( "Is Alive", Boolean.valueOf(alive) ) );
+        elems.add(new StatElement<Integer>( "Key Map Size",
+                Integer.valueOf(this.keyHash != null ? this.keyHash.size() : -1) ) );
+        try
+        {
+            elems.add(new StatElement<Long>( "Data File Length",
+                    Long.valueOf(this.dataFile != null ? this.dataFile.length() : -1L) ) );
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+        }
+        elems.add(new StatElement<Integer>( "Hit Count", Integer.valueOf(this.hitCount.get()) ) );
+        elems.add(new StatElement<Long>( "Bytes Free", Long.valueOf(this.bytesFree) ) );
+        elems.add(new StatElement<Integer>( "Optimize Operation Count", Integer.valueOf(this.removeCount) ) );
+        elems.add(new StatElement<Integer>( "Times Optimized", Integer.valueOf(this.timesOptimized) ) );
+        elems.add(new StatElement<Integer>( "Recycle Count", Integer.valueOf(this.recycleCnt) ) );
+        elems.add(new StatElement<Integer>( "Recycle Bin Size", Integer.valueOf(this.recycle.size()) ) );
+        elems.add(new StatElement<Integer>( "Startup Size", Integer.valueOf(this.startupSize) ) );
+
+        // get the stats from the super too
+        IStats sStats = super.getStatistics();
+        elems.addAll(sStats.getStatElements());
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * This is exposed for testing.
+     * <p>
+     * @return Returns the timesOptimized.
+     */
+    protected int getTimesOptimized()
+    {
+        return timesOptimized;
+    }
+
+    /**
+     * This is used by the event logging.
+     * <p>
+     * @return the location of the disk, either path or ip.
+     */
+    @Override
+    protected String getDiskLocation()
+    {
+        return dataFile.getFilePath();
+    }
+
+    /**
+     * Compares IndexedDiskElementDescriptor based on their position.
+     * <p>
+     */
+    protected static final class PositionComparator
+        implements Comparator<IndexedDiskElementDescriptor>, Serializable
+    {
+        /** serialVersionUID */
+        private static final long serialVersionUID = -8387365338590814113L;
+
+        /**
+         * Compares two descriptors based on position.
+         * <p>
+         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+         */
+        @Override
+        public int compare( IndexedDiskElementDescriptor o1, IndexedDiskElementDescriptor o2 )
+        {
+            IndexedDiskElementDescriptor ded1 = o1;
+            IndexedDiskElementDescriptor ded2 = o2;
+
+            if ( ded1.pos < ded2.pos )
+            {
+                return -1;
+            }
+            else if ( ded1.pos == ded2.pos )
+            {
+                return 0;
+            }
+            else
+            {
+                return 1;
+            }
+        }
+    }
+
+    /**
+     * Class for recycling and lru. This implements the LRU overflow callback, so we can add items
+     * to the recycle bin.
+     */
+    public class LRUMap
+        extends LRUMapJCS<K, IndexedDiskElementDescriptor>
+        // implements Serializable
+    {
+        /**
+         * <code>tag</code> tells us which map we are working on.
+         */
+        public String tag = "orig";
+
+        /**
+         * Default
+         */
+        public LRUMap()
+        {
+            super();
+        }
+
+        /**
+         * @param maxKeySize
+         */
+        public LRUMap( int maxKeySize )
+        {
+            super( maxKeySize );
+        }
+
+        /**
+         * This is called when the may key size is reached. The least recently used item will be
+         * passed here. We will store the position and size of the spot on disk in the recycle bin.
+         * <p>
+         * @param key
+         * @param value
+         */
+        @Override
+        protected void processRemovedLRU(K key, IndexedDiskElementDescriptor value )
+        {
+            addToRecycleBin( value );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( logCacheName + "Removing key: [" + key + "] from key store." );
+                log.debug( logCacheName + "Key store size: [" + this.size() + "]." );
+            }
+
+            doOptimizeRealTime();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheAttributes.java
new file mode 100644
index 0000000..09bd67f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheAttributes.java
@@ -0,0 +1,207 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheAttributes;
+
+/**
+ * Configuration class for the Indexed Disk Cache
+ */
+public class IndexedDiskCacheAttributes
+    extends AbstractDiskCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -2190863599358782950L;
+
+    /** default value */
+    private static final int DEFAULT_maxKeySize = 5000;
+
+    /** -1 means no limit. */
+    private int maxKeySize = DEFAULT_maxKeySize;
+
+    /** default value */
+    private static final int DEFAULT_maxRecycleBinSize = 5000;
+
+    /**
+     * Cannot be larger than the max size. If max is less than 0, this will be 5000
+     */
+    private int maxRecycleBinSize = DEFAULT_maxRecycleBinSize;
+
+    /** default to -1, i.e., don't optimize until shutdown */
+    private int optimizeAtRemoveCount = -1;
+
+    /** Should we optimize on shutdown. */
+    public static final boolean DEFAULT_OPTIMIZE_ON_SHUTDOWN = true;
+
+    /** Should we optimize on shutdown. */
+    private boolean optimizeOnShutdown = DEFAULT_OPTIMIZE_ON_SHUTDOWN;
+
+    /** Should we clear the disk on startup. */
+    public static final boolean DEFAULT_CLEAR_DISK_ON_STARTUP = false;
+
+    /** Should we clear the disk on startup. If true the congtents of disk are cleared. */
+    private boolean clearDiskOnStartup = DEFAULT_CLEAR_DISK_ON_STARTUP;
+
+    /**
+     * Constructor for the DiskCacheAttributes object
+     */
+    public IndexedDiskCacheAttributes()
+    {
+        super();
+    }
+
+    /**
+     * Gets the maxKeySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @return The maxKeySize value
+     */
+    public int getMaxKeySize()
+    {
+        return this.maxKeySize;
+    }
+
+    /**
+     * Sets the maxKeySize attribute of the DiskCacheAttributes object
+     * <p>
+     * @param maxKeySize The new maxKeySize value
+     */
+    public void setMaxKeySize( int maxKeySize )
+    {
+        this.maxKeySize = maxKeySize;
+
+        // make sure the sizes are in accord with our rule.
+        setMaxRecycleBinSize( maxRecycleBinSize );
+    }
+
+    /**
+     * Gets the optimizeAtRemoveCount attribute of the DiskCacheAttributes object
+     * <p>
+     * @return The optimizeAtRemoveCount value
+     */
+    public int getOptimizeAtRemoveCount()
+    {
+        return this.optimizeAtRemoveCount;
+    }
+
+    /**
+     * Sets the optimizeAtRemoveCount attribute of the DiskCacheAttributes object This number
+     * determines how often the disk cache should run real time optimizations.
+     * <p>
+     * @param cnt The new optimizeAtRemoveCount value
+     */
+    public void setOptimizeAtRemoveCount( int cnt )
+    {
+        this.optimizeAtRemoveCount = cnt;
+    }
+
+    /**
+     * This cannot be larger than the maxKeySize. It wouldn't hurt anything, but it makes the config
+     * necessary. The recycle bin entry willbe at least as large as a key.
+     * <p>
+     * If the maxKeySize is -1 this will be set tot he default, which is 5000.
+     * <p>
+     * @param maxRecycleBinSize The maxRecycleBinSize to set.
+     */
+    public void setMaxRecycleBinSize( int maxRecycleBinSize )
+    {
+        this.maxRecycleBinSize = maxRecycleBinSize;
+    }
+
+    /**
+     * @return Returns the maxRecycleBinSize.
+     */
+    public int getMaxRecycleBinSize()
+    {
+        return maxRecycleBinSize;
+    }
+
+    /**
+     * @param optimizeOnShutdown The optimizeOnShutdown to set.
+     */
+    public void setOptimizeOnShutdown( boolean optimizeOnShutdown )
+    {
+        this.optimizeOnShutdown = optimizeOnShutdown;
+    }
+
+    /**
+     * @return Returns the optimizeOnShutdown.
+     */
+    public boolean isOptimizeOnShutdown()
+    {
+        return optimizeOnShutdown;
+    }
+
+    /**
+     * @param clearDiskOnStartup the clearDiskOnStartup to set
+     */
+    public void setClearDiskOnStartup( boolean clearDiskOnStartup )
+    {
+        this.clearDiskOnStartup = clearDiskOnStartup;
+    }
+
+    /**
+     * @return the clearDiskOnStartup
+     */
+    public boolean isClearDiskOnStartup()
+    {
+        return clearDiskOnStartup;
+    }
+
+    /**
+     * Returns a copy of the attributes.
+     * <p>
+     * @return AuxiliaryCacheAttributes
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            // swallow
+        }
+        return this;
+    }
+
+    /**
+     * Write out the values for debugging purposes.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append( "IndexedDiskCacheAttributes " );
+        str.append( "\n diskPath = " + super.getDiskPath() );
+        str.append( "\n maxPurgatorySize   = " + super.getMaxPurgatorySize() );
+        str.append( "\n maxKeySize  = " + maxKeySize );
+        str.append( "\n maxRecycleBinSize  = " + maxRecycleBinSize );
+        str.append( "\n optimizeAtRemoveCount  = " + optimizeAtRemoveCount );
+        str.append( "\n shutdownSpoolTimeLimit  = " + super.getShutdownSpoolTimeLimit() );
+        str.append( "\n optimizeOnShutdown  = " + optimizeOnShutdown );
+        str.append( "\n clearDiskOnStartup  = " + clearDiskOnStartup );
+        return str.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheFactory.java
new file mode 100644
index 0000000..1757e12
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheFactory.java
@@ -0,0 +1,93 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Creates disk cache instances.
+ */
+public class IndexedDiskCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( IndexedDiskCacheFactory.class );
+
+    /** The auxiliary name. */
+    private String name;
+
+    /**
+     * Get an instance of the IndexDiskCacheManager for the attributes and then get an
+     * IndexedDiskCache from the manager.
+     * <p>
+     * The manager is a singleton.
+     * <p>
+     * One disk cache is returned per region from the manager.
+     * <p>
+     * @param iaca
+     * @param cacheMgr This allows auxiliaries to reference the manager without assuming that it is
+     *            a singleton. This will allow JCS to be a non-singleton. Also, it makes it easier to
+     *            test.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
+                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        IndexedDiskCacheAttributes idca = (IndexedDiskCacheAttributes) iaca;
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Creating DiskCache for attributes = " + idca );
+        }
+        IndexedDiskCacheManager dcm = IndexedDiskCacheManager.getInstance( idca, cacheEventLogger, elementSerializer );
+        return dcm.getCache( idca );
+    }
+
+    /**
+     * Gets the name attribute of the DiskCacheFactory object
+     * <p>
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * Sets the name attribute of the DiskCacheFactory object
+     * <p>
+     * @param name The new name value
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManager.java
new file mode 100644
index 0000000..7e5c8c8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManager.java
@@ -0,0 +1,141 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Cache manager for IndexedDiskCaches. This manages the instances of the disk cache.
+ */
+public class IndexedDiskCacheManager
+    extends AbstractDiskCacheManager
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( IndexedDiskCacheManager.class );
+
+    /** Singleton instance. */
+    private static IndexedDiskCacheManager instance;
+
+    /** Each region has an entry here. */
+    private final Map<String, IndexedDiskCache<?, ?>> caches =
+        new ConcurrentHashMap<String, IndexedDiskCache<?, ?>>();
+
+    /** User configurable attributes */
+    private final IndexedDiskCacheAttributes defaultCacheAttributes;
+
+    /**
+     * Constructor for the IndexedDiskCacheManager object
+     * <p>
+     * @param defaultCacheAttributes Default attributes for caches managed by the instance.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private IndexedDiskCacheManager( IndexedDiskCacheAttributes defaultCacheAttributes,
+         ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        this.defaultCacheAttributes = defaultCacheAttributes;
+        setElementSerializer( elementSerializer );
+        setCacheEventLogger( cacheEventLogger );
+    }
+
+    /**
+     * Gets the singleton instance of the manager
+     * <p>
+     * @param defaultCacheAttributes If the instance has not yet been created, it will be
+     *            initialized with this set of default attributes.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public static IndexedDiskCacheManager getInstance( IndexedDiskCacheAttributes defaultCacheAttributes,
+                                                       ICacheEventLogger cacheEventLogger,
+                                                       IElementSerializer elementSerializer )
+    {
+        synchronized ( IndexedDiskCacheManager.class )
+        {
+            if ( instance == null )
+            {
+                instance = new IndexedDiskCacheManager( defaultCacheAttributes, cacheEventLogger, elementSerializer );
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * Gets an IndexedDiskCache for the supplied name using the default attributes.
+     * <p>
+     * @param cacheName Name that will be used when creating attributes.
+     * @return A cache.
+     */
+    @Override
+    public <K, V> IndexedDiskCache<K, V> getCache( String cacheName )
+    {
+        IndexedDiskCacheAttributes cacheAttributes = (IndexedDiskCacheAttributes) defaultCacheAttributes.copy();
+
+        cacheAttributes.setCacheName( cacheName );
+
+        return getCache( cacheAttributes );
+    }
+
+    /**
+     * Get an IndexedDiskCache for the supplied attributes. Will provide an existing cache for the
+     * name attribute if one has been created, or will create a new cache.
+     * <p>
+     * @param cacheAttributes Attributes the cache should have.
+     * @return A cache, either from the existing set or newly created.
+     */
+    public <K, V> IndexedDiskCache<K, V> getCache( IndexedDiskCacheAttributes cacheAttributes )
+    {
+        IndexedDiskCache<K, V> cache = null;
+
+        String cacheName = cacheAttributes.getCacheName();
+
+        log.debug( "Getting cache named: " + cacheName );
+
+        synchronized ( caches )
+        {
+            // Try to load the cache from the set that have already been
+            // created. This only looks at the name attribute.
+
+            @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+            IndexedDiskCache<K, V> indexedDiskCache = (IndexedDiskCache<K, V>) caches.get( cacheName );
+            cache = indexedDiskCache;
+
+            // If it was not found, create a new one using the supplied
+            // attributes
+
+            if ( cache == null )
+            {
+                cache = new IndexedDiskCache<K, V>( cacheAttributes, getElementSerializer() );
+                cache.setCacheEventLogger( getCacheEventLogger() );
+                caches.put( cacheName, cache );
+            }
+        }
+
+        return cache;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskDumper.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskDumper.java
new file mode 100644
index 0000000..94d9cfe
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskDumper.java
@@ -0,0 +1,57 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+
+/**
+ * Used to dump out a Disk cache from disk for debugging. This is meant to be
+ * run as a command line utility for
+ */
+public class IndexedDiskDumper
+{
+    /**
+     * The main program for the DiskDumper class
+     * <p>
+     * Creates a disk cache and then calls dump, which write out the contents to
+     * a debug log.
+     * <p>
+     * @param args
+     *            The command line arguments
+     */
+    public static void main( String[] args )
+    {
+        if ( args.length != 1 )
+        {
+            System.out.println( "Usage: java org.apache.commons.jcs.auxiliary.disk.DiskDump <cache_name>" );
+            System.exit( 0 );
+        }
+
+        IndexedDiskCacheAttributes attr = new IndexedDiskCacheAttributes();
+
+        attr.setCacheName( args[0] );
+        attr.setDiskPath( args[0] );
+
+        IndexedDiskCache<Serializable, Serializable> dc = new IndexedDiskCache<Serializable, Serializable>( attr );
+        dc.dump( true );
+        System.exit( 0 );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskElementDescriptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskElementDescriptor.java
new file mode 100644
index 0000000..12435c1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskElementDescriptor.java
@@ -0,0 +1,118 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Disk objects are located by descriptor entries. These are saved on shutdown and loaded into
+ * memory on startup.
+ */
+public class IndexedDiskElementDescriptor
+    implements Serializable, Comparable<IndexedDiskElementDescriptor>
+{
+    /** Don't change */
+    private static final long serialVersionUID = -3029163572847659450L;
+
+    /** Position of the cache data entry on disk. */
+    long pos;
+
+    /** Number of bytes the serialized form of the cache data takes. */
+    int len;
+
+    /**
+     * Constructs a usable disk element descriptor.
+     * <p>
+     * @param pos
+     * @param len
+     */
+    public IndexedDiskElementDescriptor( long pos, int len )
+    {
+        this.pos = pos;
+        this.len = len;
+    }
+
+    /**
+     * @return debug string
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "[DED: " );
+        buf.append( " pos = " + pos );
+        buf.append( " len = " + len );
+        buf.append( "]" );
+        return buf.toString();
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode()
+    {
+        return Long.valueOf(this.pos).hashCode() ^ Integer.valueOf(len).hashCode();
+    }
+
+    /**
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o instanceof IndexedDiskElementDescriptor)
+        {
+            return compareTo((IndexedDiskElementDescriptor) o) == 0;
+        }
+
+        return false;
+    }
+
+    /**
+     * Compares based on length.
+     * <p>
+     * @param o Object
+     * @return int
+     */
+    @Override
+    public int compareTo( IndexedDiskElementDescriptor o )
+    {
+        if ( o == null )
+        {
+            return 1;
+        }
+
+        int oLen = o.len;
+        if ( oLen == len )
+        {
+            return 0;
+        }
+        else if ( oLen > len )
+        {
+            return -1;
+        }
+        else if ( oLen < len )
+        {
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java
new file mode 100644
index 0000000..3ba39d9
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCache.java
@@ -0,0 +1,1198 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache;
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is the jdbc disk cache plugin.
+ * <p>
+ * It expects a table created by the following script. The table name is configurable.
+ * <p>
+ *
+ * <pre>
+ *                       drop TABLE JCS_STORE;
+ *                       CREATE TABLE JCS_STORE
+ *                       (
+ *                       CACHE_KEY                  VARCHAR(250)          NOT NULL,
+ *                       REGION                     VARCHAR(250)          NOT NULL,
+ *                       ELEMENT                    BLOB,
+ *                       CREATE_TIME                TIMESTAMP,
+ *                       UPDATE_TIME_SECONDS        BIGINT,
+ *                       MAX_LIFE_SECONDS           BIGINT,
+ *                       SYSTEM_EXPIRE_TIME_SECONDS BIGINT,
+ *                       IS_ETERNAL                 CHAR(1),
+ *                       PRIMARY KEY (CACHE_KEY, REGION)
+ *                       );
+ * </pre>
+ * <p>
+ * The cleanup thread will delete non eternal items where (now - create time) > max life seconds *
+ * 1000
+ * <p>
+ * To speed up the deletion the SYSTEM_EXPIRE_TIME_SECONDS is used instead. It is recommended that
+ * an index be created on this column is you will have over a million records.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCache<K, V>
+    extends AbstractDiskCache<K, V>
+{
+    /** The local logger. */
+    private static final Log log = LogFactory.getLog( JDBCDiskCache.class );
+
+    /** custom serialization */
+    private IElementSerializer elementSerializer = new StandardSerializer();
+
+    /** configuration */
+    private JDBCDiskCacheAttributes jdbcDiskCacheAttributes;
+
+    /** # of times update was called */
+    private int updateCount = 0;
+
+    /** # of times get was called */
+    private int getCount = 0;
+
+    /** # of times getMatching was called */
+    private int getMatchingCount = 0;
+
+    /** if count % interval == 0 then log */
+    private static final int LOG_INTERVAL = 100;
+
+    /** db connection pool */
+    private JDBCDiskCachePoolAccess poolAccess = null;
+
+    /** tracks optimization */
+    private TableState tableState;
+
+    /**
+     * Constructs a JDBC Disk Cache for the provided cache attributes. The table state object is
+     * used to mark deletions.
+     * <p>
+     * @param cattr
+     * @param tableState
+     * @param compositeCacheManager
+     * @throws SQLException if the pool access could not be set up
+     */
+    public JDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState,
+                          ICompositeCacheManager compositeCacheManager ) throws SQLException
+    {
+        super( cattr );
+
+        setTableState( tableState );
+        setJdbcDiskCacheAttributes( cattr );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "jdbcDiskCacheAttributes = " + getJdbcDiskCacheAttributes() );
+        }
+
+        // This initializes the pool access.
+        this.poolAccess = initializePoolAccess( cattr, compositeCacheManager );
+
+        // Initialization finished successfully, so set alive to true.
+        alive = true;
+    }
+
+    /**
+     * Registers the driver and creates a poolAccess class.
+     * <p>
+     * @param cattr
+     * @param compositeCacheManager
+     * @return JDBCDiskCachePoolAccess for testing
+     * @throws SQLException if a database access error occurs
+     */
+    protected JDBCDiskCachePoolAccess initializePoolAccess( JDBCDiskCacheAttributes cattr,
+                                                            ICompositeCacheManager compositeCacheManager ) throws SQLException
+    {
+        JDBCDiskCachePoolAccess poolAccess1 = null;
+        if ( cattr.getConnectionPoolName() != null )
+        {
+            JDBCDiskCachePoolAccessManager manager = JDBCDiskCachePoolAccessManager.getInstance();
+            poolAccess1 = manager.getJDBCDiskCachePoolAccess(
+                    cattr.getConnectionPoolName(),
+                    compositeCacheManager.getConfigurationProperties() );
+        }
+        else
+        {
+            poolAccess1 = JDBCDiskCachePoolAccessManager.createPoolAccess( cattr );
+        }
+        return poolAccess1;
+    }
+
+    /**
+     * Inserts or updates. By default it will try to insert. If the item exists we will get an
+     * error. It will then update. This behavior is configurable. The cache can be configured to
+     * check before inserting.
+     * <p>
+     * @param ce
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> ce )
+    {
+        incrementUpdateCount();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "updating, ce = " + ce );
+        }
+
+        Connection con;
+        try
+        {
+            con = getPoolAccess().getConnection();
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem getting connection.", e );
+            return;
+        }
+
+        try
+        {
+            // TEST
+            Statement sStatement = null;
+            try
+            {
+                sStatement = con.createStatement();
+                alive = true;
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem creating statement.", e );
+                alive = false;
+            }
+            finally
+            {
+                try
+                {
+                    if (sStatement != null)
+                    {
+                        sStatement.close();
+                    }
+                }
+                catch ( SQLException e )
+                {
+                    log.error( "Problem closing statement.", e );
+                }
+            }
+
+            if ( !alive )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Disk is not alive, aborting put." );
+                }
+                return;
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Putting [" + ce.getKey() + "] on disk." );
+            }
+
+            byte[] element;
+
+            try
+            {
+                element = getElementSerializer().serialize( ce );
+            }
+            catch ( IOException e )
+            {
+                log.error( "Could not serialize element", e );
+                return;
+            }
+
+            insertOrUpdate( ce, con, element );
+        }
+        finally
+        {
+            try
+            {
+                con.close();
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem closing connection.", e );
+            }
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            if ( updateCount % LOG_INTERVAL == 0 )
+            {
+                // TODO make a log stats method
+                log.info( "Update Count [" + updateCount + "]" );
+            }
+        }
+    }
+
+    /**
+     * If test before insert it true, we check to see if the element exists. If the element exists
+     * we will update. Otherwise, we try inserting.  If this fails because the item exists, we will
+     * update.
+     * <p>
+     * @param ce
+     * @param con
+     * @param element
+     */
+    private void insertOrUpdate( ICacheElement<K, V> ce, Connection con, byte[] element )
+    {
+        boolean exists = false;
+
+        // First do a query to determine if the element already exists
+        if ( this.getJdbcDiskCacheAttributes().isTestBeforeInsert() )
+        {
+            exists = doesElementExist( ce );
+        }
+
+        // If it doesn't exist, insert it, otherwise update
+        if ( !exists )
+        {
+            exists = insertRow( ce, con, element );
+        }
+
+        // update if it exists.
+        if ( exists )
+        {
+            updateRow( ce, con, element );
+        }
+    }
+
+    /**
+     * This inserts a new row in the database.
+     * <p>
+     * @param ce
+     * @param con
+     * @param element
+     * @return true if the insertion fails because the record exists.
+     */
+    private boolean insertRow( ICacheElement<K, V> ce, Connection con, byte[] element )
+    {
+        boolean exists = false;
+        try
+        {
+            String sqlI = "insert into "
+                + getJdbcDiskCacheAttributes().getTableName()
+                + " (CACHE_KEY, REGION, ELEMENT, MAX_LIFE_SECONDS, IS_ETERNAL, CREATE_TIME, UPDATE_TIME_SECONDS, SYSTEM_EXPIRE_TIME_SECONDS) "
+                + " values (?, ?, ?, ?, ?, ?, ?, ?)";
+
+            PreparedStatement psInsert = con.prepareStatement( sqlI );
+            psInsert.setString( 1, (String) ce.getKey() );
+            psInsert.setString( 2, this.getCacheName() );
+            psInsert.setBytes( 3, element );
+            psInsert.setLong( 4, ce.getElementAttributes().getMaxLife() );
+            if ( ce.getElementAttributes().getIsEternal() )
+            {
+                psInsert.setString( 5, "T" );
+            }
+            else
+            {
+                psInsert.setString( 5, "F" );
+            }
+            Timestamp createTime = new Timestamp( ce.getElementAttributes().getCreateTime() );
+            psInsert.setTimestamp( 6, createTime );
+
+            long now = System.currentTimeMillis() / 1000;
+            psInsert.setLong( 7, now );
+
+            long expireTime = now + ce.getElementAttributes().getMaxLife();
+            psInsert.setLong( 8, expireTime );
+
+            psInsert.execute();
+            psInsert.close();
+        }
+        catch ( SQLException e )
+        {
+            if ( e.toString().indexOf( "Violation of unique index" ) != -1
+                || e.getMessage().indexOf( "Duplicate entry" ) != -1
+                || e.getMessage().indexOf( "duplicate key" ) != -1
+                || e.getMessage().indexOf( "primary key constraint" ) != -1 )
+            {
+                exists = true;
+            }
+            else
+            {
+                log.error( "Could not insert element", e );
+            }
+
+            // see if it exists, if we didn't already
+            if ( !exists && !this.getJdbcDiskCacheAttributes().isTestBeforeInsert() )
+            {
+                exists = doesElementExist( ce );
+            }
+        }
+        return exists;
+    }
+
+    /**
+     * This updates a row in the database.
+     * <p>
+     * @param ce
+     * @param con
+     * @param element
+     */
+    private void updateRow( ICacheElement<K, V> ce, Connection con, byte[] element )
+    {
+        String sqlU = null;
+        try
+        {
+            sqlU = "update " + getJdbcDiskCacheAttributes().getTableName()
+                + " set ELEMENT  = ?, CREATE_TIME = ?, UPDATE_TIME_SECONDS = ?, " + " SYSTEM_EXPIRE_TIME_SECONDS = ? "
+                + " where CACHE_KEY = ? and REGION = ?";
+            PreparedStatement psUpdate = con.prepareStatement( sqlU );
+            psUpdate.setBytes( 1, element );
+
+            Timestamp createTime = new Timestamp( ce.getElementAttributes().getCreateTime() );
+            psUpdate.setTimestamp( 2, createTime );
+
+            long now = System.currentTimeMillis() / 1000;
+            psUpdate.setLong( 3, now );
+
+            long expireTime = now + ce.getElementAttributes().getMaxLife();
+            psUpdate.setLong( 4, expireTime );
+
+            psUpdate.setString( 5, (String) ce.getKey() );
+            psUpdate.setString( 6, this.getCacheName() );
+            psUpdate.execute();
+            psUpdate.close();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "ran update " + sqlU );
+            }
+        }
+        catch ( SQLException e2 )
+        {
+            log.error( "e2 sql [" + sqlU + "] Exception: ", e2 );
+        }
+    }
+
+    /**
+     * Does an element exist for this key?
+     * <p>
+     * @param ce
+     * @return boolean
+     */
+    protected boolean doesElementExist( ICacheElement<K, V> ce )
+    {
+        boolean exists = false;
+
+        Connection con;
+        try
+        {
+            con = getPoolAccess().getConnection();
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem getting connection.", e );
+            return exists;
+        }
+
+        PreparedStatement psSelect = null;
+        try
+        {
+            // don't select the element, since we want this to be fast.
+            String sqlS = "select CACHE_KEY from " + getJdbcDiskCacheAttributes().getTableName()
+                + " where REGION = ? and CACHE_KEY = ?";
+
+            psSelect = con.prepareStatement( sqlS );
+            psSelect.setString( 1, this.getCacheName() );
+            psSelect.setString( 2, (String) ce.getKey() );
+
+            ResultSet rs = psSelect.executeQuery();
+
+            if ( rs.next() )
+            {
+                exists = true;
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "[" + ce.getKey() + "] existing status is " + exists );
+            }
+
+            rs.close();
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem looking for item before insert.", e );
+        }
+        finally
+        {
+            try
+            {
+                if ( psSelect != null )
+                {
+                    psSelect.close();
+                }
+            }
+            catch ( SQLException e1 )
+            {
+                log.error( "Problem closing statement.", e1 );
+            }
+
+            try
+            {
+                con.close();
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem closing connection.", e );
+            }
+        }
+
+        return exists;
+    }
+
+    /**
+     * Queries the database for the value. If it gets a result, the value is deserialized.
+     * <p>
+     * @param key
+     * @return ICacheElement
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#doGet(java.io.Serializable)
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+    {
+        incrementGetCount();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Getting [" + key + "] from disk" );
+        }
+
+        if ( !alive )
+        {
+            return null;
+        }
+
+        ICacheElement<K, V> obj = null;
+
+        byte[] data = null;
+        try
+        {
+            // region, key
+            String selectString = "select ELEMENT from " + getJdbcDiskCacheAttributes().getTableName()
+                + " where REGION = ? and CACHE_KEY = ?";
+
+            Connection con = getPoolAccess().getConnection();
+            try
+            {
+                PreparedStatement psSelect = null;
+                try
+                {
+                    psSelect = con.prepareStatement( selectString );
+                    psSelect.setString( 1, this.getCacheName() );
+                    psSelect.setString( 2, key.toString() );
+
+                    ResultSet rs = psSelect.executeQuery();
+                    try
+                    {
+                        if ( rs.next() )
+                        {
+                            data = rs.getBytes( 1 );
+                        }
+                        if ( data != null )
+                        {
+                            try
+                            {
+                                // USE THE SERIALIZER
+                                obj = getElementSerializer().deSerialize( data, null );
+                            }
+                            catch ( IOException ioe )
+                            {
+                                log.error( "Problem getting item for key [" + key + "]", ioe );
+                            }
+                            catch ( Exception e )
+                            {
+                                log.error( "Problem getting item for key [" + key + "]", e );
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        if ( rs != null )
+                        {
+                            rs.close();
+                        }
+                    }
+                }
+                finally
+                {
+                    if ( psSelect != null )
+                    {
+                        psSelect.close();
+                    }
+                }
+            }
+            finally
+            {
+                if ( con != null )
+                {
+                    con.close();
+                }
+            }
+        }
+        catch ( SQLException sqle )
+        {
+            log.error( "Caught a SQL exception trying to get the item for key [" + key + "]", sqle );
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            if ( getCount % LOG_INTERVAL == 0 )
+            {
+                // TODO make a log stats method
+                log.info( "Get Count [" + getCount + "]" );
+            }
+        }
+        return obj;
+    }
+
+    /**
+     * This will run a like query. It will try to construct a usable query but different
+     * implementations will be needed to adjust the syntax.
+     * <p>
+     * @param pattern
+     * @return key,value map
+     */
+    @Override
+    protected Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+    {
+        incrementGetMatchingCount();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Getting [" + pattern + "] from disk" );
+        }
+
+        if ( !alive )
+        {
+            return null;
+        }
+
+        Map<K, ICacheElement<K, V>> results = new HashMap<K, ICacheElement<K, V>>();
+
+        try
+        {
+            // region, key
+            String selectString = "select CACHE_KEY, ELEMENT from " + getJdbcDiskCacheAttributes().getTableName()
+                + " where REGION = ? and CACHE_KEY like ?";
+
+            Connection con = getPoolAccess().getConnection();
+            try
+            {
+                PreparedStatement psSelect = null;
+                try
+                {
+                    psSelect = con.prepareStatement( selectString );
+                    psSelect.setString( 1, this.getCacheName() );
+                    psSelect.setString( 2, constructLikeParameterFromPattern( pattern ) );
+
+                    ResultSet rs = psSelect.executeQuery();
+                    try
+                    {
+                        while ( rs.next() )
+                        {
+                            String key = rs.getString( 1 );
+                            byte[] data = rs.getBytes( 2 );
+                            if ( data != null )
+                            {
+                                try
+                                {
+                                    // USE THE SERIALIZER
+                                    ICacheElement<K, V> value = getElementSerializer().deSerialize( data, null );
+                                    results.put( (K) key, value );
+                                }
+                                catch ( IOException ioe )
+                                {
+                                    log.error( "Problem getting items for pattern [" + pattern + "]", ioe );
+                                }
+                                catch ( Exception e )
+                                {
+                                    log.error( "Problem getting items for pattern [" + pattern + "]", e );
+                                }
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        if ( rs != null )
+                        {
+                            rs.close();
+                        }
+                    }
+                }
+                finally
+                {
+                    if ( psSelect != null )
+                    {
+                        psSelect.close();
+                    }
+                }
+            }
+            finally
+            {
+                if ( con != null )
+                {
+                    con.close();
+                }
+            }
+        }
+        catch ( SQLException sqle )
+        {
+            log.error( "Caught a SQL exception trying to get items for pattern [" + pattern + "]", sqle );
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            if ( getMatchingCount % LOG_INTERVAL == 0 )
+            {
+                // TODO make a log stats method
+                log.info( "Get Matching Count [" + getMatchingCount + "]" );
+            }
+        }
+        return results;
+    }
+
+    /**
+     * @param pattern
+     * @return String to use in the like query.
+     */
+    public String constructLikeParameterFromPattern( String pattern )
+    {
+        String likePattern = pattern.replaceAll( "\\.\\+", "%" );
+        likePattern = likePattern.replaceAll( "\\.", "_" );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "pattern = [" + likePattern + "]" );
+        }
+
+        return likePattern;
+    }
+
+    /**
+     * Returns true if the removal was successful; or false if there is nothing to remove. Current
+     * implementation always results in a disk orphan.
+     * <p>
+     * @param key
+     * @return boolean
+     */
+    @Override
+    protected boolean processRemove( K key )
+    {
+        // remove single item.
+        String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
+            + " where REGION = ? and CACHE_KEY = ?";
+
+        try
+        {
+            boolean partial = false;
+            if ( key instanceof String && key.toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+            {
+                // remove all keys of the same name group.
+                sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
+                    + " where REGION = ? and CACHE_KEY like ?";
+                partial = true;
+            }
+            Connection con = getPoolAccess().getConnection();
+            PreparedStatement psSelect = null;
+            try
+            {
+                psSelect = con.prepareStatement( sql );
+                psSelect.setString( 1, this.getCacheName() );
+                if ( partial )
+                {
+                    psSelect.setString( 2, key.toString() + "%" );
+                }
+                else
+                {
+                    psSelect.setString( 2, key.toString() );
+                }
+
+                psSelect.executeUpdate();
+
+                alive = true;
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem creating statement. sql [" + sql + "]", e );
+                alive = false;
+            }
+            finally
+            {
+                try
+                {
+                    if ( psSelect != null )
+                    {
+                        psSelect.close();
+                    }
+                    con.close();
+                }
+                catch ( SQLException e1 )
+                {
+                    log.error( "Problem closing statement.", e1 );
+                }
+            }
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem updating cache.", e );
+            reset();
+        }
+        return false;
+    }
+
+    /**
+     * This should remove all elements. The auxiliary can be configured to forbid this behavior. If
+     * remove all is not allowed, the method balks.
+     */
+    @Override
+    protected void processRemoveAll()
+    {
+        // it should never get here formt he abstract dis cache.
+        if ( this.jdbcDiskCacheAttributes.isAllowRemoveAll() )
+        {
+            try
+            {
+                String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName() + " where REGION = ?";
+                Connection con = getPoolAccess().getConnection();
+                PreparedStatement psDelete = null;
+                try
+                {
+                    psDelete = con.prepareStatement( sql );
+                    psDelete.setString( 1, this.getCacheName() );
+                    alive = true;
+                    psDelete.executeUpdate();
+                }
+                catch ( SQLException e )
+                {
+                    log.error( "Problem creating statement.", e );
+                    alive = false;
+                }
+                finally
+                {
+                    try
+                    {
+                        if ( psDelete != null )
+                        {
+                            psDelete.close();
+                        }
+                        con.close();
+                    }
+                    catch ( SQLException e1 )
+                    {
+                        log.error( "Problem closing statement.", e1 );
+                    }
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem removing all.", e );
+                reset();
+            }
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "RemoveAll was requested but the request was not fulfilled: allowRemoveAll is set to false." );
+            }
+        }
+    }
+
+    /**
+     * Removed the expired. (now - create time) > max life seconds * 1000
+     * <p>
+     * @return the number deleted
+     */
+    protected int deleteExpired()
+    {
+        int deleted = 0;
+
+        try
+        {
+            getTableState().setState( TableState.DELETE_RUNNING );
+
+            long now = System.currentTimeMillis() / 1000;
+
+            // This is to slow when we push over a million records
+            // String sql = "delete from " +
+            // getJdbcDiskCacheAttributes().getTableName() + " where REGION = '"
+            // + this.getCacheName() + "' and IS_ETERNAL = 'F' and (" + now
+            // + " - UPDATE_TIME_SECONDS) > MAX_LIFE_SECONDS";
+
+            String sql = "delete from " + getJdbcDiskCacheAttributes().getTableName()
+                + " where IS_ETERNAL = ? and REGION = ? and ? > SYSTEM_EXPIRE_TIME_SECONDS";
+
+            Connection con = getPoolAccess().getConnection();
+            PreparedStatement psDelete = null;
+            try
+            {
+                psDelete = con.prepareStatement( sql );
+                psDelete.setString( 1, "F" );
+                psDelete.setString( 2, this.getCacheName() );
+                psDelete.setLong( 3, now );
+
+                alive = true;
+
+                deleted = psDelete.executeUpdate();
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem creating statement.", e );
+                alive = false;
+            }
+            finally
+            {
+                try
+                {
+                    if ( psDelete != null )
+                    {
+                        psDelete.close();
+                    }
+                    con.close();
+                }
+                catch ( SQLException e1 )
+                {
+                    log.error( "Problem closing statement.", e1 );
+                }
+            }
+            logApplicationEvent( getAuxiliaryCacheAttributes().getName(), "deleteExpired",
+                                 "Deleted expired elements.  URL: " + getDiskLocation() );
+        }
+        catch ( Exception e )
+        {
+            logError( getAuxiliaryCacheAttributes().getName(), "deleteExpired", e.getMessage() + " URL: "
+                + getDiskLocation() );
+            log.error( "Problem removing expired elements from the table.", e );
+            reset();
+        }
+        finally
+        {
+            getTableState().setState( TableState.FREE );
+        }
+
+        return deleted;
+    }
+
+    /**
+     * Typically this is used to handle errors by last resort, force content update, or removeall
+     */
+    public void reset()
+    {
+        // nothing
+    }
+
+    /** Shuts down the pool */
+    @Override
+    public void processDispose()
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, (K)"none", ICacheEventLogger.DISPOSE_EVENT );
+        try
+        {
+            try
+            {
+                getPoolAccess().shutdownDriver();
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem shutting down.", e );
+            }
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Returns the current cache size. Just does a count(*) for the region.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        int size = 0;
+
+        // region, key
+        String selectString = "select count(*) from " + getJdbcDiskCacheAttributes().getTableName()
+            + " where REGION = ?";
+
+        final JDBCDiskCachePoolAccess pool = getPoolAccess();
+        if (pool == null) {
+            return size;
+        }
+        Connection con;
+        try
+        {
+            con = pool.getConnection();
+        }
+        catch ( SQLException e1 )
+        {
+            log.error( "Problem getting connection.", e1 );
+            return size;
+        }
+        try
+        {
+            PreparedStatement psSelect = null;
+            try
+            {
+                psSelect = con.prepareStatement( selectString );
+                psSelect.setString( 1, this.getCacheName() );
+                ResultSet rs = null;
+
+                rs = psSelect.executeQuery();
+                try
+                {
+                    if ( rs.next() )
+                    {
+                        size = rs.getInt( 1 );
+                    }
+                }
+                finally
+                {
+                    if ( rs != null )
+                    {
+                        rs.close();
+                    }
+                }
+            }
+            finally
+            {
+                if ( psSelect != null )
+                {
+                    psSelect.close();
+                }
+            }
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem getting size.", e );
+        }
+        finally
+        {
+            try
+            {
+                con.close();
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem closing connection.", e );
+            }
+        }
+        return size;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        throw new UnsupportedOperationException( "Groups not implemented." );
+        // return null;
+    }
+
+    /**
+     * @param elementSerializer The elementSerializer to set.
+     */
+    @Override
+    public void setElementSerializer( IElementSerializer elementSerializer )
+    {
+        this.elementSerializer = elementSerializer;
+    }
+
+    /**
+     * @return Returns the elementSerializer.
+     */
+    @Override
+    public IElementSerializer getElementSerializer()
+    {
+        return elementSerializer;
+    }
+
+    /** safely increment */
+    private synchronized void incrementUpdateCount()
+    {
+        updateCount++;
+    }
+
+    /** safely increment */
+    private synchronized void incrementGetCount()
+    {
+        getCount++;
+    }
+
+    /** safely increment */
+    private synchronized void incrementGetMatchingCount()
+    {
+        getMatchingCount++;
+    }
+
+    /**
+     * @param jdbcDiskCacheAttributes The jdbcDiskCacheAttributes to set.
+     */
+    protected void setJdbcDiskCacheAttributes( JDBCDiskCacheAttributes jdbcDiskCacheAttributes )
+    {
+        this.jdbcDiskCacheAttributes = jdbcDiskCacheAttributes;
+    }
+
+    /**
+     * @return Returns the jdbcDiskCacheAttributes.
+     */
+    protected JDBCDiskCacheAttributes getJdbcDiskCacheAttributes()
+    {
+        return jdbcDiskCacheAttributes;
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return this.getJdbcDiskCacheAttributes();
+    }
+
+    /**
+     * Extends the parent stats.
+     * <p>
+     * @return IStats
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = super.getStatistics();
+        stats.setTypeName( "JDBC/Abstract Disk Cache" );
+
+        List<IStatElement<?>> elems = stats.getStatElements();
+
+        elems.add(new StatElement<Integer>( "Update Count", Integer.valueOf(updateCount) ) );
+        elems.add(new StatElement<Integer>( "Get Count", Integer.valueOf(getCount) ) );
+        elems.add(new StatElement<Integer>( "Get Matching Count", Integer.valueOf(getMatchingCount) ) );
+
+        final JDBCDiskCachePoolAccess pool = getPoolAccess();
+
+        elems.add(new StatElement<Integer>( "Size",
+                Integer.valueOf(pool != null ? getSize() : -1) ) );
+        elems.add(new StatElement<Integer>( "Active DB Connections",
+                Integer.valueOf(pool != null ? pool.getNumActiveInPool() : -1) ) );
+        elems.add(new StatElement<Integer>( "Idle DB Connections",
+                Integer.valueOf(pool != null ? pool.getNumIdleInPool() : -1) ) );
+        elems.add(new StatElement<String>( "DB URL",
+                pool != null ? pool.getPoolUrl() : getJdbcDiskCacheAttributes().getUrl()) );
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * Returns the name of the table.
+     * <p>
+     * @return the table name or UNDEFINED
+     */
+    protected String getTableName()
+    {
+        String name = "UNDEFINED";
+        if ( this.getJdbcDiskCacheAttributes() != null )
+        {
+            name = this.getJdbcDiskCacheAttributes().getTableName();
+        }
+        return name;
+    }
+
+    /**
+     * @param tableState The tableState to set.
+     */
+    public void setTableState( TableState tableState )
+    {
+        this.tableState = tableState;
+    }
+
+    /**
+     * @return Returns the tableState.
+     */
+    public TableState getTableState()
+    {
+        return tableState;
+    }
+
+    /**
+     * This is used by the event logging.
+     * <p>
+     * @return the location of the disk, either path or ip.
+     */
+    @Override
+    protected String getDiskLocation()
+    {
+        return this.jdbcDiskCacheAttributes.getUrl();
+    }
+
+    /**
+     * Public so managers can access it.
+     * @return the poolAccess
+     */
+    public JDBCDiskCachePoolAccess getPoolAccess()
+    {
+        return poolAccess;
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @return this.getStats();
+     */
+    @Override
+    public String toString()
+    {
+        return this.getStats();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java
new file mode 100644
index 0000000..a00622e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheAttributes.java
@@ -0,0 +1,290 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheAttributes;
+
+/**
+ * The configurator will set these values based on what is in the cache.ccf file.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCacheAttributes
+    extends AbstractDiskCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -6535808344813320062L;
+
+    /** default */
+    private static final String DEFAULT_TABLE_NAME = "JCS_STORE";
+
+    /** DB username */
+    private String userName;
+
+    /** DB password */
+    private String password;
+
+    /** URL for the db */
+    private String url;
+
+    /** The name of the database. */
+    private String database = "";
+
+    /** The driver */
+    private String driverClassName;
+
+    /** The table name */
+    private String tableName = DEFAULT_TABLE_NAME;
+
+    /** If false we will insert and if it fails we will update. */
+    private boolean testBeforeInsert = true;
+
+    /** This is the default limit on the maximum number of active connections. */
+    public static final int DEFAULT_MAX_ACTIVE = 10;
+
+    /** Max connections allowed */
+    private int maxActive = DEFAULT_MAX_ACTIVE;
+
+    /** This is the default setting for the cleanup routine. */
+    public static final int DEFAULT_SHRINKER_INTERVAL_SECONDS = 300;
+
+    /** How often should we remove expired. */
+    private int shrinkerIntervalSeconds = DEFAULT_SHRINKER_INTERVAL_SECONDS;
+
+    /** Should we remove expired in the background. */
+    private boolean UseDiskShrinker = true;
+
+    /**
+     * If a pool name is supplied, the manager will attempt to load it. It should be configured in a
+     * separate section as follows. Assuming the name is "MyPool":
+     *
+     * <pre>
+     * jcs.jdbcconnectionpool.MyPool.attributes.userName=MyUserName
+     * jcs.jdbcconnectionpool.MyPool.attributes.password=MyPassword
+     * jcs.jdbcconnectionpool.MyPool.attributes.url=MyUrl
+     * jcs.jdbcconnectionpool.MyPool.attributes.maxActive=MyMaxActive
+     * jcs.jdbcconnectionpool.MyPool.attributes.driverClassName=MyDriverClassName
+     * </pre>
+     */
+    private String connectionPoolName;
+
+    /**
+     * @param userName The userName to set.
+     */
+    public void setUserName( String userName )
+    {
+        this.userName = userName;
+    }
+
+    /**
+     * @return Returns the userName.
+     */
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    /**
+     * @param password The password to set.
+     */
+    public void setPassword( String password )
+    {
+        this.password = password;
+    }
+
+    /**
+     * @return Returns the password.
+     */
+    public String getPassword()
+    {
+        return password;
+    }
+
+    /**
+     * @param url The url to set.
+     */
+    public void setUrl( String url )
+    {
+        this.url = url;
+    }
+
+    /**
+     * @return Returns the url.
+     */
+    public String getUrl()
+    {
+        return url;
+    }
+
+    /**
+     * This is appended to the url.
+     * @param database The database to set.
+     */
+    public void setDatabase( String database )
+    {
+        this.database = database;
+    }
+
+    /**
+     * @return Returns the database.
+     */
+    public String getDatabase()
+    {
+        return database;
+    }
+
+    /**
+     * @param driverClassName The driverClassName to set.
+     */
+    public void setDriverClassName( String driverClassName )
+    {
+        this.driverClassName = driverClassName;
+    }
+
+    /**
+     * @return Returns the driverClassName.
+     */
+    public String getDriverClassName()
+    {
+        return driverClassName;
+    }
+
+    /**
+     * @param tableName The tableName to set.
+     */
+    public void setTableName( String tableName )
+    {
+        this.tableName = tableName;
+    }
+
+    /**
+     * @return Returns the tableName.
+     */
+    public String getTableName()
+    {
+        return tableName;
+    }
+
+    /**
+     * If this is true then the disk cache will check to see if the item already exists in the
+     * database. If it is false, it will try to insert. If the insert fails it will try to update.
+     * <p>
+     * @param testBeforeInsert The testBeforeInsert to set.
+     */
+    public void setTestBeforeInsert( boolean testBeforeInsert )
+    {
+        this.testBeforeInsert = testBeforeInsert;
+    }
+
+    /**
+     * @return Returns the testBeforeInsert.
+     */
+    public boolean isTestBeforeInsert()
+    {
+        return testBeforeInsert;
+    }
+
+    /**
+     * @param maxActive The maxActive to set.
+     */
+    public void setMaxActive( int maxActive )
+    {
+        this.maxActive = maxActive;
+    }
+
+    /**
+     * @return Returns the maxActive.
+     */
+    public int getMaxActive()
+    {
+        return maxActive;
+    }
+
+    /**
+     * @param shrinkerIntervalSecondsArg The shrinkerIntervalSeconds to set.
+     */
+    public void setShrinkerIntervalSeconds( int shrinkerIntervalSecondsArg )
+    {
+        this.shrinkerIntervalSeconds = shrinkerIntervalSecondsArg;
+    }
+
+    /**
+     * @return Returns the shrinkerIntervalSeconds.
+     */
+    public int getShrinkerIntervalSeconds()
+    {
+        return shrinkerIntervalSeconds;
+    }
+
+    /**
+     * @param useDiskShrinker The useDiskShrinker to set.
+     */
+    public void setUseDiskShrinker( boolean useDiskShrinker )
+    {
+        UseDiskShrinker = useDiskShrinker;
+    }
+
+    /**
+     * @return Returns the useDiskShrinker.
+     */
+    public boolean isUseDiskShrinker()
+    {
+        return UseDiskShrinker;
+    }
+
+    /**
+     * @param connectionPoolName the connectionPoolName to set
+     */
+    public void setConnectionPoolName( String connectionPoolName )
+    {
+        this.connectionPoolName = connectionPoolName;
+    }
+
+    /**
+     * @return the connectionPoolName
+     */
+    public String getConnectionPoolName()
+    {
+        return connectionPoolName;
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @return debug string with most of the properties.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nJDBCCacheAttributes" );
+        buf.append( "\n UserName [" + getUserName() + "]" );
+        buf.append( "\n Url [" + getUrl() + "]" );
+        buf.append( "\n Database [" + getDatabase() + "]" );
+        buf.append( "\n DriverClassName [" + getDriverClassName() + "]" );
+        buf.append( "\n TableName [" + getTableName() + "]" );
+        buf.append( "\n TestBeforeInsert [" + isTestBeforeInsert() + "]" );
+        buf.append( "\n MaxActive [" + getMaxActive() + "]" );
+        buf.append( "\n AllowRemoveAll [" + isAllowRemoveAll() + "]" );
+        buf.append( "\n ShrinkerIntervalSeconds [" + getShrinkerIntervalSeconds() + "]" );
+        buf.append( "\n UseDiskShrinker [" + isUseDiskShrinker() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheFactory.java
new file mode 100644
index 0000000..7221d9d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheFactory.java
@@ -0,0 +1,80 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/**
+ * This factory should create mysql disk caches.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** Name of the factory */
+    private String name = "JDBCDiskCacheFactory";
+
+    /**
+     * This factory method should create an instance of the mysqlcache.
+     * <p>
+     * @param rawAttr
+     * @param compositeCacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr, ICompositeCacheManager compositeCacheManager,
+                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        JDBCDiskCacheManager diskCacheManager = JDBCDiskCacheManager.getInstance( (JDBCDiskCacheAttributes) rawAttr,
+                                                                                  compositeCacheManager,
+                                                                                  cacheEventLogger, elementSerializer );
+        return diskCacheManager.getCache( (JDBCDiskCacheAttributes) rawAttr );
+    }
+
+    /**
+     * The name of the factory.
+     * <p>
+     * @param nameArg
+     */
+    @Override
+    public void setName( String nameArg )
+    {
+        name = nameArg;
+    }
+
+    /**
+     * Returns the display name
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java
new file mode 100644
index 0000000..4894ae1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManager.java
@@ -0,0 +1,148 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import java.sql.SQLException;
+
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This manages instances of the jdbc disk cache. It maintains one for each region. One for all
+ * regions would work, but this gives us more detailed stats by region.
+ */
+public class JDBCDiskCacheManager
+    extends JDBCDiskCacheManagerAbstractTemplate
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( JDBCDiskCacheManager.class );
+
+    /** Singleton instance */
+    private static JDBCDiskCacheManager instance;
+
+    /** User configurable settings. */
+    private final JDBCDiskCacheAttributes defaultJDBCDiskCacheAttributes;
+
+    /** The cache manager instance */
+    private ICompositeCacheManager compositeCacheManager;
+
+    /**
+     * Constructor for the HSQLCacheManager object
+     * <p>
+     * @param cattr
+     * @param compositeCacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private JDBCDiskCacheManager( JDBCDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager, ICacheEventLogger cacheEventLogger,
+          IElementSerializer elementSerializer )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Creating JDBCDiskCacheManager with " + cattr );
+        }
+        defaultJDBCDiskCacheAttributes = cattr;
+        setElementSerializer( elementSerializer );
+        setCacheEventLogger( cacheEventLogger );
+        setCompositeCacheManager( compositeCacheManager );
+    }
+
+    /**
+     * Gets the defaultCattr attribute of the HSQLCacheManager object
+     * <p>
+     * @return The defaultCattr value
+     */
+    public JDBCDiskCacheAttributes getDefaultJDBCDiskCacheAttributes()
+    {
+        return defaultJDBCDiskCacheAttributes;
+    }
+
+    /**
+     * Gets the instance attribute of the HSQLCacheManager class
+     * <p>
+     * @param cattr
+     * @param compositeCacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public static JDBCDiskCacheManager getInstance( JDBCDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager, ICacheEventLogger cacheEventLogger,
+        IElementSerializer elementSerializer )
+    {
+        synchronized ( JDBCDiskCacheManager.class )
+        {
+            if ( instance == null )
+            {
+                instance = new JDBCDiskCacheManager( cattr, compositeCacheManager, cacheEventLogger, elementSerializer );
+            }
+        }
+        clients++;
+        return instance;
+    }
+
+    /**
+     * Gets the cache attribute of the HSQLCacheManager object
+     * <p>
+     * @param cacheName
+     * @return The cache value
+     */
+    @Override
+    public <K, V> JDBCDiskCache<K, V> getCache( String cacheName )
+    {
+        JDBCDiskCacheAttributes cattr = (JDBCDiskCacheAttributes) defaultJDBCDiskCacheAttributes.copy();
+        cattr.setCacheName( cacheName );
+        return getCache( cattr );
+    }
+
+    /**
+     * Creates a JDBCDiskCache using the supplied attributes.
+     * <p>
+     * @param cattr
+     * @param tableState
+     * @return AuxiliaryCache
+     * @throws SQLException if database operations fail
+     */
+    @Override
+    protected <K, V> JDBCDiskCache<K, V> createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState ) throws SQLException
+    {
+        JDBCDiskCache<K, V> raf;
+        raf = new JDBCDiskCache<K, V>( cattr, tableState, getCompositeCacheManager() );
+        return raf;
+    }
+
+    /**
+     * @param compositeCacheManager the compositeCacheManager to set
+     */
+    protected void setCompositeCacheManager( ICompositeCacheManager compositeCacheManager )
+    {
+        this.compositeCacheManager = compositeCacheManager;
+    }
+
+    /**
+     * @return the compositeCacheManager
+     */
+    protected ICompositeCacheManager getCompositeCacheManager()
+    {
+        return compositeCacheManager;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java
new file mode 100644
index 0000000..d6c17e2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerAbstractTemplate.java
@@ -0,0 +1,213 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.disk.AbstractDiskCacheManager;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class serves as an abstract template for JDBCDiskCache Manager. The MySQL JDBC Disk Cache
+ * needs many of the same features as the generic manager.
+ * <p>
+ * @author Aaron Smuts
+ */
+public abstract class JDBCDiskCacheManagerAbstractTemplate
+    extends AbstractDiskCacheManager
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( JDBCDiskCacheManagerAbstractTemplate.class );
+
+    /** Incremented on getInstance, decremented on release. */
+    protected static int clients; // TODO needs to be made private and synchronized and/or turned into AtomicInt
+
+    /** A map of JDBCDiskCache objects to region names. */
+    private static Map<String, JDBCDiskCache<?, ?>> caches =
+        new ConcurrentHashMap<String, JDBCDiskCache<?, ?>>();
+
+    /**
+     * A map of TableState objects to table names. Each cache has a table state object, which is
+     * used to determine if any long processes such as deletes or optimizations are running.
+     */
+    protected static Map<String, TableState> tableStates = new ConcurrentHashMap<String, TableState>();
+
+    /** The background disk shrinker, one for all regions. */
+    private ScheduledExecutorService shrinkerDaemon; // TODO this is not accessed in a threadsafe way. Perhaps use IODH idiom?
+
+    /**
+     * A map of table name to shrinker threads. This allows each table to have a different setting.
+     * It assumes that there is only one jdbc disk cache auxiliary defined per table.
+     */
+    private final Map<String, ShrinkerThread> shrinkerThreadMap = new ConcurrentHashMap<String, ShrinkerThread>();
+
+    /**
+     * Children must implement this method.
+     * <p>
+     * @param cattr
+     * @param tableState An object used by multiple processes to indicate state.
+     * @return AuxiliaryCache -- a JDBCDiskCache
+     */
+    protected abstract <K, V> JDBCDiskCache<K, V> createJDBCDiskCache( JDBCDiskCacheAttributes cattr, TableState tableState )
+    	throws SQLException;
+
+    /**
+     * Creates a JDBCDiskCache for the region if one doesn't exist, else it returns the pre-created
+     * instance. It also adds the region to the shrinker thread if needed.
+     * <p>
+     * @param cattr
+     * @return The cache value
+     */
+    public <K, V> JDBCDiskCache<K, V> getCache( JDBCDiskCacheAttributes cattr )
+    {
+        JDBCDiskCache<K, V> diskCache = null;
+
+        log.debug( "cacheName = " + cattr.getCacheName() );
+
+        synchronized ( caches )
+        {
+            @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+            JDBCDiskCache<K, V> jdbcDiskCache = (JDBCDiskCache<K, V>) caches.get( cattr.getCacheName() );
+            diskCache = jdbcDiskCache;
+
+            if ( diskCache == null )
+            {
+                TableState tableState = tableStates.get( cattr.getTableName() );
+
+                if ( tableState == null )
+                {
+                    tableState = new TableState( cattr.getTableName() );
+                }
+
+                try 
+                {
+					diskCache = createJDBCDiskCache( cattr, tableState );
+					diskCache.setCacheEventLogger( getCacheEventLogger() );
+					diskCache.setElementSerializer( getElementSerializer() );
+					caches.put( cattr.getCacheName(), diskCache );
+				} 
+                catch (SQLException e) 
+                {
+                    log.error( "Failed to create cache " + cattr.getCacheName(), e );
+				}
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "JDBC cache = " + diskCache );
+        }
+
+        // create a shrinker if we need it.
+        createShrinkerWhenNeeded( cattr, diskCache );
+
+        return diskCache;
+    }
+
+    /**
+     * If UseDiskShrinker is true then we will create a shrinker daemon if necessary.
+     * <p>
+     * @param cattr
+     * @param raf
+     */
+    protected void createShrinkerWhenNeeded( JDBCDiskCacheAttributes cattr, AuxiliaryCache<?, ?> raf )
+    {
+        // add cache to shrinker.
+        if ( cattr.isUseDiskShrinker() )
+        {
+            if ( shrinkerDaemon == null )
+            {
+                shrinkerDaemon = Executors.newScheduledThreadPool(2,
+                        new DaemonThreadFactory("JCS-JDBCDiskCacheManager-", Thread.MIN_PRIORITY));
+            }
+
+            ShrinkerThread shrinkerThread = shrinkerThreadMap.get( cattr.getTableName() );
+            if ( shrinkerThread == null )
+            {
+                shrinkerThread = new ShrinkerThread();
+                shrinkerThreadMap.put( cattr.getTableName(), shrinkerThread );
+
+                long intervalMillis = Math.max( 999, cattr.getShrinkerIntervalSeconds() * 1000 );
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Setting the shrinker to run every [" + intervalMillis + "] ms. for table ["
+                        + cattr.getTableName() + "]" );
+                }
+                shrinkerDaemon.scheduleAtFixedRate(shrinkerThread, 0, intervalMillis, TimeUnit.MILLISECONDS);
+            }
+            shrinkerThread.addDiskCacheToShrinkList( (JDBCDiskCache<?, ?>) raf );
+        }
+    }
+
+    /**
+     * @param name
+     */
+    public void freeCache( String name )
+    {
+        JDBCDiskCache<?, ?> raf = caches.get( name );
+        if ( raf != null )
+        {
+            try
+            {
+                raf.dispose();
+            }
+            catch ( IOException e )
+            {
+                log.error( "Problem disposing of disk.", e );
+            }
+        }
+    }
+
+    /** Disposes of all regions. */
+    public void release()
+    {
+        // Wait until called by the last client
+        if ( --clients != 0 )
+        {
+            return;
+        }
+        synchronized ( caches )
+        {
+            for (JDBCDiskCache<?, ?> raf : caches.values())
+            {
+                if ( raf != null )
+                {
+                    try
+                    {
+                        raf.dispose();
+                    }
+                    catch ( IOException e )
+                    {
+                        log.error( "Problem disposing of disk.", e );
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java
new file mode 100644
index 0000000..52d1b5b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccess.java
@@ -0,0 +1,271 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.dbcp.ConnectionFactory;
+import org.apache.commons.dbcp.DriverManagerConnectionFactory;
+import org.apache.commons.dbcp.PoolableConnectionFactory;
+import org.apache.commons.dbcp.PoolingDriver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.pool.ObjectPool;
+import org.apache.commons.pool.impl.GenericObjectPool;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+/**
+ * This class provides access to the connection pool. It ensures that the various resources that
+ * need to access the tables will be able to use the same pool.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCachePoolAccess
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( JDBCDiskCachePoolAccess.class );
+
+    /** The default Pool Name to which the connection pool will be keyed. */
+    public static final String DEFAULT_POOL_NAME = "jcs";
+
+    /** The name of the pool. */
+    private String poolName = DEFAULT_POOL_NAME;
+
+    /** default jdbc driver. */
+    private static final String DRIVER_NAME = "jdbc:apache:commons:dbcp:";
+
+    /**
+     * Configures the pool name to use for the pool access.
+     * <p>
+     * This pool name should be unique to the database. It is used as part of the URL each time we
+     * lookup a connection from the driver manager.
+     * <p>
+     * @param poolName
+     */
+    public JDBCDiskCachePoolAccess( String poolName )
+    {
+        // we can default to jcs if there is only one database in use.
+        if ( poolName != null )
+        {
+            setPoolName( poolName );
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "The pool name supplied was null. Using default instead." );
+            }
+        }
+    }
+
+    /**
+     * Gets a connection from the pool.
+     * <p>
+     * @return Connection
+     * @throws SQLException
+     */
+    public Connection getConnection()
+        throws SQLException
+    {
+        Connection con;
+        try
+        {
+            con = DriverManager.getConnection( getPoolUrl() );
+        }
+        catch ( SQLException e )
+        {
+            log.error( "Problem getting connection.", e );
+            throw e;
+        }
+
+        return con;
+    }
+
+    /**
+     * How many are idle in the pool.
+     * <p>
+     * @return number idle
+     */
+    public int getNumIdleInPool()
+    {
+        int numIdle = 0;
+        try
+        {
+            PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME );
+            ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( connectionPool );
+            }
+            numIdle = connectionPool.getNumIdle();
+        }
+        catch ( SQLException e )
+        {
+            log.error( e );
+        }
+        return numIdle;
+    }
+
+    /**
+     * How many are active in the pool.
+     * <p>
+     * @return number active
+     */
+    public int getNumActiveInPool()
+    {
+        int numActive = 0;
+        try
+        {
+            PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME );
+            ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( connectionPool );
+            }
+            numActive = connectionPool.getNumActive();
+        }
+        catch ( SQLException e )
+        {
+            log.error( e );
+        }
+        return numActive;
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void shutdownDriver()
+        throws SQLException
+    {
+        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME );
+        driver.closePool( this.getPoolName() );
+    }
+
+    /**
+     * @return Returns the poolUrl.
+     */
+    public String getPoolUrl()
+    {
+        return DRIVER_NAME + this.getPoolName();
+    }
+
+    /**
+     * @param poolName The poolName to set.
+     */
+    public void setPoolName( String poolName ) // TODO does it make sense to change the pool name after construction?
+    {
+        this.poolName = poolName;
+    }
+
+    /**
+     * @return Returns the poolName.
+     */
+    public String getPoolName()
+    {
+        return poolName;
+    }
+
+    /**
+     * @param connectURI
+     * @param userName
+     * @param password
+     * @param maxActive max connections
+     * @throws SQLException if a database access error occurs
+     */
+    public void setupDriver( String connectURI, String userName, String password, int maxActive )
+        throws SQLException
+    {
+        // First, we'll need a ObjectPool that serves as the
+        // actual pool of connections.
+        // We'll use a GenericObjectPool instance, although
+        // any ObjectPool implementation will suffice.
+        ObjectPool connectionPool = new GenericObjectPool( null, maxActive );
+
+        // TODO make configurable
+        // By default the size is 8!!!!!!!
+        ( (GenericObjectPool) connectionPool ).setMaxIdle( -1 );
+
+        // Next, we'll create a ConnectionFactory that the
+        // pool will use to create Connections.
+        // We'll use the DriverManagerConnectionFactory,
+        // using the connect string passed in the command line
+        // arguments.
+        // Properties props = new Properties();
+        // props.setProperty( "user", userName );
+        // props.setProperty( "password", password );
+        ConnectionFactory connectionFactory = new DriverManagerConnectionFactory( connectURI, userName, password );
+
+        // Now we'll create the PoolableConnectionFactory, which wraps
+        // the "real" Connections created by the ConnectionFactory with
+        // the classes that implement the pooling functionality.
+        // PoolableConnectionFactory poolableConnectionFactory =
+        new PoolableConnectionFactory( connectionFactory, connectionPool, null, null, false, true );
+
+        // Finally, we create the PoolingDriver itself...
+        try
+        {
+            // com.mysql.jdbc.Driver
+            Class.forName( "org.apache.commons.dbcp.PoolingDriver" );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            throw new SQLException("Couldn't find class for driver [org.apache.commons.dbcp.PoolingDriver]", e );
+        }
+        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME );
+
+        // ...and register our pool with it.
+        driver.registerPool( this.getPoolName(), connectionPool );
+
+        // Now we can just use the connect string
+        // "jdbc:apache:commons:dbcp:jcs"
+        // to access our pool of Connections.
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void logDriverStats()
+        throws SQLException
+    {
+        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver( DRIVER_NAME );
+        ObjectPool connectionPool = driver.getConnectionPool( this.getPoolName() );
+
+        if ( connectionPool != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( connectionPool );
+            }
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "NumActive: " + getNumActiveInPool() );
+                log.info( "NumIdle: " + getNumIdleInPool() );
+            }
+        }
+        else
+        {
+            log.warn( "Could not find pool." );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessAttributes.java
new file mode 100644
index 0000000..251bd21
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessAttributes.java
@@ -0,0 +1,179 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+/** These are used to configure the JDBCDiskCachePoolAccess class. */
+public class JDBCDiskCachePoolAccessAttributes
+{
+    /** The name of the pool.  */
+    private String poolName;
+
+    /** URI to the db. */
+    private String url;
+
+    /** username for the db */
+    private String userName;
+
+    /** password for the database */
+    private String password;
+
+    /** This is the default limit on the maximum number of active connections. */
+    public static final int DEFAULT_MAX_ACTIVE = 10;
+
+    /** Max connections allowed */
+    private int maxActive = DEFAULT_MAX_ACTIVE;
+
+    /** The name of the database. */
+    private String database = "";
+
+    /** The driver */
+    private String driverClassName;
+
+    /**
+     * @param poolName the poolName to set
+     */
+    public void setPoolName( String poolName )
+    {
+        this.poolName = poolName;
+    }
+
+    /**
+     * @return the poolName
+     */
+    public String getPoolName()
+    {
+        return poolName;
+    }
+
+    /**
+     * @param connectURI the connectURI to set
+     */
+    public void setUrl( String connectURI )
+    {
+        this.url = connectURI;
+    }
+
+    /**
+     * @return the connectURI
+     */
+    public String getUrl()
+    {
+        return url;
+    }
+
+    /**
+     * @param userName the userName to set
+     */
+    public void setUserName( String userName )
+    {
+        this.userName = userName;
+    }
+
+    /**
+     * @return the userName
+     */
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    /**
+     * @param password the password to set
+     */
+    public void setPassword( String password )
+    {
+        this.password = password;
+    }
+
+    /**
+     * @return the password
+     */
+    public String getPassword()
+    {
+        return password;
+    }
+
+    /**
+     * @param maxActive the maxActive to set
+     */
+    public void setMaxActive( int maxActive )
+    {
+        this.maxActive = maxActive;
+    }
+
+    /**
+     * @return the maxActive
+     */
+    public int getMaxActive()
+    {
+        return maxActive;
+    }
+
+    /**
+     * @param database the database to set
+     */
+    public void setDatabase( String database )
+    {
+        this.database = database;
+    }
+
+    /**
+     * @return the database
+     */
+    public String getDatabase()
+    {
+        return database;
+    }
+
+    /**
+     * @param driverClassName the driverClassName to set
+     */
+    public void setDriverClassName( String driverClassName )
+    {
+        this.driverClassName = driverClassName;
+    }
+
+    /**
+     * @return the driverClassName
+     */
+    public String getDriverClassName()
+    {
+        return driverClassName;
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @return debug string with most of the properties.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nJDBCDiskCachePoolAccessAttributes" );
+        buf.append( "\n UserName [" + getUserName() + "]" );
+        buf.append( "\n Url [" + getUrl() + "]" );
+        buf.append( "\n PoolName [" + getPoolName() + "]" );
+        buf.append( "\n Database [" + getDatabase() + "]" );
+        buf.append( "\n DriverClassName [" + getDriverClassName() + "]" );
+        buf.append( "\n MaxActive [" + getMaxActive() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManager.java
new file mode 100644
index 0000000..f010cc3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManager.java
@@ -0,0 +1,211 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import java.sql.SQLException;
+import org.apache.commons.jcs.utils.config.PropertySetter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Manages JDBCDiskCachePoolAccess instances. If a connectionPoolName value is supplied, the JDBC
+ * disk cache will try to use this manager to create a pool. Assuming the name is "MyPool":
+ *
+ * <pre>
+ * jcs.jdbcconnectionpool.MyPool.attributes.userName=MyUserName
+ * jcs.jdbcconnectionpool.MyPool.attributes.password=MyPassword
+ * jcs.jdbcconnectionpool.MyPool.attributes.url=MyUrl
+ * jcs.jdbcconnectionpool.MyPool.attributes.maxActive=MyMaxActive
+ * jcs.jdbcconnectionpool.MyPool.attributes.driverClassName=MyDriverClassName
+ * </pre>
+ */
+public class JDBCDiskCachePoolAccessManager
+{
+    /** Singleton instance */
+    private static JDBCDiskCachePoolAccessManager instance;
+
+    /** Pool name to JDBCDiskCachePoolAccess */
+    private final Map<String, JDBCDiskCachePoolAccess> pools = new HashMap<String, JDBCDiskCachePoolAccess>();
+
+    /** props prefix */
+    public static final String POOL_CONFIGURATION_PREFIX = "jcs.jdbcconnectionpool.";
+
+    /** .attributes */
+    public static final String ATTRIBUTE_PREFIX = ".attributes";
+
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( JDBCDiskCachePoolAccessManager.class );
+
+    /**
+     * Singleton, private
+     */
+    private JDBCDiskCachePoolAccessManager()
+    {
+        // empty
+    }
+
+    /**
+     * returns a singleton instance
+     * <p>
+     * @return JDBCDiskCachePoolAccessManager
+     */
+    public static synchronized JDBCDiskCachePoolAccessManager getInstance()
+    {
+        if ( instance == null )
+        {
+            instance = new JDBCDiskCachePoolAccessManager();
+        }
+        return instance;
+    }
+
+    /**
+     * Returns a pool for the name if one has been created. Otherwise it creates a pool.
+     * <p>
+     * @param poolName the name of the pool
+     * @param props the configuration properties for the pool
+     * @return JDBCDiskCachePoolAccess
+     * @throws SQLException if a database access error occurs
+     */
+    public synchronized JDBCDiskCachePoolAccess getJDBCDiskCachePoolAccess( String poolName, Properties props )
+        throws SQLException
+    {
+        JDBCDiskCachePoolAccess poolAccess = pools.get( poolName );
+
+        if ( poolAccess == null )
+        {
+            JDBCDiskCachePoolAccessAttributes poolAttributes = configurePoolAccessAttributes( poolName, props );
+            poolAccess = JDBCDiskCachePoolAccessManager.createPoolAccess( poolAttributes );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Created shared pooled access for pool name [" + poolName + "]." );
+            }
+            pools.put( poolName, poolAccess );
+        }
+
+        return poolAccess;
+    }
+
+
+
+    /**
+     * Configures the attributes using the properties.
+     * <p>
+     * @param poolName the name of the pool
+     * @param props the configuration properties for the pool
+     * @return JDBCDiskCachePoolAccessAttributes
+     */
+    protected JDBCDiskCachePoolAccessAttributes configurePoolAccessAttributes( String poolName, Properties props )
+    {
+        JDBCDiskCachePoolAccessAttributes poolAttributes = new JDBCDiskCachePoolAccessAttributes();
+
+        String poolAccessAttributePrefix = POOL_CONFIGURATION_PREFIX + poolName + ATTRIBUTE_PREFIX;
+        PropertySetter.setProperties( poolAttributes, props, poolAccessAttributePrefix + "." );
+
+        poolAttributes.setPoolName( poolName );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Configured attributes " + poolAttributes );
+        }
+        return poolAttributes;
+    }
+
+    /**
+     * Creates a pool access object and registers the driver.
+     * <p>
+     * @param driverClassName
+     * @param poolName
+     * @param fullURL = (url + database)
+     * @param userName
+     * @param password
+     * @param maxActive
+     * @return JDBCDiskCachePoolAccess
+     * @throws SQLException if a database access error occurs
+     */
+    public static JDBCDiskCachePoolAccess createPoolAccess( String driverClassName, String poolName, String fullURL,
+                                                            String userName, String password, int maxActive )
+       throws SQLException
+    {
+        JDBCDiskCachePoolAccess poolAccess = null;
+
+        if (driverClassName == null)
+        {
+            throw new SQLException("Driver class name is null");
+        }
+
+        try
+        {
+            // com.mysql.jdbc.Driver
+            Class.forName( driverClassName );
+        }
+        catch ( ClassNotFoundException e )
+        {
+            throw new SQLException("Couldn't find class for driver [" + driverClassName + "]", e );
+        }
+
+        poolAccess = new JDBCDiskCachePoolAccess( poolName );
+        poolAccess.setupDriver( fullURL, userName, password, maxActive );
+        poolAccess.logDriverStats();
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Created: " + poolAccess );
+        }
+
+        return poolAccess;
+    }
+
+    /**
+     * Creates a JDBCDiskCachePoolAccess object from the JDBCDiskCacheAttributes. Use this when not
+     * using the connection pool manager.
+     * <p>
+     * @param cattr
+     * @return JDBCDiskCachePoolAccess
+     * @throws SQLException if a database access error occurs
+     */
+    public static JDBCDiskCachePoolAccess createPoolAccess( JDBCDiskCacheAttributes cattr )
+        throws SQLException
+    {
+        return JDBCDiskCachePoolAccessManager.createPoolAccess( cattr.getDriverClassName(), cattr.getName(), cattr.getUrl() + cattr.getDatabase(),
+                                 cattr.getUserName(), cattr.getPassword(), cattr.getMaxActive() );
+    }
+
+    /**
+     * Creates a JDBCDiskCachePoolAccess object from the JDBCDiskCachePoolAccessAttributes. This is
+     * used by the connection pool manager.
+     * <p>
+     * @param poolAttributes
+     * @return JDBCDiskCachePoolAccess
+     * @throws SQLException if a database access error occurs
+     */
+    public static JDBCDiskCachePoolAccess createPoolAccess( JDBCDiskCachePoolAccessAttributes poolAttributes )
+        throws SQLException
+    {
+        return JDBCDiskCachePoolAccessManager.createPoolAccess( poolAttributes.getDriverClassName(), poolAttributes.getPoolName(), poolAttributes
+            .getUrl()
+            + poolAttributes.getDatabase(), poolAttributes.getUserName(), poolAttributes.getPassword(), poolAttributes
+            .getMaxActive() );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/ShrinkerThread.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/ShrinkerThread.java
new file mode 100644
index 0000000..adcdb0e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/ShrinkerThread.java
@@ -0,0 +1,169 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Calls delete expired on the disk caches. The shrinker is run by a clock daemon. The shrinker
+ * calls delete on each region. It pauses between calls.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ShrinkerThread
+    implements Runnable
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( ShrinkerThread.class );
+
+    /** A set of JDBCDiskCache objects to call deleteExpired on. */
+    private final Set<JDBCDiskCache<?, ?>> shrinkSet =
+        Collections.synchronizedSet( new HashSet<JDBCDiskCache<?, ?>>() );
+
+    /** Default time period to use. */
+    private static final long DEFAULT_PAUSE_BETWEEN_REGION_CALLS_MILLIS = 5000;
+
+    /**
+     * How long should we wait between calls to deleteExpired when we are iterating through the list
+     * of regions. Delete can lock the table. We want to give clients a chance to get some work
+     * done.
+     */
+    private long pauseBetweenRegionCallsMillis = DEFAULT_PAUSE_BETWEEN_REGION_CALLS_MILLIS;
+
+    /**
+     * Does nothing special.
+     */
+    protected ShrinkerThread()
+    {
+        super();
+    }
+
+    /**
+     * Adds a JDBC disk cache to the set of disk cache to shrink.
+     * <p>
+     * @param diskCache
+     */
+    public void addDiskCacheToShrinkList( JDBCDiskCache<?, ?> diskCache )
+    {
+        // the set will prevent dupes.
+        // we could also just add these to a hashmap by region name
+        // but that might cause a problem if you wanted to use two different
+        // jbdc disk caches for the same region.
+        shrinkSet.add( diskCache );
+    }
+
+    /**
+     * Calls deleteExpired on each item in the set. It pauses between each call.
+     */
+    @Override
+    public void run()
+    {
+        try
+        {
+            deleteExpiredFromAllRegisteredRegions();
+        }
+        catch ( Throwable e )
+        {
+            log.error( "Caught an expcetion while trying to delete expired items.", e );
+        }
+    }
+
+    /**
+     * Deletes the expired items from all the registered regions.
+     */
+    private void deleteExpiredFromAllRegisteredRegions()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Running JDBC disk cache shrinker.  Number of regions [" + shrinkSet.size() + "]" );
+        }
+
+        Object[] caches = null;
+
+        synchronized ( shrinkSet )
+        {
+            caches = this.shrinkSet.toArray();
+        }
+
+        if ( caches != null )
+        {
+            for ( int i = 0; i < caches.length; i++ )
+            {
+                JDBCDiskCache<?, ?> cache = (JDBCDiskCache<?, ?>) caches[i];
+
+                long start = System.currentTimeMillis();
+                int deleted = cache.deleteExpired();
+                long end = System.currentTimeMillis();
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Deleted [" + deleted + "] expired for region [" + cache.getCacheName() + "] for table ["
+                        + cache.getTableName() + "] in " + ( end - start ) + " ms." );
+                }
+
+                // don't pause after the last call to delete expired.
+                if ( i < caches.length - 1 )
+                {
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "Pausing for [" + this.getPauseBetweenRegionCallsMillis()
+                            + "] ms. before shrinking the next region." );
+                    }
+
+                    try
+                    {
+                        Thread.sleep( this.getPauseBetweenRegionCallsMillis() );
+                    }
+                    catch ( InterruptedException e )
+                    {
+                        log.warn( "Interrupted while waiting to delete expired for the next region." );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * How long should we wait between calls to deleteExpired when we are iterating through the list
+     * of regions.
+     * <p>
+     * @param pauseBetweenRegionCallsMillis The pauseBetweenRegionCallsMillis to set.
+     */
+    public void setPauseBetweenRegionCallsMillis( long pauseBetweenRegionCallsMillis )
+    {
+        this.pauseBetweenRegionCallsMillis = pauseBetweenRegionCallsMillis;
+    }
+
+    /**
+     * How long should we wait between calls to deleteExpired when we are iterating through the list
+     * of regions.
+     * <p>
+     * @return Returns the pauseBetweenRegionCallsMillis.
+     */
+    public long getPauseBetweenRegionCallsMillis()
+    {
+        return pauseBetweenRegionCallsMillis;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/TableState.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/TableState.java
new file mode 100644
index 0000000..a862660
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/TableState.java
@@ -0,0 +1,114 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * This is used by various elements of the JDBC disk cache to indicate the
+ * status of a table. The MySQL disk cache, for instance, marks the status as
+ * optimizing when a scheduled optimization is taking place. This allows the
+ * cache to balk rather than block during long running optimizations.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class TableState
+    implements Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -6625081552084964885L;
+
+    /** Name of the table whose state this reflects. */
+    private String tableName;
+
+    /**
+     * The table is free. It can be accessed and no potentially table locking
+     * jobs are running.
+     */
+    public static final int FREE = 0;
+
+    /** A potentially table locking deletion is running */
+    public static final int DELETE_RUNNING = 1;
+
+    /** A table locking optimization is running. */
+    public static final int OPTIMIZATION_RUNNING = 2;
+
+    /** we might want to add error */
+    private int state = FREE;
+
+    /**
+     * Construct a usable table state.
+     * <p>
+     * @param tableName
+     */
+    public TableState( String tableName )
+    {
+        this.setTableName( tableName );
+    }
+
+    /**
+     * @param tableName
+     *            The tableName to set.
+     */
+    public void setTableName( String tableName )
+    {
+        this.tableName = tableName;
+    }
+
+    /**
+     * @return Returns the tableName.
+     */
+    public String getTableName()
+    {
+        return tableName;
+    }
+
+    /**
+     * @param state
+     *            The state to set.
+     */
+    public void setState( int state )
+    {
+        this.state = state;
+    }
+
+    /**
+     * @return Returns the state.
+     */
+    public int getState()
+    {
+        return state;
+    }
+
+    /**
+     * Write out the values for debugging purposes.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder str = new StringBuilder();
+        str.append( "TableState " );
+        str.append( "\n TableName = " + getTableName() );
+        str.append( "\n State = " + getState() );
+        return str.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheFactory.java
new file mode 100644
index 0000000..ef9b607
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheFactory.java
@@ -0,0 +1,230 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.hsql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheManager;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This factory should create mysql disk caches.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class HSQLDiskCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( HSQLDiskCacheFactory.class );
+
+    /** Name for logging, etc. */
+    private String name = "HSQLDiskCacheFactory";
+
+    /** The databases. */
+    private final Set<String> databases = Collections.synchronizedSet( new HashSet<String>() );
+
+    /**
+     * This factory method should create an instance of the hsqlcache.
+     * <p>
+     * @param rawAttr
+     * @param cacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr,
+    									ICompositeCacheManager cacheManager,
+    									ICacheEventLogger cacheEventLogger,
+    									IElementSerializer elementSerializer )
+    {
+        JDBCDiskCacheManager mgr = JDBCDiskCacheManager.getInstance( (JDBCDiskCacheAttributes) rawAttr, cacheManager,
+                                                                     cacheEventLogger, elementSerializer );
+        try
+        {
+            setupDatabase( (JDBCDiskCacheAttributes) rawAttr );
+        }
+        catch ( Exception e )
+        {
+            // TODO we may not want to try and get the cache at this point.
+            log.error( "Problem setting up database.", e );
+        }
+        return mgr.getCache( (JDBCDiskCacheAttributes) rawAttr );
+    }
+
+    /**
+     * The name of the factory.
+     * <p>
+     * @param nameArg
+     */
+    @Override
+    public void setName( String nameArg )
+    {
+        name = nameArg;
+    }
+
+    /**
+     * Returns the display name
+     * <p>
+     * @return name
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * Creates the database if it doesn't exist, registers the driver class, etc.
+     * <p>
+     * @param attributes
+     * @throws Exception
+     */
+    protected void setupDatabase( JDBCDiskCacheAttributes attributes )
+        throws Exception
+    {
+        if ( attributes == null )
+        {
+            throw new Exception( "The attributes are null." );
+        }
+
+        // url should start with "jdbc:hsqldb:"
+        String database = attributes.getUrl() + attributes.getDatabase();
+
+        if ( databases.contains( database ) )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "We already setup database [" + database + "]" );
+            }
+            return;
+        }
+
+        // TODO get this from the attributes.
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        // "org.hsqldb.jdbcDriver"
+        String driver = attributes.getDriverClassName();
+        // "sa"
+        String user = attributes.getUserName();
+        // ""
+        String password = attributes.getPassword();
+
+        new org.hsqldb.jdbcDriver();
+        try
+        {
+            Class.forName( driver ).newInstance();
+
+            Connection cConn = DriverManager.getConnection( database, user, password );
+
+            setupTABLE( cConn, attributes.getTableName() );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Finished setting up database [" + database + "]" );
+            }
+
+            databases.add( database );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Fatal problem setting up the database.", e );
+        }
+    }
+
+    /**
+     * SETUP TABLE FOR CACHE
+     * <p>
+     * @param cConn
+     * @param tableName
+     */
+    private void setupTABLE( Connection cConn, String tableName ) throws SQLException
+    {
+        boolean newT = true;
+
+        // TODO make the cached nature of the table configurable
+        StringBuilder createSql = new StringBuilder();
+        createSql.append( "CREATE CACHED TABLE " + tableName );
+        createSql.append( "( " );
+        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
+        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
+        createSql.append( "ELEMENT               BINARY, " );
+        createSql.append( "CREATE_TIME           DATE, " );
+        createSql.append( "CREATE_TIME_SECONDS   BIGINT, " );
+        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
+        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
+        createSql.append( "IS_ETERNAL            CHAR(1), " );
+        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
+        createSql.append( ");" );
+
+        Statement sStatement = cConn.createStatement();
+
+        try
+        {
+            sStatement.executeQuery( createSql.toString() );
+            sStatement.close();
+        }
+        catch ( SQLException e )
+        {
+            // FIXME: This is not reliable
+            if ( e.toString().indexOf( "already exists" ) != -1 )
+            {
+                newT = false;
+            }
+            else
+            {
+                throw e;
+            }
+        }
+
+        // TODO create an index on SYSTEM_EXPIRE_TIME_SECONDS
+        String setupData[] = { "create index iKEY on " + tableName + " (CACHE_KEY, REGION)" };
+
+        if ( newT )
+        {
+            for ( int i = 1; i < setupData.length; i++ )
+            {
+                try
+                {
+                    sStatement.executeQuery( setupData[i] );
+                }
+                catch ( SQLException e )
+                {
+                    log.error( "Exception caught when creating index." + e );
+                }
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java
new file mode 100644
index 0000000..81e5c4e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCache.java
@@ -0,0 +1,168 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCache;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The MySQLDiskCache extends the core JDBCDiskCache.
+ * <p>
+ * Although the generic JDBC Disk Cache can be used for MySQL, the MySQL JDBC Disk Cache has
+ * additional features, such as table optimization that are particular to MySQL.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLDiskCache<K, V>
+	extends JDBCDiskCache<K, V>
+{
+    /** local logger */
+    private static final Log log = LogFactory.getLog( MySQLDiskCache.class );
+
+    /** config attributes */
+    private final MySQLDiskCacheAttributes mySQLDiskCacheAttributes;
+
+    /**
+     * Delegates to the super and makes use of the MySQL specific parameters used for scheduled
+     * optimization.
+     * <p>
+     * @param attributes
+     * @param tableState
+     * @param compositeCacheManager
+     * @throws SQLException if the pool access could not be set up
+     */
+    public MySQLDiskCache( MySQLDiskCacheAttributes attributes, TableState tableState, ICompositeCacheManager compositeCacheManager ) throws SQLException
+    {
+        super( attributes, tableState, compositeCacheManager );
+
+        mySQLDiskCacheAttributes = attributes;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "MySQLDiskCacheAttributes = " + attributes );
+        }
+    }
+
+    /**
+     * This delegates to the generic JDBC disk cache. If we are currently optimizing, then this
+     * method will balk and return null.
+     * <p>
+     * @param key Key to locate value for.
+     * @return An object matching key, or null.
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+    {
+        if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING )
+        {
+            if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() )
+            {
+                return null;
+            }
+        }
+        return super.processGet( key );
+    }
+
+    /**
+     * This delegates to the generic JDBC disk cache. If we are currently optimizing, then this
+     * method will balk and return null.
+     * <p>
+     * @param pattern used for like query.
+     * @return An object matching key, or null.
+     */
+    @Override
+    protected Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+    {
+        if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING )
+        {
+            if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() )
+            {
+                return null;
+            }
+        }
+        return super.processGetMatching( pattern );
+    }
+
+    /**
+     * @param pattern
+     * @return String to use in the like query.
+     */
+    @Override
+    public String constructLikeParameterFromPattern( String pattern )
+    {
+        String likePattern = pattern.replaceAll( "\\.\\+", "%" );
+        likePattern = likePattern.replaceAll( "\\.", "_" );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "pattern = [" + likePattern + "]" );
+        }
+
+        return likePattern;
+    }
+
+    /**
+     * This delegates to the generic JDBC disk cache. If we are currently optimizing, then this
+     * method will balk and do nothing.
+     * <p>
+     * @param element
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> element )
+    {
+        if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING )
+        {
+            if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() )
+            {
+                return;
+            }
+        }
+        super.processUpdate( element );
+    }
+
+    /**
+     * Removed the expired. (now - create time) > max life seconds * 1000
+     * <p>
+     * If we are currently optimizing, then this method will balk and do nothing.
+     * <p>
+     * TODO consider blocking and trying again.
+     * <p>
+     * @return the number deleted
+     */
+    @Override
+    protected int deleteExpired()
+    {
+        if ( this.getTableState().getState() == TableState.OPTIMIZATION_RUNNING )
+        {
+            if ( this.mySQLDiskCacheAttributes.isBalkDuringOptimization() )
+            {
+                return -1;
+            }
+        }
+        return super.deleteExpired();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java
new file mode 100644
index 0000000..fca9702
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheAttributes.java
@@ -0,0 +1,107 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
+
+/**
+ * This has additional attributes that are particular to the MySQL disk cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLDiskCacheAttributes
+    extends JDBCDiskCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -6535808344813320061L;
+
+    /**
+     * For now this is a simple comma delimited list of HH:MM:SS times to optimize
+     * the table. If none is supplied, then no optimizations will be performed.
+     * <p>
+     * In the future we can add a chron like scheduling system. This is to meet
+     * a pressing current need.
+     * <p>
+     * 03:01,15:00 will cause the optimizer to run at 3 am and at 3 pm.
+     */
+    private String optimizationSchedule = null;
+
+    /**
+     * If true, we will balk, that is return null during optimization rather than block.
+     */
+    public static final boolean DEFAULT_BALK_DURING_OPTIMIZATION = true;
+
+    /**
+     * If true, we will balk, that is return null during optimization rather than block.
+     * <p>
+     * <a href="http://en.wikipedia.org/wiki/Balking_pattern">Balking</a>
+     */
+    private boolean balkDuringOptimization = DEFAULT_BALK_DURING_OPTIMIZATION;
+
+    /**
+     * @param optimizationSchedule The optimizationSchedule to set.
+     */
+    public void setOptimizationSchedule( String optimizationSchedule )
+    {
+        this.optimizationSchedule = optimizationSchedule;
+    }
+
+    /**
+     * @return Returns the optimizationSchedule.
+     */
+    public String getOptimizationSchedule()
+    {
+        return optimizationSchedule;
+    }
+
+    /**
+     * @param balkDuringOptimization The balkDuringOptimization to set.
+     */
+    public void setBalkDuringOptimization( boolean balkDuringOptimization )
+    {
+        this.balkDuringOptimization = balkDuringOptimization;
+    }
+
+    /**
+     * Should we return null while optimizing the table.
+     * <p>
+     * @return Returns the balkDuringOptimization.
+     */
+    public boolean isBalkDuringOptimization()
+    {
+        return balkDuringOptimization;
+    }
+
+    /**
+     * For debugging.
+     * <p>
+     * @return debug string
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nMySQLDiskCacheAttributes" );
+        buf.append( "\n OptimizationSchedule [" + getOptimizationSchedule() + "]" );
+        buf.append( "\n BalkDuringOptimization [" + isBalkDuringOptimization() + "]" );
+        buf.append( super.toString() );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java
new file mode 100644
index 0000000..b023580
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheFactory.java
@@ -0,0 +1,78 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/**
+ * This factory should create mysql disk caches.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLDiskCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** name of the factory */
+    private String name = "MySQLDiskCacheFactory";
+
+    /**
+     * This factory method should create an instance of the mysqlcache.
+     * <p>
+     * @param rawAttr
+     * @param cacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr, ICompositeCacheManager cacheManager,
+                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        MySQLDiskCacheManager mgr = MySQLDiskCacheManager.getInstance( (MySQLDiskCacheAttributes) rawAttr, cacheManager, cacheEventLogger, elementSerializer );
+        return mgr.getCache( (MySQLDiskCacheAttributes) rawAttr );
+    }
+
+    /**
+     * The name of the factory.
+     * <p>
+     * @param nameArg
+     */
+    @Override
+    public void setName( String nameArg )
+    {
+        name = nameArg;
+    }
+
+    /**
+     * Returns the display name.
+     * <p>
+     * @return factory name
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java
new file mode 100644
index 0000000..6914a99
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManager.java
@@ -0,0 +1,295 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheManagerAbstractTemplate;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCachePoolAccess;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleFormatException;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util.ScheduleParser;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This manages instances of the MySQL jdbc disk cache. It maintains one for each region. One for
+ * all regions would work, but this gives us more detailed stats by region.
+ * <p>
+ * Although the generic JDBC Disk Cache Manager can be used for MySQL, the MySQL JDBC Disk Cache has
+ * additional features, such as table optimization that are particular to MySQL.
+ */
+public class MySQLDiskCacheManager
+    extends JDBCDiskCacheManagerAbstractTemplate
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( MySQLDiskCacheManager.class );
+
+    /** The singleton instance. */
+    private static MySQLDiskCacheManager instance;
+
+    /** User configurable attributes. */
+    private final MySQLDiskCacheAttributes defaultJDBCDiskCacheAttributes;
+
+    /** ms in a day */
+    private static final int DAILY_INTERVAL = 60 * 60 * 24 * 1000;
+
+    /** for schedule optimizations */
+    private Timer daemon = null;
+
+    /** The cache manager instance */
+    private ICompositeCacheManager compositeCacheManager;
+
+    /**
+     * Constructor for the HSQLCacheManager object
+     * <p>
+     * @param cattr
+     * @param compositeCacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private MySQLDiskCacheManager( MySQLDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager,
+                                   ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Creating MySQLDiskCacheManager with " + cattr );
+        }
+        defaultJDBCDiskCacheAttributes = cattr;
+        setElementSerializer( elementSerializer );
+        setCacheEventLogger( cacheEventLogger );
+        setCompositeCacheManager( compositeCacheManager );
+    }
+
+    /**
+     * Gets the defaultCattr attribute of the HSQLCacheManager object
+     * <p>
+     * @return The defaultCattr value
+     */
+    public MySQLDiskCacheAttributes getDefaultJDBCDiskCacheAttributes()
+    {
+        return defaultJDBCDiskCacheAttributes;
+    }
+
+    /**
+     * Gets the instance attribute of the HSQLCacheManager class
+     * <p>
+     * @param cattr
+     * @param compositeCacheManager
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public static MySQLDiskCacheManager getInstance( MySQLDiskCacheAttributes cattr, ICompositeCacheManager compositeCacheManager,
+                                                     ICacheEventLogger cacheEventLogger,
+                                                     IElementSerializer elementSerializer )
+    {
+        synchronized ( MySQLDiskCacheManager.class )
+        {
+            if ( instance == null )
+            {
+                instance = new MySQLDiskCacheManager( cattr, compositeCacheManager, cacheEventLogger, elementSerializer );
+            }
+        }
+        clients++;
+        return instance;
+    }
+
+    /**
+     * Gets the cache attribute of the HSQLCacheManager object
+     * <p>
+     * @param cacheName
+     * @return The cache value
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <K, V> MySQLDiskCache<K, V> getCache( String cacheName )
+    {
+        MySQLDiskCacheAttributes cattr = (MySQLDiskCacheAttributes) defaultJDBCDiskCacheAttributes.copy();
+        cattr.setCacheName( cacheName );
+        return (MySQLDiskCache<K, V>) getCache( cattr );
+    }
+
+    /**
+     * Creates a JDBCDiskCache using the supplied attributes.
+     * <p>
+     * @param cattr
+     * @param tableState
+     * @return AuxiliaryCache
+     * @throws SQLException if database operations fail
+     */
+    @Override
+    protected <K, V> MySQLDiskCache<K, V> createJDBCDiskCache( JDBCDiskCacheAttributes cattr, 
+            TableState tableState ) throws SQLException
+    {
+        MySQLDiskCache<K, V> diskCache = new MySQLDiskCache<K, V>( (MySQLDiskCacheAttributes) cattr, tableState, getCompositeCacheManager() );
+
+        scheduleOptimizations( (MySQLDiskCacheAttributes) cattr, tableState, diskCache.getPoolAccess() );
+
+        return diskCache;
+    }
+
+    /**
+     * @param compositeCacheManager the compositeCacheManager to set
+     */
+    protected void setCompositeCacheManager( ICompositeCacheManager compositeCacheManager )
+    {
+        this.compositeCacheManager = compositeCacheManager;
+    }
+
+    /**
+     * @return the compositeCacheManager
+     */
+    protected ICompositeCacheManager getCompositeCacheManager()
+    {
+        return compositeCacheManager;
+    }
+    /**
+     * For each time in the optimization schedule, this calls schedule Optimizaiton.
+     * <p>
+     * @param attributes configuration propeties.
+     * @param tableState for noting optimization in progress, etc.
+     * @param poolAccess access to the pool
+     */
+    protected void scheduleOptimizations( MySQLDiskCacheAttributes attributes, TableState tableState, JDBCDiskCachePoolAccess poolAccess  )
+    {
+        if ( attributes != null )
+        {
+            if ( attributes.getOptimizationSchedule() != null )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Will try to configure optimization for table [" + attributes.getTableName()
+                        + "] on schdule [" + attributes.getOptimizationSchedule() + "]" );
+                }
+
+                MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, poolAccess );
+
+                // loop through the dates.
+                try
+                {
+                    Date[] dates = ScheduleParser.createDatesForSchedule( attributes.getOptimizationSchedule() );
+                    if ( dates != null )
+                    {
+                        for ( int i = 0; i < dates.length; i++ )
+                        {
+                            this.scheduleOptimization( dates[i], optimizer );
+                        }
+                    }
+                }
+                catch ( ScheduleFormatException e )
+                {
+                    log.warn( "Problem creating optimization schedule for table [" + attributes.getTableName() + "]" );
+                }
+            }
+            else
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Optimization is not configured for table [" + attributes.getTableName() + "]" );
+                }
+            }
+        }
+    }
+
+    /**
+     * This takes in a single time and schedules the optimizer to be called at that time every day.
+     * <p>
+     * @param startTime -- HH:MM:SS format
+     * @param optimizer
+     */
+    protected void scheduleOptimization( Date startTime, MySQLTableOptimizer optimizer )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "startTime [" + startTime + "] for optimizer " + optimizer );
+        }
+
+        // create clock daemon if necessary
+        if ( daemon == null )
+        {
+            // true for daemon status
+            daemon = new Timer( true );
+        }
+
+        // get the runnable from the factory
+        TimerTask runnable = new OptimizerTask( optimizer );
+
+        // have the daemon execute our runnable
+        // false to not execute immediately.
+        daemon.scheduleAtFixedRate( runnable, startTime, DAILY_INTERVAL );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Scheduled optimization to begin at [" + startTime + "]" );
+        }
+    }
+
+    /**
+     * This calls the optimizers' optimize table method. This is used by the timer.
+     * <p>
+     * @author Aaron Smuts
+     */
+    private static class OptimizerTask
+        extends TimerTask
+    {
+        /** Handles optimization */
+        private MySQLTableOptimizer optimizer = null;
+
+        /**
+         * Get a handle on the optimizer.
+         * <p>
+         * @param optimizer
+         */
+        public OptimizerTask( MySQLTableOptimizer optimizer )
+        {
+            this.optimizer = optimizer;
+        }
+
+        /**
+         * This calls optimize on the optimizer.
+         * <p>
+         * @see java.lang.Runnable#run()
+         */
+        @Override
+        public void run()
+        {
+            if ( optimizer != null )
+            {
+                boolean success = optimizer.optimizeTable();
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Optimization success status [" + success + "]" );
+                }
+            }
+            else
+            {
+                log.warn( "OptimizerRunner: The optimizer is null.  Could not optimize table." );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java
new file mode 100644
index 0000000..02260c8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizer.java
@@ -0,0 +1,325 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCachePoolAccess;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * The MySQL Table Optimizer can optimize MySQL tables. It knows how to optimize for MySQL datbases
+ * in particular and how to repair the table if it is corrupted in the process.
+ * <p>
+ * We will probably be able to abstract out a generic optimizer interface from this class in the
+ * future.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLTableOptimizer
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( MySQLTableOptimizer.class );
+
+    /** The pool */
+    private JDBCDiskCachePoolAccess poolAccess = null;
+
+    /** The name of the table. */
+    private String tableName = null;
+
+    /** optimizing, etc. */
+    private TableState tableState;
+
+    /**
+     * This constructs an optimizer with the disk cacn properties.
+     * <p>
+     * @param attributes
+     * @param tableState We mark the table status as optimizing when this is happening.
+     * @param poolAccess access to the database
+     */
+    public MySQLTableOptimizer( MySQLDiskCacheAttributes attributes, TableState tableState,
+                                JDBCDiskCachePoolAccess poolAccess )
+    {
+        setTableName( attributes.getTableName() );
+
+        this.tableState = tableState;
+
+        this.poolAccess = poolAccess;
+    }
+
+    /**
+     * A scheduler will call this method. When it is called the table state is marked as optimizing.
+     * TODO we need to verify that no deletions are running before we call optimize. We should wait
+     * if a deletion is in progress.
+     * <p>
+     * This restores when there is an optimization error. The error output looks like this:
+     *
+     * <pre>
+     *           mysql> optimize table JCS_STORE_FLIGHT_OPTION_ITINERARY;
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               | Table                                       | Op       | Msg_type | Msg_text            |
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | optimize | error    | 2 when fixing table |
+     *               | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | optimize | status   | Operation failed    |
+     *               +---------------------------------------------+----------+----------+---------------------+
+     *               2 rows in set (51.78 sec)
+     * </pre>
+     *
+     * A successful repair response looks like this:
+     *
+     * <pre>
+     *        mysql> REPAIR TABLE JCS_STORE_FLIGHT_OPTION_ITINERARY;
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            | Table                                       | Op     | Msg_type | Msg_text                                     |
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | error    | 2 when fixing table                          |
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | warning  | Number of rows changed from 131276 to 260461 |
+     *            | jcs_cache.JCS_STORE_FLIGHT_OPTION_ITINERARY | repair | status   | OK                                           |
+     *            +---------------------------------------------+--------+----------+----------------------------------------------+
+     *            3 rows in set (3 min 5.94 sec)
+     * </pre>
+     *
+     * A successful optimization looks like this:
+     *
+     * <pre>
+     *       mysql> optimize table JCS_STORE_DEFAULT;
+     *           +-----------------------------+----------+----------+----------+
+     *           | Table                       | Op       | Msg_type | Msg_text |
+     *           +-----------------------------+----------+----------+----------+
+     *           | jcs_cache.JCS_STORE_DEFAULT | optimize | status   | OK       |
+     *           +-----------------------------+----------+----------+----------+
+     *           1 row in set (1.10 sec)
+     * </pre>
+     * @return true if it worked
+     */
+    public boolean optimizeTable()
+    {
+        long start = System.currentTimeMillis();
+        boolean success = false;
+
+        if ( tableState.getState() == TableState.OPTIMIZATION_RUNNING )
+        {
+            log
+                .warn( "Skipping optimization.  Optimize was called, but the table state indicates that an optimization is currently running." );
+            return false;
+        }
+
+        try
+        {
+            tableState.setState( TableState.OPTIMIZATION_RUNNING );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Optimizing table [" + this.getTableName() + "]" );
+            }
+
+            Connection con;
+            try
+            {
+                con = poolAccess.getConnection();
+            }
+            catch ( SQLException e )
+            {
+                log.error( "Problem getting connection.", e );
+                return false;
+            }
+
+            try
+            {
+                // TEST
+                Statement sStatement = null;
+                try
+                {
+                    sStatement = con.createStatement();
+
+                    ResultSet rs = sStatement.executeQuery( "optimize table " + this.getTableName() );
+
+                    // first row is error, then status
+                    // if there is only one row in the result set, everything
+                    // should be fine.
+                    // This may be mysql version specific.
+                    if ( rs.next() )
+                    {
+                        String status = rs.getString( "Msg_type" );
+                        String message = rs.getString( "Msg_text" );
+
+                        if ( log.isInfoEnabled() )
+                        {
+                            log.info( "Message Type: " + status );
+                            log.info( "Message: " + message );
+                        }
+
+                        if ( "error".equals( status ) )
+                        {
+                            log.warn( "Optimization was in erorr.  Will attempt to repair the table.  Message: "
+                                + message );
+
+                            // try to repair the table.
+                            success = repairTable( sStatement );
+                        }
+                        else
+                        {
+                            success = true;
+                        }
+                    }
+
+                    // log the table status
+                    String statusString = getTableStatus( sStatement );
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "Table status after optimizing table [" + this.getTableName() + "]\n" + statusString );
+                    }
+                }
+                catch ( SQLException e )
+                {
+                    log.error( "Problem optimizing table [" + this.getTableName() + "]", e );
+                    return false;
+                }
+                finally
+                {
+                    if (sStatement != null)
+                    {
+                        try
+                        {
+                            sStatement.close();
+                        }
+                        catch ( SQLException e )
+                        {
+                            log.error( "Problem closing statement.", e );
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                try
+                {
+                    con.close();
+                }
+                catch ( SQLException e )
+                {
+                    log.error( "Problem closing connection.", e );
+                }
+            }
+        }
+        finally
+        {
+            tableState.setState( TableState.FREE );
+
+            long end = System.currentTimeMillis();
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Optimization of table [" + this.getTableName() + "] took " + ( end - start ) + " ms." );
+            }
+        }
+
+        return success;
+    }
+
+    /**
+     * This calls show table status and returns the result as a String.
+     * <p>
+     * @param sStatement
+     * @return String
+     * @throws SQLException
+     */
+    protected String getTableStatus( Statement sStatement )
+        throws SQLException
+    {
+        ResultSet statusResultSet = sStatement.executeQuery( "show table status" );
+        StringBuilder statusString = new StringBuilder();
+        int numColumns = statusResultSet.getMetaData().getColumnCount();
+        while ( statusResultSet.next() )
+        {
+            statusString.append( "\n" );
+            for ( int i = 1; i <= numColumns; i++ )
+            {
+                statusString.append( statusResultSet.getMetaData().getColumnLabel( i ) + " ["
+                    + statusResultSet.getString( i ) + "]  |  " );
+            }
+        }
+        return statusString.toString();
+    }
+
+    /**
+     * This is called if the optimizatio is in error.
+     * <p>
+     * It looks for "OK" in response. If it find "OK" as a message in any result set row, it returns
+     * true. Otherwise we assume that the repair failed.
+     * <p>
+     * @param sStatement
+     * @return true if successful
+     * @throws SQLException
+     */
+    protected boolean repairTable( Statement sStatement )
+        throws SQLException
+    {
+        boolean success = false;
+
+        // if( message != null && message.indexOf( ) )
+        ResultSet repairResult = sStatement.executeQuery( "repair table " + this.getTableName() );
+        StringBuilder repairString = new StringBuilder();
+        int numColumns = repairResult.getMetaData().getColumnCount();
+        while ( repairResult.next() )
+        {
+            for ( int i = 1; i <= numColumns; i++ )
+            {
+                repairString.append( repairResult.getMetaData().getColumnLabel( i ) + " [" + repairResult.getString( i )
+                    + "]  |  " );
+            }
+
+            String message = repairResult.getString( "Msg_text" );
+            if ( "OK".equals( message ) )
+            {
+                success = true;
+            }
+        }
+        if ( log.isInfoEnabled() )
+        {
+            log.info( repairString );
+        }
+
+        if ( !success )
+        {
+            log.warn( "Failed to repair the table. " + repairString );
+        }
+        return success;
+    }
+
+    /**
+     * @param tableName The tableName to set.
+     */
+    public void setTableName( String tableName )
+    {
+        this.tableName = tableName;
+    }
+
+    /**
+     * @return Returns the tableName.
+     */
+    public String getTableName()
+    {
+        return tableName;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java
new file mode 100644
index 0000000..311d16b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleFormatException.java
@@ -0,0 +1,40 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util;
+
+/*
+ * 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 is thrown internally by the schedule parser.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ScheduleFormatException
+    extends Exception
+{
+    /** don't change */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @param message
+     */
+    public ScheduleFormatException( String message )
+    {
+        super( message );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java
new file mode 100644
index 0000000..c371385
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParser.java
@@ -0,0 +1,111 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util;
+
+/*
+ * 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.
+ */
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.StringTokenizer;
+
+/**
+ * Parses the very simple schedule format.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ScheduleParser
+{
+    /**
+     * For each date time that is separated by a comma in the
+     * OptimizationSchedule, create a date and add it to an array of dates.
+     * <p>
+     * @param schedule
+     * @return Date[]
+     * @throws ScheduleFormatException
+     */
+    public static Date[] createDatesForSchedule( String schedule )
+        throws ScheduleFormatException
+    {
+        if ( schedule == null )
+        {
+            throw new ScheduleFormatException( "Cannot create schedules for a null String." );
+        }
+
+        StringTokenizer toker = new StringTokenizer( schedule, "," );
+        Date[] dates = new Date[toker.countTokens()];
+        int cnt = 0;
+        while ( toker.hasMoreTokens() )
+        {
+            String time = toker.nextToken();
+            dates[cnt] = getDateForSchedule( time );
+            cnt++;
+        }
+        return dates;
+    }
+
+    /**
+     * For a single string it creates a date that is the next time this hh:mm:ss
+     * combo will be seen.
+     * <p>
+     * @param startTime
+     * @return Date
+     * @throws ScheduleFormatException
+     */
+    public static Date getDateForSchedule( String startTime )
+        throws ScheduleFormatException
+    {
+        if ( startTime == null )
+        {
+            throw new ScheduleFormatException( "Cannot create date for a null String." );
+        }
+
+        int firstColon = startTime.indexOf( ":" );
+        int lastColon = startTime.lastIndexOf( ":" );
+        int len = startTime.length();
+        if ( firstColon == -1 || lastColon == -1 || firstColon == lastColon || lastColon == len )
+        {
+            String message = "StartTime [" + startTime + "] is deformed.  Unable to schedule optimizaiton.";
+            throw new ScheduleFormatException( message );
+        }
+
+        Calendar cal = Calendar.getInstance();
+        try
+        {
+            int hour = Integer.parseInt( startTime.substring( 0, firstColon ) );
+            cal.set( Calendar.HOUR_OF_DAY, hour );
+            int minute = Integer.parseInt( startTime.substring( firstColon + 1, lastColon ) );
+            cal.set( Calendar.MINUTE, minute );
+            int second = Integer.parseInt( startTime.substring( lastColon + 1, len ) );
+            cal.set( Calendar.SECOND, second );
+        }
+        catch ( NumberFormatException e )
+        {
+            String message = "Problem parsing start time [" + startTime + "].  It should be in HH:MM:SS format.";
+            throw new ScheduleFormatException( message );
+        }
+
+        // if the date is less than now, add a day.
+        Date now = new Date();
+        if ( cal.getTime().before( now ) )
+        {
+            cal.add( Calendar.DAY_OF_MONTH, 1 );
+        }
+
+        return cal.getTime();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/package.html
new file mode 100644
index 0000000..668a141
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/package.html
@@ -0,0 +1,27 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    The primary disk auxiliary. Objects are serialized to a file on disk.
+    This implementation uses memory keys and performs quite well.
+    Recomended for most cases.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCache.java
new file mode 100644
index 0000000..e922680
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCache.java
@@ -0,0 +1,457 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheEventLogging;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.IZombie;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Lateral distributor. Returns null on get by default. Net search not implemented.
+ */
+public class LateralCache<K, V>
+    extends AbstractAuxiliaryCacheEventLogging<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( LateralCache.class );
+
+    /** generalize this, use another interface */
+    private final ILateralCacheAttributes lateralCacheAttributes;
+
+    /** The region name */
+    final String cacheName;
+
+    /** either http, socket.udp, or socket.tcp can set in config */
+    private ICacheServiceNonLocal<K, V> lateralCacheService;
+
+    /** Monitors the connection. */
+    private LateralCacheMonitor monitor;
+
+    /**
+     * Constructor for the LateralCache object
+     * <p>
+     * @param cattr
+     * @param lateral
+     * @param monitor
+     */
+    public LateralCache( ILateralCacheAttributes cattr, ICacheServiceNonLocal<K, V> lateral, LateralCacheMonitor monitor )
+    {
+        this.cacheName = cattr.getCacheName();
+        this.lateralCacheAttributes = cattr;
+        this.lateralCacheService = lateral;
+        this.monitor = monitor;
+    }
+
+    /**
+     * Constructor for the LateralCache object
+     * <p>
+     * @param cattr
+     */
+    public LateralCache( ILateralCacheAttributes cattr )
+    {
+        this.cacheName = cattr.getCacheName();
+        this.lateralCacheAttributes = cattr;
+    }
+
+    /**
+     * Update lateral.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        try
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "update: lateral = [" + lateralCacheService + "], " + "CacheInfo.listenerId = "
+                    + CacheInfo.listenerId );
+            }
+            lateralCacheService.update( ce, CacheInfo.listenerId );
+        }
+        catch ( NullPointerException npe )
+        {
+            log.error( "Failure updating lateral. lateral = " + lateralCacheService, npe );
+            handleException( npe, "Failed to put [" + ce.getKey() + "] to " + ce.getCacheName() + "@" + lateralCacheAttributes );
+            return;
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to put [" + ce.getKey() + "] to " + ce.getCacheName() + "@" + lateralCacheAttributes );
+        }
+    }
+
+    /**
+     * The performance costs are too great. It is not recommended that you enable lateral gets.
+     * <p>
+     * @param key
+     * @return ICacheElement<K, V> or null
+     * @throws IOException
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+        throws IOException
+    {
+        ICacheElement<K, V> obj = null;
+
+        if ( this.lateralCacheAttributes.getPutOnlyMode() )
+        {
+            return null;
+        }
+        try
+        {
+            obj = lateralCacheService.get( cacheName, key );
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+            handleException( e, "Failed to get [" + key + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
+        }
+        return obj;
+    }
+
+    /**
+     * @param pattern
+     * @return A map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    protected Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+        throws IOException
+    {
+        if ( this.lateralCacheAttributes.getPutOnlyMode() )
+        {
+            return Collections.emptyMap();
+        }
+        try
+        {
+            return lateralCacheService.getMatching( cacheName, pattern );
+        }
+        catch ( IOException e )
+        {
+            log.error( e );
+            handleException( e, "Failed to getMatching [" + pattern + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
+            return Collections.emptyMap();
+        }
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    protected Map<K, ICacheElement<K, V>> processGetMultiple( Set<K> keys )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        try
+        {
+            return lateralCacheService.getKeySet( cacheName );
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to get key set from " + lateralCacheAttributes.getCacheName() + "@"
+                + lateralCacheAttributes );
+        }
+        return Collections.emptySet();
+    }
+
+    /**
+     * Synchronously remove from the remote cache; if failed, replace the remote handle with a
+     * zombie.
+     * <p>
+     * @param key
+     * @return false always
+     * @throws IOException
+     */
+    @Override
+    protected boolean processRemove( K key )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing key:" + key );
+        }
+
+        try
+        {
+            lateralCacheService.remove( cacheName, key, CacheInfo.listenerId );
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to remove " + key + " from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
+        }
+        return false;
+    }
+
+    /**
+     * Synchronously removeAll from the remote cache; if failed, replace the remote handle with a
+     * zombie.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    protected void processRemoveAll()
+        throws IOException
+    {
+        try
+        {
+            lateralCacheService.removeAll( cacheName, CacheInfo.listenerId );
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to remove all from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
+        }
+    }
+
+    /**
+     * Synchronously dispose the cache. Not sure we want this.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    protected void processDispose()
+        throws IOException
+    {
+        log.debug( "Disposing of lateral cache" );
+
+        ///* HELP: This section did nothing but generate compilation warnings.
+        // TODO: may limit this functionality. It is dangerous.
+        // asmuts -- Added functionality to help with warnings. I'm not getting
+        // any.
+        try
+        {
+            lateralCacheService.dispose( this.lateralCacheAttributes.getCacheName() );
+            // Should remove connection
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Couldn't dispose", ex );
+            handleException( ex, "Failed to dispose " + lateralCacheAttributes.getCacheName() );
+        }
+    }
+
+    /**
+     * Returns the cache status.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return this.lateralCacheService instanceof IZombie ? CacheStatus.ERROR : CacheStatus.ALIVE;
+    }
+
+    /**
+     * Returns the current cache size.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return 0;
+    }
+
+    /**
+     * Gets the cacheType attribute of the LateralCache object
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.LATERAL_CACHE;
+    }
+
+    /**
+     * Gets the cacheName attribute of the LateralCache object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+
+    /**
+     * Not yet sure what to do here.
+     * <p>
+     * @param ex
+     * @param msg
+     * @throws IOException
+     */
+    private void handleException( Exception ex, String msg )
+        throws IOException
+    {
+        log.error( "Disabling lateral cache due to error " + msg, ex );
+
+        lateralCacheService = new ZombieCacheServiceNonLocal<K, V>( lateralCacheAttributes.getZombieQueueMaxSize() );
+        // may want to flush if region specifies
+        // Notify the cache monitor about the error, and kick off the recovery
+        // process.
+        monitor.notifyError();
+
+        // could stop the net search if it is built and try to reconnect?
+        if ( ex instanceof IOException )
+        {
+            throw (IOException) ex;
+        }
+        throw new IOException( ex.getMessage() );
+    }
+
+    /**
+     * Replaces the current remote cache service handle with the given handle.
+     * <p>
+     * @param restoredLateral
+     */
+    public void fixCache( ICacheServiceNonLocal<K, V> restoredLateral )
+    {
+        if ( this.lateralCacheService != null && this.lateralCacheService instanceof ZombieCacheServiceNonLocal )
+        {
+            ZombieCacheServiceNonLocal<K, V> zombie = (ZombieCacheServiceNonLocal<K, V>) this.lateralCacheService;
+            this.lateralCacheService = restoredLateral;
+            try
+            {
+                zombie.propagateEvents( restoredLateral );
+            }
+            catch ( Exception e )
+            {
+                try
+                {
+                    handleException( e, "Problem propagating events from Zombie Queue to new Lateral Service." );
+                }
+                catch ( IOException e1 )
+                {
+                    // swallow, since this is just expected kick back.  Handle always throws
+                }
+            }
+        }
+        else
+        {
+            this.lateralCacheService = restoredLateral;
+        }
+    }
+
+    /**
+     * getStats
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return "";
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return lateralCacheAttributes;
+    }
+
+    /**
+     * @return debugging data.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n LateralCache " );
+        buf.append( "\n Cache Name [" + lateralCacheAttributes.getCacheName() + "]" );
+        buf.append( "\n cattr =  [" + lateralCacheAttributes + "]" );
+        return buf.toString();
+    }
+
+    /**
+     * @return extra data.
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return null;
+    }
+
+    /**
+     * The NoWait on top does not call out to here yet.
+     * <p>
+     * @return almost nothing
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "LateralCache" );
+        return stats;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractFactory.java
new file mode 100644
index 0000000..83732bc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractFactory.java
@@ -0,0 +1,101 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+
+/**
+ * Particular lateral caches should define their own factory.  It is
+ * not necessary to extend this base factory, but it can be useful.
+ * <p>
+ * The old factory tried to handle all types of laterals.  It was
+ * gettting cluttered by ad hoc if statements.  Since the javagroups
+ * lateral was jdk1.4 dependent it had to be moved.  As such, the
+ * old factory could no longer import it.  This motivated the change.
+ * <p>
+ * This abstraction layer should keep things cleaner.
+ * <p>
+ * @author Aaron Smuts
+ */
+public abstract class LateralCacheAbstractFactory
+	implements AuxiliaryCacheFactory
+{
+    /** The auxiliary name */
+    private String name;
+
+    /**
+     * Creates a lateral cache.
+     * <p>
+     * @param attr
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public abstract <K, V> AuxiliaryCache<K, V> createCache(
+            AuxiliaryCacheAttributes attr, ICompositeCacheManager cacheMgr,
+            ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer );
+
+    /**
+     * Makes sure a listener gets created. It will get monitored as soon as it
+     * is used.
+     * <p>
+     * This should be called by create cache.
+     * <p>
+     * @param lac  ILateralCacheAttributes
+     * @param cacheMgr
+     *
+     * @return the listener if created, else null
+     */
+    public abstract <K, V>
+        ILateralCacheListener<K, V> createListener( ILateralCacheAttributes lac, ICompositeCacheManager cacheMgr );
+
+    /**
+     * Gets the name attribute of the LateralCacheFactory object
+     * <p>
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * Sets the name attribute of the LateralCacheFactory object
+     * <p>
+     * @param name
+     *            The new name value
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractManager.java
new file mode 100644
index 0000000..88f8043
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAbstractManager.java
@@ -0,0 +1,148 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheManager;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheObserver;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creates lateral caches. Lateral caches are primarily used for removing non
+ * laterally configured caches. Non laterally configured cache regions should
+ * still be able to participate in removal. But if there is a non laterally
+ * configured cache hub, then lateral removals may be necessary. For flat
+ * webserver production environments, without a strong machine at the app server
+ * level, distribution and search may need to occur at the lateral cache level.
+ * This is currently not implemented in the lateral cache.
+ * <p>
+ *
+ * TODO: - need freeCache, release, getStats - need to find an interface
+ *        Acceptable for all - cache managers or a manager within a type
+ */
+public abstract class LateralCacheAbstractManager
+    implements ILateralCacheManager, IShutdownObserver
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( LateralCacheAbstractManager.class );
+
+    /** Each manager instance has caches.   */
+    protected final Map<String, LateralCacheNoWait<?, ?>> caches =
+        new HashMap<String, LateralCacheNoWait<?, ?>>();
+
+    /** Configuration */
+    protected ILateralCacheAttributes lca;
+
+    /**
+     * Wrapper of the lateral cache watch service; or wrapper of a zombie
+     * service if failed to connect.
+     */
+    private LateralCacheWatchRepairable lateralWatch;
+
+    /**
+     * Adds the lateral cache listener to the underlying cache-watch service.
+     *
+     * @param cacheName
+     *            The feature to be added to the LateralCacheListener attribute
+     * @param listener
+     *            The feature to be added to the LateralCacheListener attribute
+     * @throws IOException
+     */
+    public <K, V> void addLateralCacheListener( String cacheName, ILateralCacheListener<K, V> listener )
+        throws IOException
+    {
+        synchronized ( this.caches )
+        {
+            this.lateralWatch.addCacheListener( cacheName, listener );
+        }
+    }
+
+    /**
+     * Called to access a pre-created region or construct one with defaults.
+     * Since all aux cache access goes through the manager, this will never be
+     * called.
+     * <p>
+     * After getting the manager instance for a server, the factory gets a cache
+     * for the region name it is constructing.
+     * <p>
+     * There should be one manager per server and one cache per region per
+     * manager.
+     *
+     * @return AuxiliaryCache
+     * @param cacheName
+     */
+    @Override
+    public abstract <K, V> AuxiliaryCache<K, V> getCache( String cacheName );
+
+    /**
+     * Gets the stats attribute of the LateralCacheManager object
+     *
+     * @return String
+     */
+    public String getStats()
+    {
+        // add something here
+        return "";
+    }
+
+    /**
+     * Fixes up all the caches managed by this cache manager.
+     *
+     * @param lateralService
+     * @param lateralWatch
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Need downcast to satisfy common interface
+    public void fixCaches( ICacheServiceNonLocal<Serializable, Serializable> lateralService, ILateralCacheObserver lateralWatch )
+    {
+        log.debug( "Fixing lateral caches:" );
+
+        synchronized ( this.caches )
+        {
+            // need to implement an observer for some types of laterals( http and
+            // tcp)
+            //this.lateralWatch.setCacheWatch(lateralWatch);
+            for (LateralCacheNoWait<?, ?> cache : this.caches.values())
+            {
+                ((LateralCacheNoWait<Serializable, Serializable>)cache).fixCache( lateralService );
+            }
+        }
+    }
+
+    /**
+     * @return Map
+     *
+     */
+    @Override
+    public Map<String, LateralCacheNoWait<?, ?>> getCaches()
+    {
+        return caches;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAttributes.java
new file mode 100644
index 0000000..a02ef37
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheAttributes.java
@@ -0,0 +1,313 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+
+import java.io.Serializable;
+
+/**
+ * This class stores attributes for all of the available lateral cache auxiliaries.
+ */
+public class LateralCacheAttributes
+    extends AbstractAuxiliaryCacheAttributes
+    implements Serializable, ILateralCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -3408449508837393660L;
+
+    /** Default receive setting */
+    private static final boolean DEFAULT_RECEIVE = true;
+
+    /** THe type of lateral */
+    private String transmissionTypeName = "UDP";
+
+    /** indicates the lateral type, this needs to change */
+    private Type transmissionType = Type.UDP;
+
+    /** The http servers */
+    private String httpServers;
+
+    /** used to identify the service that this manager will be operating on */
+    private String httpServer = "";
+
+    /** this needs to change */
+    private String udpMulticastAddr = "228.5.6.7";
+
+    /** this needs to change */
+    private int udpMulticastPort = 6789;
+
+    /** this needs to change */
+    private int httpListenerPort = 8080;
+
+    /** disables gets from laterals */
+    private boolean putOnlyMode = true;
+
+    /**
+     * do we receive and broadcast or only broadcast this is useful when you don't want to get any
+     * notifications
+     */
+    private boolean receive = DEFAULT_RECEIVE;
+
+    /** If the primary fails, we will queue items before reconnect.  This limits the number of items that can be queued. */
+    private int zombieQueueMaxSize = DEFAULT_ZOMBIE_QUEUE_MAX_SIZE;
+
+    /**
+     * Sets the httpServer attribute of the LateralCacheAttributes object
+     * <P>
+     * @param val The new httpServer value
+     */
+    @Override
+    public void setHttpServer( String val )
+    {
+        httpServer = val;
+    }
+
+    /**
+     * Gets the httpServer attribute of the LateralCacheAttributes object
+     * @return The httpServer value
+     */
+    @Override
+    public String getHttpServer()
+    {
+        return httpServer;
+    }
+
+    /**
+     * Sets the httpServers attribute of the LateralCacheAttributes object
+     * @param val The new httpServers value
+     */
+    @Override
+    public void setHttpServers( String val )
+    {
+        httpServers = val;
+    }
+
+    /**
+     * Gets the httpSrvers attribute of the LateralCacheAttributes object
+     * @return The httpServers value
+     */
+    @Override
+    public String getHttpServers()
+    {
+        return httpServers;
+    }
+
+    /**
+     * Sets the httpListenerPort attribute of the ILateralCacheAttributes object
+     * @param val The new tcpListenerPort value
+     */
+    @Override
+    public void setHttpListenerPort( int val )
+    {
+        this.httpListenerPort = val;
+    }
+
+    /**
+     * Gets the httpListenerPort attribute of the ILateralCacheAttributes object
+     * @return The httpListenerPort value
+     */
+    @Override
+    public int getHttpListenerPort()
+    {
+        return this.httpListenerPort;
+    }
+
+    /**
+     * Sets the udpMulticastAddr attribute of the LateralCacheAttributes object
+     * @param val The new udpMulticastAddr value
+     */
+    @Override
+    public void setUdpMulticastAddr( String val )
+    {
+        udpMulticastAddr = val;
+    }
+
+    /**
+     * Gets the udpMulticastAddr attribute of the LateralCacheAttributes object
+     * @return The udpMulticastAddr value
+     */
+    @Override
+    public String getUdpMulticastAddr()
+    {
+        return udpMulticastAddr;
+    }
+
+    /**
+     * Sets the udpMulticastPort attribute of the LateralCacheAttributes object
+     * @param val The new udpMulticastPort value
+     */
+    @Override
+    public void setUdpMulticastPort( int val )
+    {
+        udpMulticastPort = val;
+    }
+
+    /**
+     * Gets the udpMulticastPort attribute of the LateralCacheAttributes object
+     * @return The udpMulticastPort value
+     */
+    @Override
+    public int getUdpMulticastPort()
+    {
+        return udpMulticastPort;
+    }
+
+    /**
+     * Sets the transmissionType attribute of the LateralCacheAttributes object
+     * @param val The new transmissionType value
+     */
+    @Override
+    public void setTransmissionType( Type val )
+    {
+        this.transmissionType = val;
+        this.transmissionTypeName = val.toString();
+    }
+
+    /**
+     * Gets the transmissionType attribute of the LateralCacheAttributes object
+     * @return The transmissionType value
+     */
+    @Override
+    public Type getTransmissionType()
+    {
+        return this.transmissionType;
+    }
+
+    /**
+     * Sets the transmissionTypeName attribute of the LateralCacheAttributes object
+     * @param val The new transmissionTypeName value
+     */
+    @Override
+    public void setTransmissionTypeName( String val )
+    {
+        this.transmissionTypeName = val;
+        this.transmissionType = Type.valueOf(val);
+    }
+
+    /**
+     * Gets the transmissionTypeName attribute of the LateralCacheAttributes object
+     * @return The transmissionTypeName value
+     */
+    @Override
+    public String getTransmissionTypeName()
+    {
+        return this.transmissionTypeName;
+    }
+
+    /**
+     * Sets the outgoingOnlyMode attribute of the ILateralCacheAttributes. When this is true the
+     * lateral cache will only issue put and remove order and will not try to retrieve elements from
+     * other lateral caches.
+     * @param val The new transmissionTypeName value
+     */
+    @Override
+    public void setPutOnlyMode( boolean val )
+    {
+        this.putOnlyMode = val;
+    }
+
+    /**
+     * @return The outgoingOnlyMode value. Stops gets from going remote.
+     */
+    @Override
+    public boolean getPutOnlyMode()
+    {
+        return putOnlyMode;
+    }
+
+    /**
+     * Returns a clone of the attributes.
+     * @return Self
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            //noop
+        }
+        return this;
+    }
+
+    /**
+     * @param receive The receive to set.
+     */
+    @Override
+    public void setReceive( boolean receive )
+    {
+        this.receive = receive;
+    }
+
+    /**
+     * @return Returns the receive.
+     */
+    @Override
+    public boolean isReceive()
+    {
+        return receive;
+    }
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @param zombieQueueMaxSize The zombieQueueMaxSize to set.
+     */
+    @Override
+    public void setZombieQueueMaxSize( int zombieQueueMaxSize )
+    {
+        this.zombieQueueMaxSize = zombieQueueMaxSize;
+    }
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @return Returns the zombieQueueMaxSize.
+     */
+    @Override
+    public int getZombieQueueMaxSize()
+    {
+        return zombieQueueMaxSize;
+    }
+
+    /**
+     * @return debug string.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        //buf.append( "cacheName=" + cacheName + "\n" );
+        //buf.append( "putOnlyMode=" + putOnlyMode + "\n" );
+        //buf.append( "transmissionTypeName=" + transmissionTypeName + "\n" );
+        //buf.append( "transmissionType=" + transmissionType + "\n" );
+        //buf.append( "tcpServer=" + tcpServer + "\n" );
+        buf.append( transmissionTypeName + httpServer + udpMulticastAddr + String.valueOf( udpMulticastPort ) );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheMonitor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheMonitor.java
new file mode 100644
index 0000000..7d0b6cc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheMonitor.java
@@ -0,0 +1,290 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheManager;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Used to monitor and repair any failed connection for the lateral cache service. By default the
+ * monitor operates in a failure driven mode. That is, it goes into a wait state until there is an
+ * error. Upon the notification of a connection error, the monitor changes to operate in a time
+ * driven mode. That is, it attempts to recover the connections on a periodic basis. When all failed
+ * connections are restored, it changes back to the failure driven mode.
+ */
+public class LateralCacheMonitor
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralCacheMonitor.class );
+
+    /** How long to wait between runs */
+    private static long idlePeriod = 20 * 1000;
+
+    /**
+     * Must make sure LateralCacheMonitor is started before any lateral error can be detected!
+     */
+    private boolean alright = true;
+
+    /**
+     * shutdown flag
+     */
+    private boolean shutdown = false;
+
+    /** code for eror */
+    private static final int ERROR = 1;
+
+    /** The mode we are running in. Error driven */
+    private static int mode = ERROR;
+
+    /** The manager */
+    private final ILateralCacheManager manager;
+
+    /**
+     * Configures the idle period between repairs.
+     * <p>
+     * @param idlePeriod The new idlePeriod value
+     */
+    public static void setIdlePeriod( long idlePeriod )
+    {
+        if ( idlePeriod > LateralCacheMonitor.idlePeriod )
+        {
+            LateralCacheMonitor.idlePeriod = idlePeriod;
+        }
+    }
+
+    /**
+     * Allows close classes, ie testers to set the idle period to something testable.
+     * <p>
+     * @param idlePeriod
+     */
+    protected static void forceShortIdlePeriod( long idlePeriod )
+    {
+        LateralCacheMonitor.idlePeriod = idlePeriod;
+    }
+
+    /**
+     * Constructor for the LateralCacheMonitor object
+     * <p>
+     * It's the clients responsibility to decide how many of these there will be.
+     * <p>
+     * @param manager
+     */
+    public LateralCacheMonitor( ILateralCacheManager manager )
+    {
+        this.manager = manager;
+    }
+
+    /**
+     * Notifies the cache monitor that an error occurred, and kicks off the error recovery process.
+     */
+    public void notifyError()
+    {
+        bad();
+        synchronized ( this )
+        {
+            notify();
+        }
+    }
+
+    /**
+     * Notifies the cache monitor that the service shall shut down
+     */
+    public void notifyShutdown()
+    {
+        synchronized ( this )
+        {
+            this.shutdown = true;
+            notify();
+        }
+    }
+
+    /**
+     * Main processing method for the LateralCacheMonitor object
+     */
+    @Override
+    public void run()
+    {
+        do
+        {
+            if ( mode == ERROR )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    if ( alright )
+                    {
+                        log.debug( "ERROR DRIVEN MODE: alright = " + alright
+                            + ", connection monitor will wait for an error." );
+                    }
+                    else
+                    {
+                        log.debug( "ERROR DRIVEN MODE: alright = " + alright + " connection monitor running." );
+                    }
+                }
+
+                synchronized ( this )
+                {
+                    if ( alright )
+                    {
+                        // Failure driven mode.
+                        try
+                        {
+                            wait();
+                            // wake up only if there is an error.
+                        }
+                        catch ( InterruptedException ignore )
+                        {
+                            //no op, this is expected
+                        }
+                    }
+                }
+            }
+            else
+            {
+                log.debug( "TIME DRIVEN MODE: connection monitor will sleep for " + idlePeriod + " after this run." );
+                // Time driven mode: sleep between each round of recovery
+                // attempt.
+                // will need to test not just check status
+            }
+
+            // check for requested shutdown
+            synchronized ( this )
+            {
+                if (shutdown)
+                {
+                    log.info( "Shutting down cache monitor" );
+                    return;
+                }
+            }
+
+            // The "alright" flag must be false here.
+            // Simply presume we can fix all the errors until proven otherwise.
+            synchronized ( this )
+            {
+                alright = true;
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Cache monitor running." );
+            }
+
+            // Monitor each LateralCacheManager instance one after the other.
+            // Each LateralCacheManager corresponds to one lateral connection.
+            log.info( "LateralCacheManager.instances.size() = " + manager.getInstances().size() );
+            //for
+            int cnt = 0;
+            for (ILateralCacheManager mgr : manager.getInstances().values())
+            {
+                cnt++;
+                try
+                {
+                    // If any cache is in error, it strongly suggests all caches
+                    // managed by the
+                    // same LateralCacheManager instance are in error. So we fix
+                    // them once and for all.
+                    //for
+                    //log.info( "\n " + cnt + "- mgr.lca.getTcpServer() = " + mgr.lca.getTcpServer() + " mgr = " + mgr );
+                    log.info( "\n " + cnt + "- mgr.getCaches().size() = " + mgr.getCaches().size() );
+
+                    if ( mgr.getCaches().size() == 0 )
+                    {
+                        // there is probably a problem.
+                        // monitor may be running when we just started up and
+                        // there
+                        // is not a cache yet.
+                        // if this is error driven mode, mark as bad,
+                        // otherwise we will come back around again.
+                        if ( mode == ERROR )
+                        {
+                            bad();
+                        }
+                    }
+
+                    for (LateralCacheNoWait<?, ?> c : mgr.getCaches().values())
+                    {
+                        if ( c.getStatus() == CacheStatus.ERROR )
+                        {
+                            log.info( "found LateralCacheNoWait in error, " + c.toString() );
+
+                            LateralCacheRestore repairer = new LateralCacheRestore( mgr );
+                            // If we can't fix them, just skip and re-try in the
+                            // next round.
+                            if ( repairer.canFix() )
+                            {
+                                repairer.fix();
+                            }
+                            else
+                            {
+                                bad();
+                            }
+                            //break;
+                        }
+                        else
+                        {
+                            log.info( "Lateral Cache No Wait not in error" );
+                        }
+                    }
+                }
+                catch ( Exception ex )
+                {
+                    bad();
+                    // Problem encountered in fixing the caches managed by a
+                    // LateralCacheManager instance.
+                    // Soldier on to the next LateralCacheManager instance.
+                    log.error( "Problem encountered in fixing the caches", ex );
+                }
+            }
+
+            try
+            {
+                // don't want to sleep after waking from an error
+                // run immediately and sleep here.
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Lateral cache monitor sleeping for " + idlePeriod + " between runs." );
+                }
+
+                Thread.sleep( idlePeriod );
+            }
+            catch ( InterruptedException ex )
+            {
+                // ignore;
+            }
+        }
+        while ( true );
+    }
+
+    /**
+     * Sets the "alright" flag to false in a critical section.
+     */
+    private void bad()
+    {
+        if ( alright )
+        {
+            synchronized ( this )
+            {
+                alright = false;
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWait.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWait.java
new file mode 100644
index 0000000..77ca1cf
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWait.java
@@ -0,0 +1,436 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.engine.CacheAdaptor;
+import org.apache.commons.jcs.engine.CacheEventQueueFactory;
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.rmi.UnmarshalException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used to queue up update requests to the underlying cache. These requests will be processed in
+ * their order of arrival via the cache event queue processor.
+ */
+public class LateralCacheNoWait<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( LateralCacheNoWait.class );
+
+    /** The cache */
+    private final LateralCache<K, V> cache;
+
+    /** The event queue */
+    private ICacheEventQueue<K, V> eventQueue;
+
+    /** times get called */
+    private int getCount = 0;
+
+    /** times remove called */
+    private int removeCount = 0;
+
+    /** times put called */
+    private int putCount = 0;
+
+    /**
+     * Constructs with the given lateral cache, and fires up an event queue for aysnchronous
+     * processing.
+     * <p>
+     * @param cache
+     */
+    public LateralCacheNoWait( LateralCache<K, V> cache )
+    {
+        this.cache = cache;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Constructing LateralCacheNoWait, LateralCache = [" + cache + "]" );
+        }
+
+        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
+        this.eventQueue = fact.createCacheEventQueue( new CacheAdaptor<K, V>( cache ), CacheInfo.listenerId, cache
+            .getCacheName(), cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
+            .getAuxiliaryCacheAttributes().getEventQueueType() );
+
+        // need each no wait to handle each of its real updates and removes,
+        // since there may
+        // be more than one per cache? alternative is to have the cache
+        // perform updates using a different method that specifies the listener
+        // this.q = new CacheEventQueue(new CacheAdaptor(this),
+        // LateralCacheInfo.listenerId, cache.getCacheName());
+        if ( cache.getStatus() == CacheStatus.ERROR )
+        {
+            eventQueue.destroy();
+        }
+    }
+
+    /**
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        putCount++;
+        try
+        {
+            eventQueue.addPutEvent( ce );
+        }
+        catch ( IOException ex )
+        {
+            log.error( ex );
+            eventQueue.destroy();
+        }
+    }
+
+    /**
+     * Synchronously reads from the lateral cache.
+     * <p>
+     * @param key
+     * @return ICacheElement<K, V> if found, else null
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+    {
+        getCount++;
+        if ( this.getStatus() != CacheStatus.ERROR )
+        {
+            try
+            {
+                return cache.get( key );
+            }
+            catch ( UnmarshalException ue )
+            {
+                log.debug( "Retrying the get owing to UnmarshalException..." );
+                try
+                {
+                    return cache.get( key );
+                }
+                catch ( IOException ex )
+                {
+                    log.error( "Failed in retrying the get for the second time." );
+                    eventQueue.destroy();
+                }
+            }
+            catch ( IOException ex )
+            {
+                eventQueue.destroy();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Synchronously reads from the lateral cache.
+     * <p>
+     * @param pattern
+     * @return ICacheElement<K, V> if found, else empty
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching(String pattern)
+    {
+        getCount++;
+        if ( this.getStatus() != CacheStatus.ERROR )
+        {
+            try
+            {
+                return cache.getMatching( pattern );
+            }
+            catch ( UnmarshalException ue )
+            {
+                log.debug( "Retrying the get owing to UnmarshalException." );
+                try
+                {
+                    return cache.getMatching( pattern );
+                }
+                catch ( IOException ex )
+                {
+                    log.error( "Failed in retrying the get for the second time." );
+                    eventQueue.destroy();
+                }
+            }
+            catch ( IOException ex )
+            {
+                eventQueue.destroy();
+            }
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        try
+        {
+            return cache.getKeySet();
+        }
+        catch ( IOException ex )
+        {
+            log.error( ex );
+            eventQueue.destroy();
+        }
+        return Collections.emptySet();
+    }
+
+    /**
+     * Adds a remove request to the lateral cache.
+     * <p>
+     * @param key
+     * @return always false
+     */
+    @Override
+    public boolean remove( K key )
+    {
+        removeCount++;
+        try
+        {
+            eventQueue.addRemoveEvent( key );
+        }
+        catch ( IOException ex )
+        {
+            log.error( ex );
+            eventQueue.destroy();
+        }
+        return false;
+    }
+
+    /** Adds a removeAll request to the lateral cache. */
+    @Override
+    public void removeAll()
+    {
+        try
+        {
+            eventQueue.addRemoveAllEvent();
+        }
+        catch ( IOException ex )
+        {
+            log.error( ex );
+            eventQueue.destroy();
+        }
+    }
+
+    /** Adds a dispose request to the lateral cache. */
+    @Override
+    public void dispose()
+    {
+        try
+        {
+            eventQueue.addDisposeEvent();
+        }
+        catch ( IOException ex )
+        {
+            log.error( ex );
+            eventQueue.destroy();
+        }
+    }
+
+    /**
+     * No lateral invocation.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return cache.getSize();
+    }
+
+    /**
+     * No lateral invocation.
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return cache.getCacheType();
+    }
+
+    /**
+     * Returns the asyn cache status. An error status indicates either the lateral connection is not
+     * available, or the asyn queue has been unexpectedly destroyed. No lateral invocation.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return eventQueue.isWorking() ? cache.getStatus() : CacheStatus.ERROR;
+    }
+
+    /**
+     * Gets the cacheName attribute of the LateralCacheNoWait object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cache.getCacheName();
+    }
+
+    /**
+     * Replaces the lateral cache service handle with the given handle and reset the queue by
+     * starting up a new instance.
+     * <p>
+     * @param lateral
+     */
+    public void fixCache( ICacheServiceNonLocal<K, V> lateral )
+    {
+        cache.fixCache( lateral );
+        resetEventQ();
+    }
+
+    /**
+     * Resets the event q by first destroying the existing one and starting up new one.
+     */
+    public void resetEventQ()
+    {
+        if ( eventQueue.isWorking() )
+        {
+            eventQueue.destroy();
+        }
+        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
+        this.eventQueue = fact.createCacheEventQueue( new CacheAdaptor<K, V>( cache ), CacheInfo.listenerId, cache
+            .getCacheName(), cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
+            .getAuxiliaryCacheAttributes().getEventQueueType() );
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return cache.getAuxiliaryCacheAttributes();
+    }
+
+    /**
+     * getStats
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * this won't be called since we don't do ICache logging here.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return "Lateral Cache No Wait";
+    }
+
+    /**
+     * @return statistics about this communication
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Lateral Cache No Wait" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        // get the stats from the event queue too
+        IStats eqStats = this.eventQueue.getStatistics();
+        elems.addAll(eqStats.getStatElements());
+
+        elems.add(new StatElement<Integer>( "Get Count", Integer.valueOf(this.getCount) ) );
+        elems.add(new StatElement<Integer>( "Remove Count", Integer.valueOf(this.removeCount) ) );
+        elems.add(new StatElement<Integer>( "Put Count", Integer.valueOf(this.putCount) ) );
+        elems.add(new StatElement<AuxiliaryCacheAttributes>( "Attributes", cache.getAuxiliaryCacheAttributes() ) );
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * @return debugging info.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( " LateralCacheNoWait " );
+        buf.append( " Status = " + this.getStatus() );
+        buf.append( " cache = [" + cache.toString() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacade.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacade.java
new file mode 100644
index 0000000..0a5d0b2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacade.java
@@ -0,0 +1,533 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used to provide access to multiple services under nowait protection. Composite factory should
+ * construct LateralCacheNoWaitFacade to give to the composite cache out of caches it constructs
+ * from the varies manager to lateral services. Perhaps the lateralcache factory should be able to
+ * do this.
+ */
+public class LateralCacheNoWaitFacade<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralCacheNoWaitFacade.class );
+
+    /** The queuing facade to the client. */
+    public LateralCacheNoWait<K, V>[] noWaits;
+
+    /** The region name */
+    private final String cacheName;
+
+    /** A cache listener */
+    private ILateralCacheListener<K, V> listener;
+
+    /** User configurable attributes. */
+    private final ILateralCacheAttributes lateralCacheAttributes;
+
+    /** Disposed state of this facade */
+    private boolean disposed = false;
+
+    /**
+     * Constructs with the given lateral cache, and fires events to any listeners.
+     * <p>
+     * @param noWaits
+     * @param cattr
+     */
+    public LateralCacheNoWaitFacade(ILateralCacheListener<K, V> listener, LateralCacheNoWait<K, V>[] noWaits, ILateralCacheAttributes cattr )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "CONSTRUCTING NO WAIT FACADE" );
+        }
+        this.listener = listener;
+        this.noWaits = noWaits;
+        this.cacheName = cattr.getCacheName();
+        this.lateralCacheAttributes = cattr;
+    }
+
+    /**
+     * Tells you if the no wait is in the list or not.
+     * <p>
+     * @param noWait
+     * @return true if the noWait is in the list.
+     */
+    public boolean containsNoWait( LateralCacheNoWait<K, V> noWait )
+    {
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            // we know noWait isn't null
+            if ( noWait.equals( noWaits[i] ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Adds a no wait to the list if it isn't already in the list.
+     * <p>
+     * @param noWait
+     * @return true if it wasn't already contained
+     */
+    public synchronized boolean addNoWait( LateralCacheNoWait<K, V> noWait )
+    {
+        if ( noWait == null )
+        {
+            return false;
+        }
+
+        if ( containsNoWait( noWait ) )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "No Wait already contained, [" + noWait + "]" );
+            }
+            return false;
+        }
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        LateralCacheNoWait<K, V>[] newArray = new LateralCacheNoWait[noWaits.length + 1];
+
+        System.arraycopy( noWaits, 0, newArray, 0, noWaits.length );
+
+        // set the last position to the new noWait
+        newArray[noWaits.length] = noWait;
+
+        noWaits = newArray;
+
+        return true;
+    }
+
+    /**
+     * Removes a no wait from the list if it is already there.
+     * <p>
+     * @param noWait
+     * @return true if it was already in the array
+     */
+    public synchronized boolean removeNoWait( LateralCacheNoWait<K, V> noWait )
+    {
+        if ( noWait == null )
+        {
+            return false;
+        }
+
+        int position = -1;
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            // we know noWait isn't null
+            if ( noWait.equals( noWaits[i] ) )
+            {
+                position = i;
+                break;
+            }
+        }
+
+        if ( position == -1 )
+        {
+            return false;
+        }
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        LateralCacheNoWait<K, V>[] newArray = new LateralCacheNoWait[noWaits.length - 1];
+
+        System.arraycopy( noWaits, 0, newArray, 0, position );
+        if ( noWaits.length != position )
+        {
+            System.arraycopy( noWaits, position + 1, newArray, position, noWaits.length - position - 1 );
+        }
+        noWaits = newArray;
+
+        return true;
+    }
+
+    /**
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "updating through lateral cache facade, noWaits.length = " + noWaits.length );
+        }
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].update( ce );
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+    }
+
+    /**
+     * Synchronously reads from the lateral cache.
+     * <p>
+     * @param key
+     * @return ICacheElement
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+    {
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            try
+            {
+                ICacheElement<K, V> obj = noWaits[i].get( key );
+
+                if ( obj != null )
+                {
+                    // TODO: return after first success
+                    // could do this simultaneously
+                    // serious blocking risk here
+                    return obj;
+                }
+            }
+            catch ( Exception ex )
+            {
+                log.error( "Failed to get", ex );
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Synchronously reads from the lateral cache. Get a response from each! This will be slow.
+     * Merge them.
+     * <p>
+     * @param pattern
+     * @return ICacheElement
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching(String pattern)
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            try
+            {
+                elements.putAll( noWaits[i].getMatching( pattern ) );
+            }
+            catch ( Exception ex )
+            {
+                log.error( "Failed to get", ex );
+            }
+        }
+        return elements;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        HashSet<K> allKeys = new HashSet<K>();
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            AuxiliaryCache<K, V> aux = noWaits[i];
+            if ( aux != null )
+            {
+                Set<K> keys = aux.getKeySet();
+                if(keys != null)
+                {
+                    allKeys.addAll( keys );
+                }
+            }
+        }
+        return allKeys;
+    }
+
+    /**
+     * Adds a remove request to the lateral cache.
+     * <p>
+     * @param key
+     * @return always false.
+     */
+    @Override
+    public boolean remove( K key )
+    {
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].remove( key );
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+        return false;
+    }
+
+    /**
+     * Adds a removeAll request to the lateral cache.
+     */
+    @Override
+    public void removeAll()
+    {
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].removeAll();
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+    }
+
+    /** Adds a dispose request to the lateral cache. */
+    @Override
+    public void dispose()
+    {
+        try
+        {
+            if ( listener != null )
+            {
+                listener.dispose();
+                listener = null;
+            }
+
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].dispose();
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+        finally
+        {
+            disposed = true;
+        }
+    }
+
+    /**
+     * No lateral invocation.
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return 0;
+        //cache.getSize();
+    }
+
+    /**
+     * Gets the cacheType attribute of the LateralCacheNoWaitFacade object.
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.LATERAL_CACHE;
+    }
+
+    /**
+     * Gets the cacheName attribute of the LateralCacheNoWaitFacade object.
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return "";
+        //cache.getCacheName();
+    }
+
+    /**
+     * Gets the status attribute of the LateralCacheNoWaitFacade object
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        if (disposed)
+        {
+            return CacheStatus.DISPOSED;
+        }
+
+        if (noWaits.length == 0 || listener != null)
+        {
+            return CacheStatus.ALIVE;
+        }
+
+        CacheStatus[] statii = new CacheStatus[noWaits.length];
+        for (int i = 0; i < noWaits.length; i++)
+        {
+            statii[i] = noWaits[i].getStatus();
+        }
+        // It's alive if ANY of its nowaits is alive
+        for (int i = 0; i < noWaits.length; i++)
+        {
+            if (statii[i] == CacheStatus.ALIVE)
+            {
+                return CacheStatus.ALIVE;
+            }
+        }
+        // It's alive if ANY of its nowaits is in error, but
+        // none are alive, then it's in error
+        for (int i = 0; i < noWaits.length; i++)
+        {
+            if (statii[i] == CacheStatus.ERROR)
+            {
+                return CacheStatus.ERROR;
+            }
+        }
+
+        // Otherwise, it's been disposed, since it's the only status left
+        return CacheStatus.DISPOSED;
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return this.lateralCacheAttributes;
+    }
+
+    /**
+     * @return "LateralCacheNoWaitFacade: " + cacheName;
+     */
+    @Override
+    public String toString()
+    {
+        return "LateralCacheNoWaitFacade: " + cacheName;
+    }
+
+    /**
+     * this won't be called since we don't do ICache logging here.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return "Lateral Cache No Wait";
+    }
+
+    /**
+     * getStats
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * @return IStats
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Lateral Cache No Wait Facade" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        if ( noWaits != null )
+        {
+            elems.add(new StatElement<Integer>( "Number of No Waits", Integer.valueOf(noWaits.length) ) );
+
+            for ( LateralCacheNoWait<K, V> lcnw : noWaits )
+            {
+                if ( lcnw != null )
+                {
+                    // get the stats from the super too
+                    IStats sStats = lcnw.getStatistics();
+                    elems.addAll(sStats.getStatElements());
+                }
+            }
+        }
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheRestore.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheRestore.java
new file mode 100644
index 0000000..5518744
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheRestore.java
@@ -0,0 +1,101 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheManager;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheObserver;
+import org.apache.commons.jcs.engine.behavior.ICacheRestore;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+
+/**
+ * Used to repair the lateral caches managed by the associated instance of LateralCacheManager.
+ */
+public class LateralCacheRestore
+    implements ICacheRestore
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralCacheRestore.class );
+
+    /** The lateral manager. */
+    private final ILateralCacheManager lcm;
+
+    /** fixable indicator */
+    private boolean canFix = true;
+
+    /** handle */
+    private Object lateralObj;
+
+    /**
+     * Constructs with the given instance of LateralCacheManager.
+     * <p>
+     * @param lcm
+     */
+    public LateralCacheRestore( ILateralCacheManager lcm )
+    {
+        this.lcm = lcm;
+    }
+
+    /**
+     * Returns true if the connection to the lateral host for the corresponding cache manager can
+     * be successfully re-established.
+     * <p>
+     * @return whether or not the cache can be fixed.
+     */
+    @Override
+    public boolean canFix()
+    {
+        if ( !canFix )
+        {
+            return canFix;
+        }
+
+        try
+        {
+            lateralObj = lcm.fixService();
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Can't fix " + ex.getMessage() );
+            canFix = false;
+        }
+
+        return canFix;
+    }
+
+    /**
+     * Fixes up all the caches managed by the associated cache manager.
+     */
+    @Override
+    @SuppressWarnings("unchecked") // TODO: Strange cast. Need to revisit
+    public void fix()
+    {
+        if ( !canFix )
+        {
+            return;
+        }
+        lcm.fixCaches( (ICacheServiceNonLocal<Serializable, Serializable>) lateralObj, (ILateralCacheObserver) lateralObj );
+        String msg = "Lateral connection resumed.";
+        log.info( msg );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheWatchRepairable.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheWatchRepairable.java
new file mode 100644
index 0000000..ccbd445
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheWatchRepairable.java
@@ -0,0 +1,34 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheObserver;
+import org.apache.commons.jcs.engine.CacheWatchRepairable;
+
+/**
+ * Same as CacheWatcherWrapper but implements the IRemoteCacheWatch interface.
+ *
+ */
+public class LateralCacheWatchRepairable
+    extends CacheWatchRepairable
+    implements ILateralCacheObserver
+{
+    //nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCommand.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCommand.java
new file mode 100644
index 0000000..5c4ff57
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralCommand.java
@@ -0,0 +1,47 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+/**
+ * Enumeration of the available lateral commands
+ */
+public enum LateralCommand
+{
+    /** The command for updates */
+    UPDATE,
+
+    /** The command for removes */
+    REMOVE,
+
+    /** The command instructing us to remove all */
+    REMOVEALL,
+
+    /** The command for disposing the cache. */
+    DISPOSE,
+
+    /** Command to return an object. */
+    GET,
+
+    /** Command to return an object. */
+    GET_MATCHING,
+
+    /** Command to get all keys */
+    GET_KEYSET
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralElementDescriptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralElementDescriptor.java
new file mode 100644
index 0000000..85dced6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/LateralElementDescriptor.java
@@ -0,0 +1,83 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.Serializable;
+
+/**
+ * This class wraps command to other laterals. It is essentially a
+ * JCS-TCP-Lateral packet. The headers specify the action the receiver should
+ * take.
+ */
+public class LateralElementDescriptor<K, V>
+    implements Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = 5268222498076063575L;
+
+    /** The Cache Element that we are distributing. */
+    public ICacheElement<K, V> ce;
+
+    /**
+     * The id of the the source of the request. This is used to prevent infinite
+     * loops.
+     */
+    public long requesterId;
+
+    /** The operation has been requested by the client. */
+    public LateralCommand command = LateralCommand.UPDATE;
+
+    /**
+     * The hashcode value for this element.
+     */
+    public int valHashCode = -1;
+
+    /** Constructor for the LateralElementDescriptor object */
+    public LateralElementDescriptor()
+    {
+        super();
+    }
+
+    /**
+     * Constructor for the LateralElementDescriptor object
+     * <p>
+     * @param ce ICacheElement<K, V> payload
+     */
+    public LateralElementDescriptor( ICacheElement<K, V> ce )
+    {
+        this.ce = ce;
+    }
+
+    /**
+     * @return String, all the important values that can be configured
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n LateralElementDescriptor " );
+        buf.append( "\n command = [" + this.command + "]" );
+        buf.append( "\n valHashCode = [" + this.valHashCode + "]" );
+        buf.append( "\n ICacheElement = [" + this.ce + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/ZombieLateralCacheWatch.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/ZombieLateralCacheWatch.java
new file mode 100644
index 0000000..9d2bd8e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/ZombieLateralCacheWatch.java
@@ -0,0 +1,34 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheObserver;
+import org.apache.commons.jcs.engine.ZombieCacheWatch;
+
+/**
+ * Description of the Class
+ *
+ */
+public class ZombieLateralCacheWatch
+    extends ZombieCacheWatch
+    implements ILateralCacheObserver
+{
+    // nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheAttributes.java
new file mode 100644
index 0000000..8c28981
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheAttributes.java
@@ -0,0 +1,202 @@
+package org.apache.commons.jcs.auxiliary.lateral.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+
+import java.io.Serializable;
+
+/**
+ * This interface defines configuration options common to lateral cache plugins.
+ * <p>
+ * TODO it needs to be trimmed down. The old version had features for every lateral. Now, the
+ * individual laterals have their own specific attributes interfaces.
+ */
+public interface ILateralCacheAttributes
+    extends Serializable, AuxiliaryCacheAttributes
+{
+    enum Type
+    {
+        /** HTTP type */
+        HTTP, // 1
+
+        /** UDP type */
+        UDP, // 2
+
+        /** TCP type */
+        TCP, // 3
+
+        /** XMLRPC type */
+        XMLRPC // 4
+    }
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     */
+    int DEFAULT_ZOMBIE_QUEUE_MAX_SIZE = 1000;
+
+    /**
+     * Sets the httpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new httpServer value
+     */
+    void setHttpServer( String val );
+
+    /**
+     * Gets the httpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The httpServer value
+     */
+    String getHttpServer();
+
+    /**
+     * Sets the httpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new tcpListenerPort value
+     */
+    void setHttpListenerPort( int val );
+
+    /**
+     * Gets the httpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The httpListenerPort value
+     */
+    int getHttpListenerPort();
+
+    /**
+     * Sets the httpServers attribute of the LateralCacheAttributes object
+     * <p>
+     * @param val The new httpServers value
+     */
+    void setHttpServers( String val );
+
+    /**
+     * Gets the httpSrvers attribute of the LateralCacheAttributes object
+     * <p>
+     * @return The httpServers value
+     */
+    String getHttpServers();
+
+    /**
+     * Sets the udpMulticastAddr attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new udpMulticastAddr value
+     */
+    void setUdpMulticastAddr( String val );
+
+    /**
+     * Gets the udpMulticastAddr attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The udpMulticastAddr value
+     */
+    String getUdpMulticastAddr();
+
+    /**
+     * Sets the udpMulticastPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new udpMulticastPort value
+     */
+    void setUdpMulticastPort( int val );
+
+    /**
+     * Gets the udpMulticastPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The udpMulticastPort value
+     */
+    int getUdpMulticastPort();
+
+    /**
+     * Sets the transmissionType attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new transmissionType value
+     */
+    void setTransmissionType( Type val );
+
+    /**
+     * Gets the transmissionType attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The transmissionType value
+     */
+    Type getTransmissionType();
+
+    /**
+     * Sets the transmissionTypeName attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new transmissionTypeName value
+     */
+    void setTransmissionTypeName( String val );
+
+    /**
+     * Gets the transmissionTypeName attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The transmissionTypeName value
+     */
+    String getTransmissionTypeName();
+
+    /**
+     * Sets the putOnlyMode attribute of the ILateralCacheAttributes. When this is true the lateral
+     * cache will only issue put and remove order and will not try to retrieve elements from other
+     * lateral caches.
+     * <p>
+     * @param val The new transmissionTypeName value
+     */
+    void setPutOnlyMode( boolean val );
+
+    /**
+     * @return The outgoingOnlyMode value. Stops gets from going remote.
+     */
+    boolean getPutOnlyMode();
+
+    /**
+     * @param receive The receive to set.
+     */
+    void setReceive( boolean receive );
+
+    /**
+     * Should a listener be created. By default this is true.
+     * <p>
+     * If this is false the lateral will connect to others but it will not create a listener to
+     * receive.
+     * <p>
+     * It is possible if two laterals are misconfigured that lateral A may have a region R1 that is
+     * not configured for the lateral but another is. And if cache B has region R1 configured for
+     * lateral distribution, A will get messages for R1 but not send them.
+     * <p>
+     * @return true if we should have a listener connection
+     */
+    boolean isReceive();
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @param zombieQueueMaxSize The zombieQueueMaxSize to set.
+     */
+    void setZombieQueueMaxSize( int zombieQueueMaxSize );
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @return Returns the zombieQueueMaxSize.
+     */
+    int getZombieQueueMaxSize();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheListener.java
new file mode 100644
index 0000000..581f593
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheListener.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.auxiliary.lateral.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+
+/**
+ * Listens for lateral cache event notification.
+ */
+public interface ILateralCacheListener<K, V>
+    extends ICacheListener<K, V>
+{
+    /**
+     * Initialize this listener
+     */
+    void init();
+
+    /**
+     * @param cacheMgr
+     *            The cacheMgr to set.
+     */
+    void setCacheManager( ICompositeCacheManager cacheMgr );
+
+    /**
+     * @return Returns the cacheMgr.
+     */
+    ICompositeCacheManager getCacheManager();
+
+    /**
+     * Dispose this listener
+     */
+    void dispose();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheManager.java
new file mode 100644
index 0000000..2782954
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheManager.java
@@ -0,0 +1,72 @@
+package org.apache.commons.jcs.auxiliary.lateral.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheManager;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWait;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * This helps ensure some common behavior among LateraLCacheManagers for things
+ * such as monitors.
+ * <p>
+ * @author Aaron Smuts
+ */
+public interface ILateralCacheManager
+    extends AuxiliaryCacheManager
+{
+    /**
+     * This is a temporary solution that allows the monitor to get the instances
+     * of a manager.
+     * <p>
+     * @return Map
+     */
+    Map<String, ? extends ILateralCacheManager> getInstances();
+
+    /**
+     * This is a temporary solution that allows the monitor to get caches from an
+     * instance of a manager.
+     * <p>
+     * @return Map
+     */
+    Map<String, LateralCacheNoWait<?, ?>> getCaches();
+
+    /**
+     * The restore calls this on the manger if a cache if found to be in error.
+     * <p>
+     * @return Object is the service if it can be fixed.
+     * @throws IOException
+     *             if the service cannot be fixed.
+     */
+    Object fixService() throws IOException;
+
+    /**
+     * Sets the corrected service. The restore process will call this if it gets
+     * a good service back from fixService.
+     * <p>
+     * @param lateralService
+     * @param lateralWatch
+     */
+    void fixCaches( ICacheServiceNonLocal<Serializable, Serializable> lateralService, ILateralCacheObserver lateralWatch );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheObserver.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheObserver.java
new file mode 100644
index 0000000..c2ce9ee
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/behavior/ILateralCacheObserver.java
@@ -0,0 +1,31 @@
+package org.apache.commons.jcs.auxiliary.lateral.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheObserver;
+
+/**
+ * Description of the Interface
+ */
+public interface ILateralCacheObserver
+    extends ICacheObserver
+{
+    // nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/package.html
new file mode 100644
index 0000000..d38b4cc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/package.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    Root package for the lateral cache family. Lateral caches broadcast puts
+    and removals to other local caches.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheFactory.java
new file mode 100644
index 0000000..c6aa3b6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheFactory.java
@@ -0,0 +1,219 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAbstractFactory;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWait;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWaitFacade;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.discovery.UDPDiscoveryManager;
+import org.apache.commons.jcs.utils.discovery.UDPDiscoveryService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * Constructs a LateralCacheNoWaitFacade for the given configuration. Each lateral service / local
+ * relationship is managed by one manager. This manager can have multiple caches. The remote
+ * relationships are consolidated and restored via these managers.
+ * <p>
+ * The facade provides a front to the composite cache so the implementation is transparent.
+ */
+public class LateralTCPCacheFactory
+    extends LateralCacheAbstractFactory
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralTCPCacheFactory.class );
+
+    /** Non singleton manager. Used by this instance of the factory. */
+    private LateralTCPDiscoveryListenerManager lateralTCPDiscoveryListenerManager;
+
+    /**
+     * Creates a TCP lateral.
+     * <p>
+     * @param iaca
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache(
+            AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
+           ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        ITCPLateralCacheAttributes lac = (ITCPLateralCacheAttributes) iaca;
+        ArrayList<ICache<K, V>> noWaits = new ArrayList<ICache<K, V>>();
+
+        // pairs up the tcp servers and set the tcpServer value and
+        // get the manager and then get the cache
+        // no servers are required.
+        if ( lac.getTcpServers() != null )
+        {
+            StringTokenizer it = new StringTokenizer( lac.getTcpServers(), "," );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Configured for [" + it.countTokens() + "]  servers." );
+            }
+            while ( it.hasMoreElements() )
+            {
+                String server = (String) it.nextElement();
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "tcp server = " + server );
+                }
+                ITCPLateralCacheAttributes lacC = (ITCPLateralCacheAttributes) lac.copy();
+                lacC.setTcpServer( server );
+                LateralTCPCacheManager lcm = LateralTCPCacheManager.getInstance( lacC, cacheMgr, cacheEventLogger,
+                                                                                 elementSerializer );
+
+                // register for shutdown notification
+                cacheMgr.registerShutdownObserver( lcm );
+
+                ICache<K, V> ic = lcm.getCache( lacC.getCacheName() );
+                noWaits.add( ic );
+            }
+        }
+
+        ILateralCacheListener<K, V> listener = createListener( (ILateralCacheAttributes) iaca, cacheMgr );
+
+        // create the no wait facade.
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        LateralCacheNoWait<K, V>[] lcnwArray = noWaits.toArray( new LateralCacheNoWait[0] );
+        LateralCacheNoWaitFacade<K, V> lcnwf =
+            new LateralCacheNoWaitFacade<K, V>(listener, lcnwArray, (ILateralCacheAttributes) iaca );
+
+        // create udp discovery if available.
+        createDiscoveryService( lac, lcnwf, cacheMgr, cacheEventLogger, elementSerializer );
+
+        return lcnwf;
+    }
+
+    /**
+     * Makes sure a listener gets created. It will get monitored as soon as it
+     * is used.
+     * <p>
+     * This should be called by create cache.
+     * <p>
+     * @param lac  ILateralCacheAttributes
+     * @param cacheMgr
+     *
+     * @return the listener if created, else null
+     */
+    @Override
+    public <K, V>
+        ILateralCacheListener<K, V> createListener( ILateralCacheAttributes lac, ICompositeCacheManager cacheMgr )
+    {
+        ITCPLateralCacheAttributes attr = (ITCPLateralCacheAttributes) lac;
+        ILateralCacheListener<K, V> listener = null;
+
+        // don't create a listener if we are not receiving.
+        if ( attr.isReceive() )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Getting listener for " + lac );
+            }
+
+            try
+            {
+                // make a listener. if one doesn't exist
+                listener = LateralTCPListener.getInstance( attr, cacheMgr );
+
+                // register for shutdown notification
+                cacheMgr.registerShutdownObserver( (IShutdownObserver) listener );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem creating lateral listener", e );
+            }
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Not creating a listener since we are not receiving." );
+            }
+        }
+
+        return listener;
+    }
+
+    /**
+     * Creates the discovery service. Only creates this for tcp laterals right now.
+     * <p>
+     * @param lac ITCPLateralCacheAttributes
+     * @param lcnwf
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return null if none is created.
+     */
+    private synchronized <K, V> UDPDiscoveryService createDiscoveryService(
+            ITCPLateralCacheAttributes lac,
+            LateralCacheNoWaitFacade<K, V> lcnwf,
+            ICompositeCacheManager cacheMgr,
+            ICacheEventLogger cacheEventLogger,
+            IElementSerializer elementSerializer )
+    {
+        UDPDiscoveryService discovery = null;
+
+        // create the UDP discovery for the TCP lateral
+        if ( lac.isUdpDiscoveryEnabled() )
+        {
+            if ( lateralTCPDiscoveryListenerManager == null )
+            {
+                lateralTCPDiscoveryListenerManager = new LateralTCPDiscoveryListenerManager();
+            }
+
+            // One can be used for all regions
+            LateralTCPDiscoveryListener discoveryListener = lateralTCPDiscoveryListenerManager
+                .getDiscoveryListener( lac, cacheMgr, cacheEventLogger, elementSerializer );
+
+            discoveryListener.addNoWaitFacade( lac.getCacheName(), lcnwf );
+
+            // need a factory for this so it doesn't
+            // get dereferenced, also we don't want one for every region.
+            discovery = UDPDiscoveryManager.getInstance().getService( lac.getUdpDiscoveryAddr(),
+                                                                      lac.getUdpDiscoveryPort(),
+                                                                      lac.getTcpListenerPort(), cacheMgr);
+
+            discovery.addParticipatingCacheName( lac.getCacheName() );
+            discovery.addDiscoveryListener( discoveryListener );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Registered TCP lateral cache [" + lac.getCacheName() + "] with UDPDiscoveryService." );
+            }
+        }
+        return discovery;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheManager.java
new file mode 100644
index 0000000..b478873
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPCacheManager.java
@@ -0,0 +1,355 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.LateralCache;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAbstractManager;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheMonitor;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWait;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheWatchRepairable;
+import org.apache.commons.jcs.auxiliary.lateral.ZombieLateralCacheWatch;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheManager;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Creates lateral caches. Lateral caches are primarily used for removing non laterally configured
+ * caches. Non laterally configured cache regions should still be able to participate in removal.
+ * But if there is a non laterally configured cache hub, then lateral removals may be necessary. For
+ * flat webserver production environments, without a strong machine at the app server level,
+ * distribution and search may need to occur at the lateral cache level. This is currently not
+ * implemented in the lateral cache.
+ * <p>
+ * TODO: - need freeCache, release, getStats - need to find an interface acceptable for all - cache
+ * managers or a manager within a type
+ */
+public class LateralTCPCacheManager
+    extends LateralCacheAbstractManager
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( LateralTCPCacheManager.class );
+
+    /** The monitor */
+    private static LateralCacheMonitor monitor;
+
+    /** Address to instance map. */
+    private static final Map<String, LateralTCPCacheManager> instances =
+        new ConcurrentHashMap<String, LateralTCPCacheManager>();
+
+    /** ITCPLateralCacheAttributes */
+    private final ITCPLateralCacheAttributes lateralCacheAttributes;
+
+    /**
+     * Handle to the lateral cache service; or a zombie handle if failed to connect.
+     */
+    private ICacheServiceNonLocal<?, ?> lateralService;
+
+    /**
+     * Wrapper of the lateral cache watch service; or wrapper of a zombie service if failed to
+     * connect.
+     */
+    private final LateralCacheWatchRepairable lateralWatch;
+
+    /** This is set in the constructor. */
+    private final ICompositeCacheManager cacheMgr;
+
+    private final ICacheEventLogger cacheEventLogger;
+
+    private final IElementSerializer elementSerializer;
+
+    /**
+     * Returns an instance of the LateralCacheManager.
+     * <p>
+     * @param lca
+     * @param cacheMgr this allows the auxiliary to be passed a cache manager.
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return LateralTCPCacheManager
+     */
+    public static LateralTCPCacheManager getInstance( ITCPLateralCacheAttributes lca, ICompositeCacheManager cacheMgr,
+                                                      ICacheEventLogger cacheEventLogger,
+                                                      IElementSerializer elementSerializer )
+    {
+        synchronized ( instances )
+        {
+            String key = lca.getTcpServer();
+            LateralTCPCacheManager ins = instances.get( key );
+            if ( ins == null )
+            {
+                log.info( "Instance for [" + key + "] is null, creating" );
+
+                ins = instances.get( lca.getTcpServer() );
+                if ( ins == null )
+                {
+                    ins = new LateralTCPCacheManager( lca, cacheMgr, cacheEventLogger, elementSerializer );
+                    instances.put( key, ins );
+                }
+
+                createMonitor( ins );
+            }
+
+            return ins;
+        }
+    }
+
+    /**
+     * The monitor needs reference to one instance, actually just a type.
+     * <p>
+     * TODO refactor this.
+     * <p>
+     * @param instance
+     */
+    private static synchronized void createMonitor( ILateralCacheManager instance )
+    {
+        // only want one monitor per lateral type
+        // Fires up the monitoring daemon.
+        if ( monitor == null )
+        {
+            monitor = new LateralCacheMonitor( instance );
+            Thread t = new Thread( monitor );
+            t.setDaemon( true );
+            t.start();
+        }
+    }
+
+    /**
+     * Constructor for the LateralCacheManager object.
+     * <p>
+     * @param lcaA
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private LateralTCPCacheManager( ITCPLateralCacheAttributes lcaA, ICompositeCacheManager cacheMgr,
+                                    ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        this.lateralCacheAttributes = lcaA;
+        this.cacheMgr = cacheMgr;
+        this.cacheEventLogger = cacheEventLogger;
+        this.elementSerializer = elementSerializer;
+
+        this.lateralWatch = new LateralCacheWatchRepairable();
+        this.lateralWatch.setCacheWatch( new ZombieLateralCacheWatch() );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Creating lateral cache service, lca = " + this.lateralCacheAttributes );
+        }
+
+        // Create the service
+        try
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Creating TCP service, lca = " + this.lateralCacheAttributes );
+            }
+
+            this.lateralService = new LateralTCPService<Serializable, Serializable>( this.lateralCacheAttributes );
+        }
+        catch ( Exception ex )
+        {
+            // Failed to connect to the lateral server.
+            // Configure this LateralCacheManager instance to use the
+            // "zombie" services.
+            log.error( "Failure, lateral instance will use zombie service", ex );
+
+            this.lateralService = new ZombieCacheServiceNonLocal<Serializable, Serializable>( lateralCacheAttributes.getZombieQueueMaxSize() );
+
+            // Notify the cache monitor about the error, and kick off
+            // the recovery process.
+            createMonitor( this );
+            monitor.notifyError();
+        }
+    }
+
+    /**
+     * Adds the lateral cache listener to the underlying cache-watch service.
+     * <p>
+     * @param cacheName The feature to be added to the LateralCacheListener attribute
+     * @param listener The feature to be added to the LateralCacheListener attribute
+     * @throws IOException
+     */
+    @Override
+    public <K, V> void addLateralCacheListener( String cacheName, ILateralCacheListener<K, V> listener )
+        throws IOException
+    {
+        synchronized ( this.caches )
+        {
+            lateralWatch.addCacheListener( cacheName, listener );
+        }
+    }
+
+    /**
+     * Called to access a precreated region or construct one with defaults. Since all aux cache
+     * access goes through the manager, this will never be called.
+     * <p>
+     * After getting the manager instance for a server, the factory gets a cache for the region name
+     * it is constructing.
+     * <p>
+     * There should be one manager per server and one cache per region per manager.
+     * <p>
+     * @return AuxiliaryCache
+     * @param cacheName
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <K, V> LateralCacheNoWait<K, V> getCache( String cacheName )
+    {
+        LateralCacheNoWait<K, V> lateralNoWait = null;
+        synchronized ( caches )
+        {
+            // Need to cast because of common map for all caches
+            lateralNoWait = (LateralCacheNoWait<K, V>) caches.get( cacheName );
+            if ( lateralNoWait == null )
+            {
+                LateralCacheAttributes attr = (LateralCacheAttributes) lateralCacheAttributes.copy();
+                attr.setCacheName( cacheName );
+
+                // Need to cast to specific type
+                LateralCache<K, V> cache = new LateralCache<K, V>( attr,
+                        (ICacheServiceNonLocal<K, V>)this.lateralService, monitor );
+                cache.setCacheEventLogger( cacheEventLogger );
+                cache.setElementSerializer( elementSerializer );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Created cache for noWait, cache [" + cache + "]" );
+                }
+
+                lateralNoWait = new LateralCacheNoWait<K, V>( cache );
+                lateralNoWait.setCacheEventLogger( cacheEventLogger );
+                lateralNoWait.setElementSerializer( elementSerializer );
+
+                caches.put( cacheName, lateralNoWait );
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Created LateralCacheNoWait for [" + lateralCacheAttributes + "] LateralCacheNoWait = [" + lateralNoWait
+                        + "]" );
+                }
+
+                // this used to be called every getCache. i move it in here.
+                addListenerIfNeeded( cacheName );
+            }
+        }
+
+        return lateralNoWait;
+    }
+
+    /**
+     * TODO see if this belongs here or in the factory.
+     * <p>
+     * @param cacheName
+     */
+    private void addListenerIfNeeded( String cacheName )
+    {
+        // don't create a listener if we are not receiving.
+        if ( lateralCacheAttributes.isReceive() )
+        {
+            try
+            {
+                addLateralCacheListener( cacheName, LateralTCPListener.getInstance( lateralCacheAttributes, cacheMgr ) );
+            }
+            catch ( IOException ioe )
+            {
+                log.error( "Problem creating lateral listener", ioe );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem creating lateral listener", e );
+            }
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Not creating a listener since we are not receiving." );
+            }
+        }
+    }
+
+    /**
+     * @return Map
+     */
+    @Override
+    public Map<String, ? extends ILateralCacheManager> getInstances()
+    {
+        return instances;
+    }
+
+    /**
+     * @return a new service
+     * @throws IOException
+     */
+    @Override
+    public Object fixService()
+        throws IOException
+    {
+        Object service = null;
+        try
+        {
+            service = new LateralTCPService<Serializable, Serializable>( lateralCacheAttributes );
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Can't fix " + ex.getMessage() );
+            throw new IOException( "Can't fix " + ex.getMessage() );
+        }
+        return service;
+    }
+
+    /**
+     * Shuts down the lateral sender. This does not shutdown the listener. This can be called if the
+     * end point is taken out of service.
+     */
+    @Override
+    public void shutdown()
+    {
+        // TODO revisit this.
+        // the name here doesn't matter.
+        try
+        {
+            lateralService.dispose( "ALL" );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem disposing of service", e );
+        }
+
+        // shut down monitor
+        if (monitor != null)
+        {
+            monitor.notifyShutdown();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListener.java
new file mode 100644
index 0000000..c8ea363
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListener.java
@@ -0,0 +1,356 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWait;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWaitFacade;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.discovery.DiscoveredService;
+import org.apache.commons.jcs.utils.discovery.behavior.IDiscoveryListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This knows how to add and remove discovered services. It observes UDP discovery events.
+ * <p>
+ * We can have one listener per region, or one shared by all regions.
+ */
+public class LateralTCPDiscoveryListener
+    implements IDiscoveryListener
+{
+    /** The log factory */
+    private static final Log log = LogFactory.getLog( LateralTCPDiscoveryListener.class );
+
+    /**
+     * Map of no wait facades. these are used to determine which regions are locally configured to
+     * use laterals.
+     */
+    private final Map<String, LateralCacheNoWaitFacade<?, ?>> facades =
+        Collections.synchronizedMap( new HashMap<String, LateralCacheNoWaitFacade<?, ?>>() );
+
+    /**
+     * List of regions that are configured differently here than on another server. We keep track of
+     * this to limit the amount of info logging.
+     */
+    private final Set<String> knownDifferentlyConfiguredRegions =
+        Collections.synchronizedSet( new HashSet<String>() );
+
+    /** The cache manager. */
+    private final ICompositeCacheManager cacheMgr;
+
+    /** The event logger. */
+    private final ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /**
+     * This plugs into the udp discovery system. It will receive add and remove events.
+     * <p>
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    protected LateralTCPDiscoveryListener( ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger,
+                                           IElementSerializer elementSerializer )
+    {
+        this.cacheMgr = cacheMgr;
+        this.cacheEventLogger = cacheEventLogger;
+        this.elementSerializer = elementSerializer;
+    }
+
+    /**
+     * Adds a nowait facade under this cachename. If one already existed, it will be overridden.
+     * <p>
+     * This adds nowaits to a facade for the region name. If the region has no facade, then it is
+     * not configured to use the lateral cache, and no facade will be created.
+     * <p>
+     * @param cacheName - the region name
+     * @param facade - facade (for region) => multiple lateral clients.
+     * @return true if the facade was not already registered.
+     */
+    public synchronized boolean addNoWaitFacade( String cacheName, LateralCacheNoWaitFacade<?, ?> facade )
+    {
+        boolean isNew = !containsNoWaitFacade( cacheName );
+
+        // override or put anew, it doesn't matter
+        facades.put( cacheName, facade );
+        knownDifferentlyConfiguredRegions.remove( cacheName );
+
+        return isNew;
+    }
+
+    /**
+     * Allows us to see if the facade is present.
+     * <p>
+     * @param cacheName - facades are for a region
+     * @return do we contain the no wait. true if so
+     */
+    public boolean containsNoWaitFacade( String cacheName )
+    {
+        return facades.containsKey( cacheName );
+    }
+
+    /**
+     * Allows us to see if the facade is present and if it has the no wait.
+     * <p>
+     * @param cacheName - facades are for a region
+     * @param noWait - is this no wait in the facade
+     * @return do we contain the no wait. true if so
+     */
+    public <K, V> boolean containsNoWait( String cacheName, LateralCacheNoWait<K, V> noWait )
+    {
+        @SuppressWarnings("unchecked") // Need to cast because of common map for all facades
+        LateralCacheNoWaitFacade<K, V> facade = (LateralCacheNoWaitFacade<K, V>)facades.get( noWait.getCacheName() );
+        if ( facade == null )
+        {
+            return false;
+        }
+
+        return facade.containsNoWait( noWait );
+    }
+
+    /**
+     * When a broadcast is received from the UDP Discovery receiver, for each cacheName in the
+     * message, the add no wait will be called here. To add a no wait, the facade is looked up for
+     * this cache name.
+     * <p>
+     * Each region has a facade. The facade contains a list of end points--the other tcp lateral
+     * services.
+     * <p>
+     * @param noWait
+     * @return true if we found the no wait and added it. False if the no wait was not present or it
+     *         we already had it.
+     */
+    protected <K, V> boolean addNoWait( LateralCacheNoWait<K, V> noWait )
+    {
+        @SuppressWarnings("unchecked") // Need to cast because of common map for all facades
+        LateralCacheNoWaitFacade<K, V> facade = (LateralCacheNoWaitFacade<K, V>)facades.get( noWait.getCacheName() );
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "addNoWait > Got facade for " + noWait.getCacheName() + " = " + facade );
+        }
+
+        if ( facade != null )
+        {
+            boolean isNew = facade.addNoWait( noWait );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Called addNoWait, isNew = " + isNew );
+            }
+            return isNew;
+        }
+        else
+        {
+            if ( !knownDifferentlyConfiguredRegions.contains( noWait.getCacheName() ) )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "addNoWait > Different nodes are configured differently or region ["
+                        + noWait.getCacheName() + "] is not yet used on this side.  " );
+                }
+                knownDifferentlyConfiguredRegions.add( noWait.getCacheName() );
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Look up the facade for the name. If it doesn't exist, then the region is not configured for
+     * use with the lateral cache. If it is present, remove the item from the no wait list.
+     * <p>
+     * @param noWait
+     * @return true if we found the no wait and removed it. False if the no wait was not present.
+     */
+    protected <K, V> boolean removeNoWait( LateralCacheNoWait<K, V> noWait )
+    {
+        @SuppressWarnings("unchecked") // Need to cast because of common map for all facades
+        LateralCacheNoWaitFacade<K, V> facade = (LateralCacheNoWaitFacade<K, V>)facades.get( noWait.getCacheName() );
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removeNoWait > Got facade for " + noWait.getCacheName() + " = " + facade );
+        }
+
+        if ( facade != null )
+        {
+            boolean removed = facade.removeNoWait( noWait );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Called removeNoWait, removed " + removed );
+            }
+            return removed;
+        }
+        else
+        {
+            if ( !knownDifferentlyConfiguredRegions.contains( noWait.getCacheName() ) )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "removeNoWait > Different nodes are configured differently or region ["
+                        + noWait.getCacheName() + "] is not yet used on this side.  " );
+                }
+                knownDifferentlyConfiguredRegions.add( noWait.getCacheName() );
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Creates the lateral cache if needed.
+     * <p>
+     * We could go to the composite cache manager and get the the cache for the region. This would
+     * force a full configuration of the region. One advantage of this would be that the creation of
+     * the later would go through the factory, which would add the item to the no wait list. But we
+     * don't want to do this. This would force this client to have all the regions as the other.
+     * This might not be desired. We don't want to send or receive for a region here that is either
+     * not used or not configured to use the lateral.
+     * <p>
+     * Right now, I'm afraid that the region will get puts if another instance has the region
+     * configured to use the lateral and our address is configured. This might be a bug, but it
+     * shouldn't happen with discovery.
+     * <p>
+     * @param service
+     */
+    @Override
+    public void addDiscoveredService( DiscoveredService service )
+    {
+        // get a cache and add it to the no waits
+        // the add method should not add the same.
+        // we need the listener port from the original config.
+        LateralTCPCacheManager lcm = findManagerForServiceEndPoint( service );
+
+        ArrayList<String> regions = service.getCacheNames();
+        if ( regions != null )
+        {
+            // for each region get the cache
+            for (String cacheName : regions)
+            {
+                try
+                {
+                    ICache<?, ?> ic = lcm.getCache( cacheName );
+
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Got cache, ic = " + ic );
+                    }
+
+                    // add this to the nowaits for this cachename
+                    if ( ic != null )
+                    {
+                        addNoWait( (LateralCacheNoWait<?, ?>) ic );
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Called addNoWait for cacheName [" + cacheName + "]" );
+                        }
+                    }
+                }
+                catch ( Exception e )
+                {
+                    log.error( "Problem creating no wait", e );
+                }
+            }
+        }
+        else
+        {
+            log.warn( "No cache names found in message " + service );
+        }
+    }
+
+    /**
+     * Removes the lateral cache.
+     * <p>
+     * We need to tell the manager that this instance is bad, so it will reconnect the sender if it
+     * comes back.
+     * <p>
+     * @param service
+     */
+    @Override
+    public void removeDiscoveredService( DiscoveredService service )
+    {
+        // get a cache and add it to the no waits
+        // the add method should not add the same.
+        // we need the listener port from the original config.
+        LateralTCPCacheManager lcm = findManagerForServiceEndPoint( service );
+
+        ArrayList<String> regions = service.getCacheNames();
+        if ( regions != null )
+        {
+            // for each region get the cache
+            for (String cacheName : regions)
+            {
+                try
+                {
+                    ICache<?, ?> ic = lcm.getCache( cacheName );
+
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Got cache, ic = " + ic );
+                    }
+
+                    // remove this to the nowaits for this cachename
+                    if ( ic != null )
+                    {
+                        removeNoWait( (LateralCacheNoWait<?, ?>) ic );
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Called removeNoWait for cacheName [" + cacheName + "]" );
+                        }
+                    }
+                }
+                catch ( Exception e )
+                {
+                    log.error( "Problem removing no wait", e );
+                }
+            }
+        }
+        else
+        {
+            log.warn( "No cache names found in message " + service );
+        }
+    }
+
+    /**
+     * Gets the appropriate manager.
+     * <p>
+     * @param service
+     * @return LateralTCPCacheManager configured for that end point.
+     */
+    private LateralTCPCacheManager findManagerForServiceEndPoint( DiscoveredService service )
+    {
+        ITCPLateralCacheAttributes lca = new TCPLateralCacheAttributes();
+        lca.setTransmissionType( LateralCacheAttributes.Type.TCP );
+        lca.setTcpServer( service.getServiceAddress() + ":" + service.getServicePort() );
+        LateralTCPCacheManager lcm = LateralTCPCacheManager.getInstance( lca, cacheMgr, cacheEventLogger,
+                                                                         elementSerializer );
+        return lcm;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerManager.java
new file mode 100644
index 0000000..553156b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerManager.java
@@ -0,0 +1,90 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The factory holds an instance of this manager. This manager has a map of listeners, keyed to the
+ * discovery configuration. I'm not using a static map, because I'm trying to make JCS
+ * multi-instance.
+ * <p>
+ * During configuration, the factory is only created once per auxiliary definition. Two different
+ * laterals cannot use the same discovery service. We will likely want to change this.
+ */
+public class LateralTCPDiscoveryListenerManager
+{
+    /** Map of available instances, keyed by port. Note, this is not static. */
+    private final Map<String, LateralTCPDiscoveryListener> instances =
+        Collections.synchronizedMap( new HashMap<String, LateralTCPDiscoveryListener>() );
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralTCPDiscoveryListenerManager.class );
+
+    /** Does nothing. */
+    public LateralTCPDiscoveryListenerManager()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Creating new LateralTCPDiscoveryListenerManager" );
+        }
+    }
+
+    /**
+     * Gets the instance attribute of the LateralCacheTCPListener class.
+     * <p>
+     * @param ilca ITCPLateralCacheAttributes
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public synchronized LateralTCPDiscoveryListener getDiscoveryListener( ITCPLateralCacheAttributes ilca,
+                                                                          ICompositeCacheManager cacheMgr,
+                                                                          ICacheEventLogger cacheEventLogger,
+                                                                          IElementSerializer elementSerializer )
+    {
+        String key = ilca.getUdpDiscoveryAddr() + ":" + ilca.getUdpDiscoveryPort();
+        LateralTCPDiscoveryListener ins = instances.get( key );
+
+        if ( ins == null )
+        {
+            ins = new LateralTCPDiscoveryListener( cacheMgr, cacheEventLogger, elementSerializer );
+
+            instances.put( key, ins );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Created new discovery listener for " + key + " cacheName for request " + ilca.getCacheName() );
+            }
+        }
+
+        return ins;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPListener.java
new file mode 100644
index 0000000..aceaa1c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPListener.java
@@ -0,0 +1,774 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.lateral.LateralElementDescriptor;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheListener;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Listens for connections from other TCP lateral caches and handles them. The initialization method
+ * starts a listening thread, which creates a socket server. When messages are received they are
+ * passed to a pooled executor which then calls the appropriate handle method.
+ */
+public class LateralTCPListener<K, V>
+    implements ILateralCacheListener<K, V>, IShutdownObserver
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralTCPListener.class );
+
+    /** How long the server will block on an accept(). 0 is infinite. */
+    private static final int acceptTimeOut = 1000;
+
+    /** The CacheHub this listener is associated with */
+    private transient ICompositeCacheManager cacheManager;
+
+    /** Map of available instances, keyed by port */
+    private static final HashMap<String, ILateralCacheListener<?, ?>> instances =
+        new HashMap<String, ILateralCacheListener<?, ?>>();
+
+    /** The socket listener */
+    private ListenerThread receiver;
+
+    /** Configuration attributes */
+    private ITCPLateralCacheAttributes tcpLateralCacheAttributes;
+
+    /** Listening port */
+    private int port;
+
+    /** The processor. We should probably use an event queue here. */
+    private ExecutorService pooledExecutor;
+
+    /** put count */
+    private int putCnt = 0;
+
+    /** remove count */
+    private int removeCnt = 0;
+
+    /** get count */
+    private int getCnt = 0;
+
+    /**
+     * Use the vmid by default. This can be set for testing. If we ever need to run more than one
+     * per vm, then we need a new technique.
+     */
+    private long listenerId = CacheInfo.listenerId;
+
+    /** is this shut down? */
+    private boolean shutdown = false;
+
+    /** is this terminated? */
+    private boolean terminated = false;
+
+    /**
+     * Gets the instance attribute of the LateralCacheTCPListener class.
+     * <p>
+     * @param ilca ITCPLateralCacheAttributes
+     * @param cacheMgr
+     * @return The instance value
+     */
+    public synchronized static <K, V> LateralTCPListener<K, V>
+        getInstance( ITCPLateralCacheAttributes ilca, ICompositeCacheManager cacheMgr )
+    {
+        @SuppressWarnings("unchecked") // Need to cast because of common map for all instances
+        LateralTCPListener<K, V> ins = (LateralTCPListener<K, V>) instances.get( String.valueOf( ilca.getTcpListenerPort() ) );
+
+        if ( ins == null )
+        {
+            ins = new LateralTCPListener<K, V>( ilca );
+
+            ins.init();
+            ins.setCacheManager( cacheMgr );
+
+            instances.put( String.valueOf( ilca.getTcpListenerPort() ), ins );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Created new listener " + ilca.getTcpListenerPort() );
+            }
+        }
+
+        return ins;
+    }
+
+    /**
+     * Only need one since it does work for all regions, just reference by multiple region names.
+     * <p>
+     * @param ilca
+     */
+    protected LateralTCPListener( ITCPLateralCacheAttributes ilca )
+    {
+        this.setTcpLateralCacheAttributes( ilca );
+    }
+
+    /**
+     * This starts the ListenerThread on the specified port.
+     */
+    @Override
+    public synchronized void init()
+    {
+        try
+        {
+            this.port = getTcpLateralCacheAttributes().getTcpListenerPort();
+
+            pooledExecutor = Executors.newCachedThreadPool(
+                    new DaemonThreadFactory("JCS-LateralTCPListener-"));
+            terminated = false;
+            shutdown = false;
+
+            log.info( "Listening on port " + port );
+
+            ServerSocket serverSocket = new ServerSocket( port );
+            serverSocket.setSoTimeout( acceptTimeOut );
+
+            receiver = new ListenerThread(serverSocket);
+            receiver.setDaemon( true );
+            receiver.start();
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+            throw new IllegalStateException( ex.getMessage() );
+        }
+    }
+
+    /**
+     * Let the lateral cache set a listener_id. Since there is only one listener for all the
+     * regions and every region gets registered? the id shouldn't be set if it isn't zero. If it is
+     * we assume that it is a reconnect.
+     * <p>
+     * By default, the listener id is the vmid.
+     * <p>
+     * The service should set this value. This value will never be changed by a server we connect
+     * to. It needs to be non static, for unit tests.
+     * <p>
+     * The service will use the value it sets in all send requests to the sender.
+     * <p>
+     * @param id The new listenerId value
+     * @throws IOException
+     */
+    @Override
+    public void setListenerId( long id )
+        throws IOException
+    {
+        this.listenerId = id;
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "set listenerId = " + id );
+        }
+    }
+
+    /**
+     * Gets the listenerId attribute of the LateralCacheTCPListener object
+     * <p>
+     * @return The listenerId value
+     * @throws IOException
+     */
+    @Override
+    public long getListenerId()
+        throws IOException
+    {
+        return this.listenerId;
+    }
+
+    /**
+     * Increments the put count. Gets the cache that was injected by the lateral factory. Calls put
+     * on the cache.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheListener#handlePut(org.apache.commons.jcs.engine.behavior.ICacheElement)
+     */
+    @Override
+    public void handlePut( ICacheElement<K, V> element )
+        throws IOException
+    {
+        putCnt++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( getPutCnt() % 100 == 0 )
+            {
+                log.info( "Put Count (port " + getTcpLateralCacheAttributes().getTcpListenerPort() + ") = "
+                    + getPutCnt() );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handlePut> cacheName=" + element.getCacheName() + ", key=" + element.getKey() );
+        }
+
+        getCache( element.getCacheName() ).localUpdate( element );
+    }
+
+    /**
+     * Increments the remove count. Gets the cache that was injected by the lateral factory. Calls
+     * remove on the cache.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheListener#handleRemove(java.lang.String,
+     *      Object)
+     */
+    @Override
+    public void handleRemove( String cacheName, K key )
+        throws IOException
+    {
+        removeCnt++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( getRemoveCnt() % 100 == 0 )
+            {
+                log.info( "Remove Count = " + getRemoveCnt() );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleRemove> cacheName=" + cacheName + ", key=" + key );
+        }
+
+        getCache( cacheName ).localRemove( key );
+    }
+
+    /**
+     * Gets the cache that was injected by the lateral factory. Calls removeAll on the cache.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheListener#handleRemoveAll(java.lang.String)
+     */
+    @Override
+    public void handleRemoveAll( String cacheName )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleRemoveAll> cacheName=" + cacheName );
+        }
+
+        getCache( cacheName ).localRemoveAll();
+    }
+
+    /**
+     * Gets the cache that was injected by the lateral factory. Calls get on the cache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @return a ICacheElement
+     * @throws IOException
+     */
+    public ICacheElement<K, V> handleGet( String cacheName, K key )
+        throws IOException
+    {
+        getCnt++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( getGetCnt() % 100 == 0 )
+            {
+                log.info( "Get Count (port " + getTcpLateralCacheAttributes().getTcpListenerPort() + ") = "
+                    + getGetCnt() );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleGet> cacheName=" + cacheName + ", key = " + key );
+        }
+
+        return getCache( cacheName ).localGet( key );
+    }
+
+    /**
+     * Gets the cache that was injected by the lateral factory. Calls get on the cache.
+     * <p>
+     * @param cacheName the name of the cache
+     * @param pattern the matching pattern
+     * @return Map
+     * @throws IOException
+     */
+    public Map<K, ICacheElement<K, V>> handleGetMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        getCnt++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( getGetCnt() % 100 == 0 )
+            {
+                log.info( "GetMatching Count (port " + getTcpLateralCacheAttributes().getTcpListenerPort() + ") = "
+                    + getGetCnt() );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleGetMatching> cacheName=" + cacheName + ", pattern = " + pattern );
+        }
+
+        return getCache( cacheName ).localGetMatching( pattern );
+    }
+
+    /**
+     * Gets the cache that was injected by the lateral factory. Calls getKeySet on the cache.
+     * <p>
+     * @param cacheName the name of the cache
+     * @return a set of keys
+     * @throws IOException
+     */
+    public Set<K> handleGetKeySet( String cacheName ) throws IOException
+    {
+    	return getCache( cacheName ).getKeySet(true);
+    }
+
+    /**
+     * This marks this instance as terminated.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheListener#handleDispose(java.lang.String)
+     */
+    @Override
+    public void handleDispose( String cacheName )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "handleDispose > cacheName=" + cacheName + " | Ignoring message.  Do not dispose from remote." );
+        }
+
+        // TODO handle active deregistration, rather than passive detection
+        synchronized (this)
+        {
+            terminated = true;
+        }
+    }
+
+    @Override
+    public synchronized void dispose()
+    {
+        terminated = true;
+        notify();
+
+        pooledExecutor.shutdownNow();
+    }
+
+    /**
+     * Gets the cacheManager attribute of the LateralCacheTCPListener object.
+     * <p>
+     * Normally this is set by the factory. If it wasn't set the listener defaults to the expected
+     * singleton behavior of the cache manager.
+     * <p>
+     * @param name
+     * @return CompositeCache
+     */
+    protected CompositeCache<K, V> getCache( String name )
+    {
+        if ( getCacheManager() == null )
+        {
+            // revert to singleton on failure
+            try
+            {
+                setCacheManager( CompositeCacheManager.getInstance() );
+            }
+            catch (CacheException e)
+            {
+                throw new RuntimeException("Could not retrieve cache manager instance", e);
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "cacheMgr = " + getCacheManager() );
+            }
+        }
+
+        return getCacheManager().getCache( name );
+    }
+
+    /**
+     * This is roughly the number of updates the lateral has received.
+     * <p>
+     * @return Returns the putCnt.
+     */
+    public int getPutCnt()
+    {
+        return putCnt;
+    }
+
+    /**
+     * @return Returns the getCnt.
+     */
+    public int getGetCnt()
+    {
+        return getCnt;
+    }
+
+    /**
+     * @return Returns the removeCnt.
+     */
+    public int getRemoveCnt()
+    {
+        return removeCnt;
+    }
+
+    /**
+     * @param cacheMgr The cacheMgr to set.
+     */
+    @Override
+    public void setCacheManager( ICompositeCacheManager cacheMgr )
+    {
+        this.cacheManager = cacheMgr;
+    }
+
+    /**
+     * @return Returns the cacheMgr.
+     */
+    @Override
+    public ICompositeCacheManager getCacheManager()
+    {
+        return cacheManager;
+    }
+
+    /**
+     * @param tcpLateralCacheAttributes The tcpLateralCacheAttributes to set.
+     */
+    public void setTcpLateralCacheAttributes( ITCPLateralCacheAttributes tcpLateralCacheAttributes )
+    {
+        this.tcpLateralCacheAttributes = tcpLateralCacheAttributes;
+    }
+
+    /**
+     * @return Returns the tcpLateralCacheAttributes.
+     */
+    public ITCPLateralCacheAttributes getTcpLateralCacheAttributes()
+    {
+        return tcpLateralCacheAttributes;
+    }
+
+    /**
+     * Processes commands from the server socket. There should be one listener for each configured
+     * TCP lateral.
+     */
+    public class ListenerThread
+        extends Thread
+    {
+        /** The socket listener */
+        private final ServerSocket serverSocket;
+
+        /**
+         * Constructor
+         *
+         * @param serverSocket
+         */
+        public ListenerThread(ServerSocket serverSocket)
+        {
+            super();
+            this.serverSocket = serverSocket;
+        }
+
+        /** Main processing method for the ListenerThread object */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            try
+            {
+                ConnectionHandler handler;
+
+                outer: while ( true )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Waiting for clients to connect " );
+                    }
+
+                    Socket socket = null;
+                    inner: while (true)
+                    {
+                        // Check to see if we've been asked to exit, and exit
+                        synchronized (LateralTCPListener.this)
+                        {
+                            if (terminated)
+                            {
+                                if (log.isDebugEnabled())
+                                {
+                                    log.debug("Thread terminated, exiting gracefully");
+                                }
+                                break outer;
+                            }
+                        }
+                        try
+                        {
+                            socket = serverSocket.accept();
+                            break inner;
+                        }
+                        catch (SocketTimeoutException e)
+                        {
+                            // No problem! We loop back up!
+                            continue inner;
+                        }
+                    }
+
+                    if ( socket != null && log.isDebugEnabled() )
+                    {
+                        InetAddress inetAddress = socket.getInetAddress();
+                        log.debug( "Connected to client at " + inetAddress );
+                    }
+
+                    handler = new ConnectionHandler( socket );
+                    pooledExecutor.execute( handler );
+                }
+            }
+            catch ( IOException e )
+            {
+                log.error( "Exception caught in TCP listener", e );
+            }
+            finally
+            {
+            	if (serverSocket != null)
+            	{
+            		try
+            		{
+						serverSocket.close();
+					}
+            		catch (IOException e)
+            		{
+                        log.error( "Exception caught closing socket", e );
+					}
+            	}
+            }
+        }
+    }
+
+    /**
+     * A Separate thread that runs when a command comes into the LateralTCPReceiver.
+     */
+    public class ConnectionHandler
+        implements Runnable
+    {
+        /** The socket connection, passed in via constructor */
+        private final Socket socket;
+
+        /**
+         * Construct for a given socket
+         * @param socket
+         */
+        public ConnectionHandler( Socket socket )
+        {
+            this.socket = socket;
+        }
+
+        /**
+         * Main processing method for the LateralTCPReceiverConnection object
+         */
+        @Override
+        @SuppressWarnings({"unchecked", // Need to cast from Object
+            "synthetic-access" })
+        public void run()
+        {
+            ObjectInputStream ois;
+
+            try
+            {
+                ois = new ObjectInputStreamClassLoaderAware( socket.getInputStream(), null );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Could not open ObjectInputStream on " + socket, e );
+
+                return;
+            }
+
+            LateralElementDescriptor<K, V> led;
+
+            try
+            {
+                while ( true )
+                {
+                    led = (LateralElementDescriptor<K, V>) ois.readObject();
+
+                    if ( led == null )
+                    {
+                        log.debug( "LateralElementDescriptor is null" );
+                        continue;
+                    }
+                    if ( led.requesterId == getListenerId() )
+                    {
+                        log.debug( "from self" );
+                    }
+                    else
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "receiving LateralElementDescriptor from another" + "led = " + led
+                                + ", led.command = " + led.command + ", led.ce = " + led.ce );
+                        }
+
+                        handle( led );
+                    }
+                }
+            }
+            catch ( java.io.EOFException e )
+            {
+                log.info( "Caught java.io.EOFException closing connection." + e.getMessage() );
+            }
+            catch ( java.net.SocketException e )
+            {
+                log.info( "Caught java.net.SocketException closing connection." + e.getMessage() );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Unexpected exception.", e );
+            }
+
+            try
+            {
+                ois.close();
+            }
+            catch ( IOException e )
+            {
+                log.error( "Could not close object input stream.", e );
+            }
+        }
+
+        /**
+         * This calls the appropriate method, based on the command sent in the Lateral element
+         * descriptor.
+         * <p>
+         * @param led
+         * @throws IOException
+         */
+        @SuppressWarnings("synthetic-access")
+        private void handle( LateralElementDescriptor<K, V> led )
+            throws IOException
+        {
+            String cacheName = led.ce.getCacheName();
+            K key = led.ce.getKey();
+            Serializable obj = null;
+
+            switch (led.command)
+            {
+                case UPDATE:
+                    handlePut( led.ce );
+                    break;
+
+                case REMOVE:
+                    // if a hashcode was given and filtering is on
+                    // check to see if they are the same
+                    // if so, then don't remove, otherwise issue a remove
+                    if ( led.valHashCode != -1 )
+                    {
+                        if ( getTcpLateralCacheAttributes().isFilterRemoveByHashCode() )
+                        {
+                            ICacheElement<K, V> test = getCache( cacheName ).localGet( key );
+                            if ( test != null )
+                            {
+                                if ( test.getVal().hashCode() == led.valHashCode )
+                                {
+                                    if ( log.isDebugEnabled() )
+                                    {
+                                        log.debug( "Filtering detected identical hashCode [" + led.valHashCode
+                                            + "], not issuing a remove for led " + led );
+                                    }
+                                    return;
+                                }
+                                else
+                                {
+                                    if ( log.isDebugEnabled() )
+                                    {
+                                        log.debug( "Different hashcodes, in cache [" + test.getVal().hashCode()
+                                            + "] sent [" + led.valHashCode + "]" );
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    handleRemove( cacheName, key );
+                    break;
+
+                case REMOVEALL:
+                    handleRemoveAll( cacheName );
+                    break;
+
+                case GET:
+                    obj = handleGet( cacheName, key );
+                    break;
+
+                case GET_MATCHING:
+                    obj = (Serializable) handleGetMatching( cacheName, (String) key );
+                    break;
+
+                case GET_KEYSET:
+                	obj = (Serializable) handleGetKeySet(cacheName);
+                    break;
+
+                default: break;
+            }
+
+            if (obj != null)
+            {
+                ObjectOutputStream oos = new ObjectOutputStream( socket.getOutputStream() );
+                oos.writeObject( obj );
+                oos.flush();
+            }
+        }
+    }
+
+    /**
+     * Shuts down the receiver.
+     */
+    @Override
+    public void shutdown()
+    {
+        if ( !shutdown )
+        {
+            shutdown = true;
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Shutting down TCP Lateral receiver." );
+            }
+            receiver.interrupt();
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Shutdown already called." );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPSender.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPSender.java
new file mode 100644
index 0000000..674bde4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPSender.java
@@ -0,0 +1,363 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.LateralElementDescriptor;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * This class is based on the log4j SocketAppender class. I'm using a different repair structure, so
+ * it is significantly different.
+ */
+public class LateralTCPSender
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LateralTCPSender.class );
+
+    /** Config */
+    private ITCPLateralCacheAttributes tcpLateralCacheAttributes;
+
+    /** The hostname of the remote server. */
+    private String remoteHost;
+
+    /** The address of the server */
+    private InetAddress address;
+
+    /** The port the server is listening to. */
+    private int port = 1111;
+
+    /** The stream from the server connection. */
+    private ObjectOutputStream oos;
+
+    /** The socket connection with the server. */
+    private Socket socket;
+
+    /** how many messages sent */
+    private int sendCnt = 0;
+
+    /** Use to synchronize multiple threads that may be trying to get. */
+    private final Object getLock = new int[0];
+
+    /**
+     * Constructor for the LateralTCPSender object.
+     * <p>
+     * @param lca
+     * @throws IOException
+     */
+    public LateralTCPSender( ITCPLateralCacheAttributes lca )
+        throws IOException
+    {
+        this.setTcpLateralCacheAttributes( lca );
+
+        String p1 = lca.getTcpServer();
+        if ( p1 != null )
+        {
+            String h2 = p1.substring( 0, p1.indexOf( ":" ) );
+            int po = Integer.parseInt( p1.substring( p1.indexOf( ":" ) + 1 ) );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "h2 = " + h2 );
+                log.debug( "po = " + po );
+            }
+
+            if ( h2.length() == 0 )
+            {
+                throw new IOException( "Cannot connect to invalid address [" + h2 + ":" + po + "]" );
+            }
+
+            init( h2, po );
+        }
+    }
+
+    /**
+     * Creates a connection to a TCP server.
+     * <p>
+     * @param host
+     * @param port
+     * @throws IOException
+     */
+    protected void init( String host, int port )
+        throws IOException
+    {
+        this.port = port;
+        this.address = getAddressByName( host );
+        this.setRemoteHost( host );
+
+        try
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Attempting connection to [" + address.getHostName() + "]" );
+            }
+
+            // have time out socket open do this for us
+            try
+            {
+                socket = new Socket();
+                socket.connect( new InetSocketAddress( host, port ), tcpLateralCacheAttributes.getOpenTimeOut() );
+            }
+            catch ( IOException ioe )
+            {
+                if (socket != null)
+                {
+                    socket.close();
+                }
+
+                throw new IOException( "Cannot connect to " + host + ":" + port, ioe );
+            }
+
+            socket.setSoTimeout( tcpLateralCacheAttributes.getSocketTimeOut() );
+            synchronized ( this )
+            {
+                oos = new ObjectOutputStream( socket.getOutputStream() );
+            }
+        }
+        catch ( java.net.ConnectException e )
+        {
+            log.debug( "Remote host [" + address.getHostName() + "] refused connection." );
+            throw e;
+        }
+        catch ( IOException e )
+        {
+            log.debug( "Could not connect to [" + address.getHostName() + "]. Exception is " + e );
+            throw e;
+        }
+    }
+
+    /**
+     * Gets the addressByName attribute of the LateralTCPSender object.
+     * <p>
+     * @param host
+     * @return The addressByName value
+     * @throws IOException
+     */
+    private InetAddress getAddressByName( String host )
+        throws IOException
+    {
+        try
+        {
+            return InetAddress.getByName( host );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Could not find address of [" + host + "] ", e );
+            throw new IOException( "Could not find address of [" + host + "] " + e.getMessage() );
+        }
+    }
+
+    /**
+     * Sends commands to the lateral cache listener.
+     * <p>
+     * @param led
+     * @throws IOException
+     */
+    public <K, V> void send( LateralElementDescriptor<K, V> led )
+        throws IOException
+    {
+        sendCnt++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( sendCnt % 100 == 0 )
+            {
+                log.info( "Send Count (port " + port + ") = " + sendCnt );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "sending LateralElementDescriptor" );
+        }
+
+        if ( led == null )
+        {
+            return;
+        }
+
+        if ( address == null )
+        {
+            throw new IOException( "No remote host is set for LateralTCPSender." );
+        }
+
+        if ( oos != null )
+        {
+            synchronized ( this.getLock )
+            {
+                try
+                {
+                    oos.writeUnshared( led );
+                    oos.flush();
+                }
+                catch ( IOException e )
+                {
+                    oos = null;
+                    log.error( "Detected problem with connection: " + e );
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Sends commands to the lateral cache listener and gets a response. I'm afraid that we could
+     * get into a pretty bad blocking situation here. This needs work. I just wanted to get some
+     * form of get working. However, get is not recommended for performance reasons. If you have 10
+     * laterals, then you have to make 10 failed gets to find out none of the caches have the item.
+     * <p>
+     * @param led
+     * @return ICacheElement
+     * @throws IOException
+     */
+    public <K, V> Object sendAndReceive( LateralElementDescriptor<K, V> led )
+        throws IOException
+    {
+        if ( led == null )
+        {
+            return null;
+        }
+
+        if ( address == null )
+        {
+            throw new IOException( "No remote host is set for LateralTCPSender." );
+        }
+
+        Object response = null;
+
+        if ( oos != null )
+        {
+            // Synchronized to insure that the get requests to server from this
+            // sender and the responses are processed in order, else you could
+            // return the wrong item from the cache.
+            // This is a big block of code. May need to re-think this strategy.
+            // This may not be necessary.
+            // Normal puts, etc to laterals do not have to be synchronized.
+            synchronized ( this.getLock )
+            {
+                try
+                {
+                    try
+                    {
+                        // clean up input stream, nothing should be there yet.
+                        if ( socket.getInputStream().available() > 0 )
+                        {
+                            socket.getInputStream().read( new byte[socket.getInputStream().available()] );
+                        }
+                    }
+                    catch ( IOException ioe )
+                    {
+                        log.error( "Problem cleaning socket before send " + socket, ioe );
+                        throw ioe;
+                    }
+
+                    // write object to listener
+                    oos.writeUnshared( led );
+                    oos.flush();
+
+                    try
+                    {
+                        // TODO make configurable
+                        // socket.setSoTimeout( 2000 );
+                        ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( socket.getInputStream(), null );
+                        response = ois.readObject();
+                    }
+                    catch ( IOException ioe )
+                    {
+                        String message = "Could not open ObjectInputStream to " + socket;
+                        message += " SoTimeout [" + socket.getSoTimeout() + "] Connected [" + socket.isConnected() + "]";
+                        log.error( message, ioe );
+                        throw ioe;
+                    }
+                    catch ( Exception e )
+                    {
+                        log.error( e );
+                    }
+                }
+                catch ( IOException e )
+                {
+                    oos = null;
+                    log.error( "Detected problem with connection: " + e );
+                    throw e;
+                }
+            }
+        }
+
+        return response;
+    }
+
+    /**
+     * Closes connection used by all LateralTCPSenders for this lateral connection. Dispose request
+     * should come into the facade and be sent to all lateral cache services. The lateral cache
+     * service will then call this method.
+     * <p>
+     * @param cache
+     * @throws IOException
+     */
+    public void dispose( String cache )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Dispose called for cache [" + cache + "]" );
+        }
+        // WILL CLOSE CONNECTION USED BY ALL
+        oos.close();
+    }
+
+    /**
+     * @param tcpLateralCacheAttributes The tcpLateralCacheAttributes to set.
+     */
+    public void setTcpLateralCacheAttributes( ITCPLateralCacheAttributes tcpLateralCacheAttributes )
+    {
+        this.tcpLateralCacheAttributes = tcpLateralCacheAttributes;
+    }
+
+    /**
+     * @return Returns the tcpLateralCacheAttributes.
+     */
+    public ITCPLateralCacheAttributes getTcpLateralCacheAttributes()
+    {
+        return tcpLateralCacheAttributes;
+    }
+
+    /**
+     * @param remoteHost The remoteHost to set.
+     */
+    public void setRemoteHost( String remoteHost )
+    {
+        this.remoteHost = remoteHost;
+    }
+
+    /**
+     * @return Returns the remoteHost.
+     */
+    public String getRemoteHost()
+    {
+        return remoteHost;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPService.java
new file mode 100644
index 0000000..5799bff
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPService.java
@@ -0,0 +1,547 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.LateralCommand;
+import org.apache.commons.jcs.auxiliary.lateral.LateralElementDescriptor;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheObserver;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A lateral cache service implementation. Does not implement getGroupKey
+ */
+public class LateralTCPService<K, V>
+    implements ICacheServiceNonLocal<K, V>, ILateralCacheObserver
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( LateralTCPService.class );
+
+    /** special configuration */
+    private ITCPLateralCacheAttributes tcpLateralCacheAttributes;
+
+    /** Sends to another lateral. */
+    private LateralTCPSender sender;
+
+    /** use the vmid by default */
+    private long listenerId = CacheInfo.listenerId;
+
+    /**
+     * Constructor for the LateralTCPService object
+     * <p>
+     * @param lca ITCPLateralCacheAttributes
+     * @throws IOException
+     */
+    public LateralTCPService( ITCPLateralCacheAttributes lca )
+        throws IOException
+    {
+        this.setTcpLateralCacheAttributes( lca );
+        try
+        {
+            log.debug( "creating sender, attributes = " + getTcpLateralCacheAttributes() );
+
+            sender = new LateralTCPSender( lca );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.debug( "Created sender to [" + lca.getTcpServer() + "]" );
+            }
+        }
+        catch ( IOException e )
+        {
+            // log.error( "Could not create sender", e );
+            // This gets thrown over and over in recovery mode.
+            // The stack trace isn't useful here.
+            log.error( "Could not create sender to [" + lca.getTcpServer() + "] -- " + e.getMessage() );
+
+            throw e;
+        }
+    }
+
+    /**
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+        throws IOException
+    {
+        update( item, getListenerId() );
+    }
+
+    /**
+     * If put is allowed, we will issue a put. If issue put on remove is configured, we will issue a
+     * remove. Either way, we create a lateral element descriptor, which is essentially a JCS TCP
+     * packet. It describes what operation the receiver should take when it gets the packet.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal#update(org.apache.commons.jcs.engine.behavior.ICacheElement,
+     *      long)
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long requesterId )
+        throws IOException
+    {
+        // if we don't allow put, see if we should remove on put
+        if ( !this.getTcpLateralCacheAttributes().isAllowPut() )
+        {
+            // if we can't remove on put, and we can't put then return
+            if ( !this.getTcpLateralCacheAttributes().isIssueRemoveOnPut() )
+            {
+                return;
+            }
+        }
+
+        // if we shouldn't remove on put, then put
+        if ( !this.getTcpLateralCacheAttributes().isIssueRemoveOnPut() )
+        {
+            LateralElementDescriptor<K, V> led = new LateralElementDescriptor<K, V>( item );
+            led.requesterId = requesterId;
+            led.command = LateralCommand.UPDATE;
+            sender.send( led );
+        }
+        // else issue a remove with the hashcode for remove check on
+        // on the other end, this will be a server config option
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Issuing a remove for a put" );
+            }
+            // set the value to null so we don't send the item
+            CacheElement<K, V> ce = new CacheElement<K, V>( item.getCacheName(), item.getKey(), null );
+            LateralElementDescriptor<K, V> led = new LateralElementDescriptor<K, V>( ce );
+            led.requesterId = requesterId;
+            led.command = LateralCommand.REMOVE;
+            led.valHashCode = item.getVal().hashCode();
+            sender.send( led );
+        }
+    }
+
+    /**
+     * Uses the default listener id and calls the next remove method.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheService#remove(java.lang.String,
+     *      java.io.Serializable)
+     */
+    @Override
+    public void remove( String cacheName, K key )
+        throws IOException
+    {
+        remove( cacheName, key, getListenerId() );
+    }
+
+    /**
+     * Wraps the key in a LateralElementDescriptor.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal#remove(java.lang.String,
+     *      java.io.Serializable, long)
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        CacheElement<K, V> ce = new CacheElement<K, V>( cacheName, key, null );
+        LateralElementDescriptor<K, V> led = new LateralElementDescriptor<K, V>( ce );
+        led.requesterId = requesterId;
+        led.command = LateralCommand.REMOVE;
+        sender.send( led );
+    }
+
+    /**
+     * Does nothing.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void release()
+        throws IOException
+    {
+        // nothing needs to be done
+    }
+
+    /**
+     * Will close the connection.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void dispose( String cacheName )
+        throws IOException
+    {
+        sender.dispose( cacheName );
+    }
+
+    /**
+     * The service does not get via this method, so this return null.
+     * <p>
+     * @param key
+     * @return always null.
+     * @throws IOException
+     */
+    public Serializable get( String key )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "balking at get for key [" + key + "]" );
+        }
+        // p( "junk get" );
+        // return get( cattr.cacheName, key, true );
+        return null;
+        // nothing needs to be done
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     * @return ICacheElement<K, V> if found.
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+        throws IOException
+    {
+        return get( cacheName, key, getListenerId() );
+    }
+
+    /**
+     * If get is allowed, we will issues a get request.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement<K, V> if found.
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        // if get is not allowed return
+        if ( this.getTcpLateralCacheAttributes().isAllowGet() )
+        {
+            CacheElement<K, V> ce = new CacheElement<K, V>( cacheName, key, null );
+            LateralElementDescriptor<K, V> led = new LateralElementDescriptor<K, V>( ce );
+            // led.requesterId = requesterId; // later
+            led.command = LateralCommand.GET;
+            @SuppressWarnings("unchecked") // Need to cast from Object
+            ICacheElement<K, V> response = (ICacheElement<K, V>)sender.sendAndReceive( led );
+            if ( response != null )
+            {
+                return response;
+            }
+            return null;
+        }
+        else
+        {
+            // nothing needs to be done
+            return null;
+        }
+    }
+
+    /**
+     * If allow get is true, we will issue a getmatching query.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, getListenerId() );
+    }
+
+    /**
+     * If allow get is true, we will issue a getmatching query.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId - our identity
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Need to cast from Object
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        // if get is not allowed return
+        if ( this.getTcpLateralCacheAttributes().isAllowGet() )
+        {
+            CacheElement<String, String> ce = new CacheElement<String, String>( cacheName, pattern, null );
+            LateralElementDescriptor<String, String> led = new LateralElementDescriptor<String, String>( ce );
+            // led.requesterId = requesterId; // later
+            led.command = LateralCommand.GET_MATCHING;
+
+            Object response = sender.sendAndReceive( led );
+            if ( response != null )
+            {
+                return (Map<K, ICacheElement<K, V>>) response;
+            }
+            return Collections.emptyMap();
+        }
+        else
+        {
+            // nothing needs to be done
+            return null;
+        }
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+        throws IOException
+    {
+        return getMultiple( cacheName, keys, getListenerId() );
+    }
+
+    /**
+     * This issues a separate get for each item.
+     * <p>
+     * TODO We should change this. It should issue one request.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( cacheName, key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+        return elements;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @param cacheName the name of the cache region
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Need cast from Object
+    public Set<K> getKeySet(String cacheName) throws IOException
+    {
+        CacheElement<String, String> ce = new CacheElement<String, String>(cacheName, null, null);
+        LateralElementDescriptor<String, String> led = new LateralElementDescriptor<String, String>(ce);
+        // led.requesterId = requesterId; // later
+        led.command = LateralCommand.GET_KEYSET;
+        Object response = sender.sendAndReceive(led);
+        if (response != null)
+        {
+            return (Set<K>) response;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName )
+        throws IOException
+    {
+        removeAll( cacheName, getListenerId() );
+    }
+
+    /**
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        CacheElement<String, String> ce = new CacheElement<String, String>( cacheName, "ALL", null );
+        LateralElementDescriptor<String, String> led = new LateralElementDescriptor<String, String>( ce );
+        led.requesterId = requesterId;
+        led.command = LateralCommand.REMOVEALL;
+        sender.send( led );
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        try
+        {
+            LateralTCPSender sender = new LateralTCPSender( new TCPLateralCacheAttributes() );
+
+            // process user input till done
+            boolean notDone = true;
+            String message = null;
+            // wait to dispose
+            BufferedReader br = new BufferedReader( new InputStreamReader( System.in, "UTF-8" ) );
+
+            while ( notDone )
+            {
+                System.out.println( "enter mesage:" );
+                message = br.readLine();
+
+                if (message == null)
+                {
+                    notDone = false;
+                    continue;
+                }
+
+                CacheElement<String, String> ce = new CacheElement<String, String>( "test", "test", message );
+                LateralElementDescriptor<String, String> led = new LateralElementDescriptor<String, String>( ce );
+                sender.send( led );
+            }
+        }
+        catch ( IOException e )
+        {
+            System.out.println( e.toString() );
+        }
+    }
+
+    // ILateralCacheObserver methods, do nothing here since
+    // the connection is not registered, the udp service is
+    // is not registered.
+
+    /**
+     * @param cacheName
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void addCacheListener( String cacheName, ICacheListener<KK, VV> obj )
+        throws IOException
+    {
+        // Empty
+    }
+
+    /**
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void addCacheListener( ICacheListener<KK, VV> obj )
+        throws IOException
+    {
+        // Empty
+    }
+
+    /**
+     * @param cacheName
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void removeCacheListener( String cacheName, ICacheListener<KK, VV> obj )
+        throws IOException
+    {
+        // Empty
+    }
+
+    /**
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void removeCacheListener( ICacheListener<KK, VV> obj )
+        throws IOException
+    {
+        // Empty
+    }
+
+    /**
+     * @param listernId The listernId to set.
+     */
+    protected void setListenerId( long listernId )
+    {
+        this.listenerId = listernId;
+    }
+
+    /**
+     * @return Returns the listernId.
+     */
+    protected long getListenerId()
+    {
+        return listenerId;
+    }
+
+    /**
+     * @param tcpLateralCacheAttributes The tcpLateralCacheAttributes to set.
+     */
+    public void setTcpLateralCacheAttributes( ITCPLateralCacheAttributes tcpLateralCacheAttributes )
+    {
+        this.tcpLateralCacheAttributes = tcpLateralCacheAttributes;
+    }
+
+    /**
+     * @return Returns the tcpLateralCacheAttributes.
+     */
+    public ITCPLateralCacheAttributes getTcpLateralCacheAttributes()
+    {
+        return tcpLateralCacheAttributes;
+    }
+
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TCPLateralCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TCPLateralCacheAttributes.java
new file mode 100644
index 0000000..5c6d55f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TCPLateralCacheAttributes.java
@@ -0,0 +1,399 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+
+/**
+ * This interface defines functions that are particular to the TCP Lateral Cache plugin. It extends
+ * the generic LateralCacheAttributes interface which in turn extends the AuxiliaryCache interface.
+ */
+public class TCPLateralCacheAttributes
+    extends LateralCacheAttributes
+    implements ITCPLateralCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 1077889204513905220L;
+
+    /** default */
+    private static final String DEFAULT_UDP_DISCOVERY_ADDRESS = "228.5.6.7";
+
+    /** default */
+    private static final int DEFAULT_UDP_DISCOVERY_PORT = 6789;
+
+    /** default */
+    private static final boolean DEFAULT_UDP_DISCOVERY_ENABLED = true;
+
+    /** default */
+    private static final boolean DEFAULT_ALLOW_GET = true;
+
+    /** default */
+    private static final boolean DEFAULT_ALLOW_PUT = true;
+
+    /** default */
+    private static final boolean DEFAULT_ISSUE_REMOVE_FOR_PUT = false;
+
+    /** default */
+    private static final boolean DEFAULT_FILTER_REMOVE_BY_HASH_CODE = true;
+
+    /** default - Only block for 1 second before timing out on a read.*/
+    private static final int DEFAULT_SOCKET_TIME_OUT = 1000;
+
+    /** default - Only block for 2 seconds before timing out on startup.*/
+    private static final int DEFAULT_OPEN_TIMEOUT = 2000;
+
+    /** TCP -------------------------------------------- */
+    private String tcpServers = "";
+
+    /** used to identify the service that this manager will be operating on */
+    private String tcpServer = "";
+
+    /** The pot */
+    private int tcpListenerPort = 0;
+
+    /** udp discovery for tcp server */
+    private String udpDiscoveryAddr = DEFAULT_UDP_DISCOVERY_ADDRESS;
+
+    /** discovery port */
+    private int udpDiscoveryPort = DEFAULT_UDP_DISCOVERY_PORT;
+
+    /** discovery switch */
+    private boolean udpDiscoveryEnabled = DEFAULT_UDP_DISCOVERY_ENABLED;
+
+    /** can we put */
+    private boolean allowPut = DEFAULT_ALLOW_GET;
+
+    /** can we go laterally for a get */
+    private boolean allowGet = DEFAULT_ALLOW_PUT;
+
+    /** call remove when there is a put */
+    private boolean issueRemoveOnPut = DEFAULT_ISSUE_REMOVE_FOR_PUT;
+
+    /** don't remove it the hashcode is the same */
+    private boolean filterRemoveByHashCode = DEFAULT_FILTER_REMOVE_BY_HASH_CODE;
+
+    /** Only block for socketTimeOut seconds before timing out on a read.  */
+    private int socketTimeOut = DEFAULT_SOCKET_TIME_OUT;
+
+    /** Only block for openTimeOut seconds before timing out on startup. */
+    private int openTimeOut = DEFAULT_OPEN_TIMEOUT;
+
+    /**
+     * Sets the tcpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new tcpServer value
+     */
+    @Override
+    public void setTcpServer( String val )
+    {
+        this.tcpServer = val;
+    }
+
+    /**
+     * Gets the tcpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpServer value
+     */
+    @Override
+    public String getTcpServer()
+    {
+        return this.tcpServer;
+    }
+
+    /**
+     * Sets the tcpServers attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new tcpServers value
+     */
+    @Override
+    public void setTcpServers( String val )
+    {
+        this.tcpServers = val;
+    }
+
+    /**
+     * Gets the tcpServers attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpServers value
+     */
+    @Override
+    public String getTcpServers()
+    {
+        return this.tcpServers;
+    }
+
+    /**
+     * Sets the tcpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val The new tcpListenerPort value
+     */
+    @Override
+    public void setTcpListenerPort( int val )
+    {
+        this.tcpListenerPort = val;
+    }
+
+    /**
+     * Gets the tcpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpListenerPort value
+     */
+    @Override
+    public int getTcpListenerPort()
+    {
+        return this.tcpListenerPort;
+    }
+
+    /**
+     * Can setup UDP Discovery. This only works for TCp laterals right now. It allows TCP laterals
+     * to find each other by broadcasting to a multicast port.
+     * <p>
+     * @param udpDiscoveryEnabled The udpDiscoveryEnabled to set.
+     */
+    @Override
+    public void setUdpDiscoveryEnabled( boolean udpDiscoveryEnabled )
+    {
+        this.udpDiscoveryEnabled = udpDiscoveryEnabled;
+    }
+
+    /**
+     * Whether or not TCP laterals can try to find each other by multicast communication.
+     * <p>
+     * @return Returns the udpDiscoveryEnabled.
+     */
+    @Override
+    public boolean isUdpDiscoveryEnabled()
+    {
+        return this.udpDiscoveryEnabled;
+    }
+
+    /**
+     * The port to use if UDPDiscovery is enabled.
+     * <p>
+     * @return Returns the udpDiscoveryPort.
+     */
+    @Override
+    public int getUdpDiscoveryPort()
+    {
+        return this.udpDiscoveryPort;
+    }
+
+    /**
+     * Sets the port to use if UDPDiscovery is enabled.
+     * <p>
+     * @param udpDiscoveryPort The udpDiscoveryPort to set.
+     */
+    @Override
+    public void setUdpDiscoveryPort( int udpDiscoveryPort )
+    {
+        this.udpDiscoveryPort = udpDiscoveryPort;
+    }
+
+    /**
+     * The address to broadcast to if UDPDiscovery is enabled.
+     * <p>
+     * @return Returns the udpDiscoveryAddr.
+     */
+    @Override
+    public String getUdpDiscoveryAddr()
+    {
+        return this.udpDiscoveryAddr;
+    }
+
+    /**
+     * Sets the address to broadcast to if UDPDiscovery is enabled.
+     * <p>
+     * @param udpDiscoveryAddr The udpDiscoveryAddr to set.
+     */
+    @Override
+    public void setUdpDiscoveryAddr( String udpDiscoveryAddr )
+    {
+        this.udpDiscoveryAddr = udpDiscoveryAddr;
+    }
+
+    /**
+     * Is the lateral allowed to try and get from other laterals.
+     * <p>
+     * This replaces the old putOnlyMode
+     * <p>
+     * @param allowGet
+     */
+    @Override
+    public void setAllowGet( boolean allowGet )
+    {
+        this.allowGet = allowGet;
+    }
+
+    /**
+     * Is the lateral allowed to try and get from other laterals.
+     * <p>
+     * @return true if the lateral will try to get
+     */
+    @Override
+    public boolean isAllowGet()
+    {
+        return this.allowGet;
+    }
+
+    /**
+     * Is the lateral allowed to put objects to other laterals.
+     * <p>
+     * @param allowPut
+     */
+    @Override
+    public void setAllowPut( boolean allowPut )
+    {
+        this.allowPut = allowPut;
+    }
+
+    /**
+     * Is the lateral allowed to put objects to other laterals.
+     * <p>
+     * @return true if puts are allowed
+     */
+    @Override
+    public boolean isAllowPut()
+    {
+        return this.allowPut;
+    }
+
+    /**
+     * Should the client send a remove command rather than a put when update is called. This is a
+     * client option, not a receiver option. This allows you to prevent the lateral from serializing
+     * objects.
+     * <p>
+     * @param issueRemoveOnPut
+     */
+    @Override
+    public void setIssueRemoveOnPut( boolean issueRemoveOnPut )
+    {
+        this.issueRemoveOnPut = issueRemoveOnPut;
+    }
+
+    /**
+     * Should the client send a remove command rather than a put when update is called. This is a
+     * client option, not a receiver option. This allows you to prevent the lateral from serializing
+     * objects.
+     * <p>
+     * @return true if updates will result in a remove command being sent.
+     */
+    @Override
+    public boolean isIssueRemoveOnPut()
+    {
+        return this.issueRemoveOnPut;
+    }
+
+    /**
+     * @return AuxiliaryCacheAttributes
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            //noop
+        }
+        return this;
+    }
+
+    /**
+     * Should the receiver try to match hashcodes. If true, the receiver will see if the client
+     * supplied a hshcode. If it did, then it will try to get the item locally. If the item exists,
+     * then it will compare the hashcode. if they are the same, it will not remove. This isn't
+     * perfect since different objects can have the same hashcode, but it is unlikely of objects of
+     * the same type.
+     * <p>
+     * @return boolean
+     */
+    @Override
+    public boolean isFilterRemoveByHashCode()
+    {
+        return this.filterRemoveByHashCode;
+    }
+
+    /**
+     * Should the receiver try to match hashcodes. If true, the receiver will see if the client
+     * supplied a hshcode. If it did, then it will try to get the item locally. If the item exists,
+     * then it will compare the hashcode. if they are the same, it will not remove. This isn't
+     * perfect since different objects can have the same hashcode, but it is unlikely of objects of
+     * the same type.
+     * <p>
+     * @param filter
+     */
+    @Override
+    public void setFilterRemoveByHashCode( boolean filter )
+    {
+        this.filterRemoveByHashCode = filter;
+    }
+
+    /**
+     * @param socketTimeOut the socketTimeOut to set
+     */
+    @Override
+    public void setSocketTimeOut( int socketTimeOut )
+    {
+        this.socketTimeOut = socketTimeOut;
+    }
+
+    /**
+     * @return the socketTimeOut
+     */
+    @Override
+    public int getSocketTimeOut()
+    {
+        return socketTimeOut;
+    }
+
+    /**
+     * @param openTimeOut the openTimeOut to set
+     */
+    @Override
+    public void setOpenTimeOut( int openTimeOut )
+    {
+        this.openTimeOut = openTimeOut;
+    }
+
+    /**
+     * @return the openTimeOut
+     */
+    @Override
+    public int getOpenTimeOut()
+    {
+        return openTimeOut;
+    }
+
+    /**
+     * Used to key the instance TODO create another method for this and use toString for debugging
+     * only.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        return this.getTcpServer() + ":" + this.getTcpListenerPort();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/behavior/ITCPLateralCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/behavior/ITCPLateralCacheAttributes.java
new file mode 100644
index 0000000..db12800
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/behavior/ITCPLateralCacheAttributes.java
@@ -0,0 +1,218 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+
+/**
+ * This interface defines functions that are particular to the TCP Lateral Cache
+ * plugin. It extends the generic LateralCacheAttributes interface which in turn
+ * extends the AuxiliaryCache interface.
+ * <p>
+ * @author Aaron Smuts
+ */
+public interface ITCPLateralCacheAttributes
+    extends ILateralCacheAttributes
+{
+    /**
+     * Sets the tcpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val
+     *            The new tcpServer value
+     */
+    void setTcpServer( String val );
+
+    /**
+     * Gets the tcpServer attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpServer value
+     */
+    String getTcpServer();
+
+    /**
+     * Sets the tcpServers attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val
+     *            The new tcpServers value
+     */
+    void setTcpServers( String val );
+
+    /**
+     * Gets the tcpServers attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpServers value
+     */
+    String getTcpServers();
+
+    /**
+     * Sets the tcpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @param val
+     *            The new tcpListenerPort value
+     */
+    void setTcpListenerPort( int val );
+
+    /**
+     * Gets the tcpListenerPort attribute of the ILateralCacheAttributes object
+     * <p>
+     * @return The tcpListenerPort value
+     */
+    int getTcpListenerPort();
+
+    /**
+     * Can setup UDP Discovery. This only works for TCp laterals right now. It
+     * allows TCP laterals to find each other by broadcasting to a multicast
+     * port.
+     * <p>
+     * @param udpDiscoveryEnabled
+     *            The udpDiscoveryEnabled to set.
+     */
+    void setUdpDiscoveryEnabled( boolean udpDiscoveryEnabled );
+
+    /**
+     * Whether or not TCP laterals can try to find each other by multicast
+     * communication.
+     * <p>
+     * @return Returns the udpDiscoveryEnabled.
+     */
+    boolean isUdpDiscoveryEnabled();
+
+    /**
+     * The port to use if UDPDiscovery is enabled.
+     * <p>
+     * @return Returns the udpDiscoveryPort.
+     */
+    int getUdpDiscoveryPort();
+
+    /**
+     * Sets the port to use if UDPDiscovery is enabled.
+     * <p>
+     * @param udpDiscoveryPort
+     *            The udpDiscoveryPort to set.
+     */
+    void setUdpDiscoveryPort( int udpDiscoveryPort );
+
+    /**
+     * The address to broadcast to if UDPDiscovery is enabled.
+     * <p>
+     * @return Returns the udpDiscoveryAddr.
+     */
+    String getUdpDiscoveryAddr();
+
+    /**
+     * Sets the address to broadcast to if UDPDiscovery is enabled.
+     * <p>
+     * @param udpDiscoveryAddr
+     *            The udpDiscoveryAddr to set.
+     */
+    void setUdpDiscoveryAddr( String udpDiscoveryAddr );
+
+    /**
+     * Is the lateral allowed to try and get from other laterals.
+     * <p>
+     * This replaces the old putOnlyMode
+     * <p>
+     * @param allowGet
+     */
+    void setAllowGet( boolean allowGet );
+
+    /**
+     * Is the lateral allowed to try and get from other laterals.
+     * <p>
+     * @return true if the lateral will try to get
+     */
+    boolean isAllowGet();
+
+    /**
+     * Is the lateral allowed to put objects to other laterals.
+     * <p>
+     * @param allowPut
+     */
+    void setAllowPut( boolean allowPut );
+
+    /**
+     * Is the lateral allowed to put objects to other laterals.
+     * <p>
+     * @return true if puts are allowed
+     */
+    boolean isAllowPut();
+
+    /**
+     * Should the client send a remove command rather than a put when update is
+     * called. This is a client option, not a receiver option. This allows you
+     * to prevent the lateral from serializing objects.
+     * <p>
+     * @param issueRemoveOnPut
+     */
+    void setIssueRemoveOnPut( boolean issueRemoveOnPut );
+
+    /**
+     * Should the client send a remove command rather than a put when update is
+     * called. This is a client option, not a receiver option. This allows you
+     * to prevent the lateral from serializing objects.
+     * <p>
+     * @return true if updates will result in a remove command being sent.
+     */
+    boolean isIssueRemoveOnPut();
+
+    /**
+     * Should the receiver try to match hashcodes. If true, the receiver will
+     * see if the client supplied a hashcode. If it did, then it will try to get
+     * the item locally. If the item exists, then it will compare the hashcode.
+     * if they are the same, it will not remove. This isn't perfect since
+     * different objects can have the same hashcode, but it is unlikely of
+     * objects of the same type.
+     * <p>
+     * @return boolean
+     */
+    boolean isFilterRemoveByHashCode();
+
+    /**
+     * Should the receiver try to match hashcodes. If true, the receiver will
+     * see if the client supplied a hashcode. If it did, then it will try to get
+     * the item locally. If the item exists, then it will compare the hashcode.
+     * if they are the same, it will not remove. This isn't perfect since
+     * different objects can have the same hashcode, but it is unlikely of
+     * objects of the same type.
+     * <p>
+     * @param filter
+     */
+    void setFilterRemoveByHashCode( boolean filter );
+
+    /**
+     * @param socketTimeOut the socketTimeOut to set
+     */
+    void setSocketTimeOut( int socketTimeOut );
+
+    /**
+     * @return the socketTimeOut
+     */
+    int getSocketTimeOut();
+
+    /**
+     * @param openTimeOut the openTimeOut to set
+     */
+    void setOpenTimeOut( int openTimeOut );
+
+    /**
+     * @return the openTimeOut
+     */
+    int getOpenTimeOut();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/package.html
new file mode 100644
index 0000000..d1d712f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    Root package for auxiliary caches.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteAuxiliaryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteAuxiliaryCache.java
new file mode 100644
index 0000000..bb4e805
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteAuxiliaryCache.java
@@ -0,0 +1,718 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheEventLogging;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.IZombie;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.jcs.utils.serialization.SerializationConversionUtil;
+import org.apache.commons.jcs.utils.threadpool.ThreadPoolManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** Abstract base for remote caches. I'm trying to break out and reuse common functionality. */
+public abstract class AbstractRemoteAuxiliaryCache<K, V>
+    extends AbstractAuxiliaryCacheEventLogging<K, V>
+    implements IRemoteCacheClient<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( AbstractRemoteAuxiliaryCache.class );
+
+    /**
+     * This does the work. In an RMI instances, it will be a remote reference. In an http remote
+     * cache it will be an http client. In zombie mode it is replaced with a balking facade.
+     */
+    private ICacheServiceNonLocal<K, V> remoteCacheService;
+
+    /** The cacheName */
+    protected final String cacheName;
+
+    /** The listener. This can be null. */
+    private IRemoteCacheListener<K, V> remoteCacheListener;
+
+    /** The configuration values. TODO, we'll need a base here. */
+    private IRemoteCacheAttributes remoteCacheAttributes;
+
+    /** A thread pool for gets if configured. */
+    private ThreadPoolExecutor pool = null;
+
+    /** Should we get asynchronously using a pool. */
+    private boolean usePoolForGet = false;
+
+    /**
+     * Creates the base.
+     * <p>
+     * @param cattr
+     * @param remote
+     * @param listener
+     */
+    public AbstractRemoteAuxiliaryCache( IRemoteCacheAttributes cattr, ICacheServiceNonLocal<K, V> remote,
+                                         IRemoteCacheListener<K, V> listener )
+    {
+        this.setRemoteCacheAttributes( cattr );
+        this.cacheName = cattr.getCacheName();
+        this.setRemoteCacheService( remote );
+        this.setRemoteCacheListener( listener );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Construct> cacheName=" + cattr.getCacheName() );
+            log.debug( "irca = " + getRemoteCacheAttributes() );
+            log.debug( "remote = " + remote );
+            log.debug( "listener = " + listener );
+        }
+
+        // use a pool if it is greater than 0
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "GetTimeoutMillis() = " + getRemoteCacheAttributes().getGetTimeoutMillis() );
+        }
+
+        if ( getRemoteCacheAttributes().getGetTimeoutMillis() > 0 )
+        {
+            pool = ThreadPoolManager.getInstance().getPool( getRemoteCacheAttributes().getThreadPoolName() );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Thread Pool = " + pool );
+            }
+            if ( pool != null )
+            {
+                usePoolForGet = true;
+            }
+        }
+    }
+
+    /**
+     * Synchronously dispose the remote cache; if failed, replace the remote handle with a zombie.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    protected void processDispose()
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Disposing of remote cache." );
+        }
+        try
+        {
+            if ( getRemoteCacheListener() != null )
+            {
+                getRemoteCacheListener().dispose();
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Couldn't dispose", ex );
+            handleException( ex, "Failed to dispose [" + cacheName + "]", ICacheEventLogger.DISPOSE_EVENT );
+        }
+    }
+
+    /**
+     * Synchronously get from the remote cache; if failed, replace the remote handle with a zombie.
+     * <p>
+     * Use threadpool to timeout if a value is set for GetTimeoutMillis
+     * <p>
+     * If we are a cluster client, we need to leave the Element in its serialized form. Cluster
+     * clients cannot deserialize objects. Cluster clients get ICacheElementSerialized objects from
+     * other remote servers.
+     * <p>
+     * @param key
+     * @return ICacheElement, a wrapper around the key, value, and attributes
+     * @throws IOException
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+        throws IOException
+    {
+        ICacheElement<K, V> retVal = null;
+        try
+        {
+            if ( usePoolForGet )
+            {
+                retVal = getUsingPool( key );
+            }
+            else
+            {
+                retVal = getRemoteCacheService().get( cacheName, key, getListenerId() );
+            }
+
+            // Eventually the instance of will not be necessary.
+            if ( retVal != null && retVal instanceof ICacheElementSerialized )
+            {
+                // Never try to deserialize if you are a cluster client. Cluster
+                // clients are merely intra-remote cache communicators. Remote caches are assumed
+                // to have no ability to deserialize the objects.
+                if ( this.getRemoteCacheAttributes().getRemoteType() != RemoteType.CLUSTER )
+                {
+                    retVal = SerializationConversionUtil.getDeSerializedCacheElement( (ICacheElementSerialized<K, V>) retVal,
+                            super.getElementSerializer() );
+                }
+            }
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to get [" + key + "] from [" + cacheName + "]", ICacheEventLogger.GET_EVENT );
+        }
+        return retVal;
+    }
+
+    /**
+     * This allows gets to timeout in case of remote server machine shutdown.
+     * <p>
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    public ICacheElement<K, V> getUsingPool( final K key )
+        throws IOException
+    {
+        int timeout = getRemoteCacheAttributes().getGetTimeoutMillis();
+
+        try
+        {
+            Callable<ICacheElement<K, V>> command = new Callable<ICacheElement<K, V>>()
+            {
+                @Override
+                public ICacheElement<K, V> call()
+                    throws IOException
+                {
+                    return getRemoteCacheService().get( cacheName, key, getListenerId() );
+                }
+            };
+
+            // execute using the pool
+            Future<ICacheElement<K, V>> future = pool.submit(command);
+
+            // used timed get in order to timeout
+            ICacheElement<K, V> ice = future.get(timeout, TimeUnit.MILLISECONDS);
+
+            if ( log.isDebugEnabled() )
+            {
+                if ( ice == null )
+                {
+                    log.debug( "nothing found in remote cache" );
+                }
+                else
+                {
+                    log.debug( "found item in remote cache" );
+                }
+            }
+            return ice;
+        }
+        catch ( TimeoutException te )
+        {
+            log.warn( "TimeoutException, Get Request timed out after " + timeout );
+            throw new IOException( "Get Request timed out after " + timeout );
+        }
+        catch ( InterruptedException ex )
+        {
+            log.warn( "InterruptedException, Get Request timed out after " + timeout );
+            throw new IOException( "Get Request timed out after " + timeout );
+        }
+        catch (ExecutionException ex)
+        {
+            // assume that this is an IOException thrown by the callable.
+            log.error( "ExecutionException, Assuming an IO exception thrown in the background.", ex );
+            throw new IOException( "Get Request timed out after " + timeout );
+        }
+    }
+
+    /**
+     * Calls get matching on the server. Each entry in the result is unwrapped.
+     * <p>
+     * @param pattern
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> results = new HashMap<K, ICacheElement<K, V>>();
+        try
+        {
+            Map<K, ICacheElement<K, V>> rawResults = getRemoteCacheService().getMatching( cacheName, pattern, getListenerId() );
+
+            // Eventually the instance of will not be necessary.
+            if ( rawResults != null )
+            {
+                for (Map.Entry<K, ICacheElement<K, V>> entry : rawResults.entrySet())
+                {
+                    ICacheElement<K, V> unwrappedResult = null;
+                    if ( entry.getValue() instanceof ICacheElementSerialized )
+                    {
+                        // Never try to deserialize if you are a cluster client. Cluster
+                        // clients are merely intra-remote cache communicators. Remote caches are assumed
+                        // to have no ability to deserialize the objects.
+                        if ( this.getRemoteCacheAttributes().getRemoteType() != RemoteType.CLUSTER )
+                        {
+                            unwrappedResult = SerializationConversionUtil
+                                .getDeSerializedCacheElement( (ICacheElementSerialized<K, V>) entry.getValue(),
+                                        super.getElementSerializer() );
+                        }
+                    }
+                    else
+                    {
+                        unwrappedResult = entry.getValue();
+                    }
+                    results.put( entry.getKey(), unwrappedResult );
+                }
+            }
+        }
+        catch ( Exception ex )
+        {
+            handleException( ex, "Failed to getMatching [" + pattern + "] from [" + cacheName + "]",
+                             ICacheEventLogger.GET_EVENT );
+        }
+        return results;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    protected Map<K, ICacheElement<K, V>> processGetMultiple( Set<K> keys )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+        return elements;
+    }
+
+    /**
+     * Synchronously remove from the remote cache; if failed, replace the remote handle with a
+     * zombie.
+     * <p>
+     * @param key
+     * @return boolean, whether or not the item was removed
+     * @throws IOException
+     */
+    @Override
+    protected boolean processRemove( K key )
+        throws IOException
+    {
+        if ( !this.getRemoteCacheAttributes().getGetOnly() )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "remove> key=" + key );
+            }
+            try
+            {
+                getRemoteCacheService().remove( cacheName, key, getListenerId() );
+            }
+            catch ( Exception ex )
+            {
+                handleException( ex, "Failed to remove " + key + " from " + cacheName, ICacheEventLogger.REMOVE_EVENT );
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Synchronously removeAll from the remote cache; if failed, replace the remote handle with a
+     * zombie.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    protected void processRemoveAll()
+        throws IOException
+    {
+        if ( !this.getRemoteCacheAttributes().getGetOnly() )
+        {
+            try
+            {
+                getRemoteCacheService().removeAll( cacheName, getListenerId() );
+            }
+            catch ( Exception ex )
+            {
+                handleException( ex, "Failed to remove all from " + cacheName, ICacheEventLogger.REMOVEALL_EVENT );
+            }
+        }
+    }
+
+    /**
+     * Serializes the object and then calls update on the remote server with the byte array. The
+     * byte array is wrapped in a ICacheElementSerialized. This allows the remote server to operate
+     * without any knowledge of caches classes.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        if ( !getRemoteCacheAttributes().getGetOnly() )
+        {
+            ICacheElementSerialized<K, V> serialized = null;
+            try
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "sending item to remote server" );
+                }
+
+                // convert so we don't have to know about the object on the
+                // other end.
+                serialized = SerializationConversionUtil.getSerializedCacheElement( ce, super.getElementSerializer() );
+
+                remoteCacheService.update( serialized, getListenerId() );
+            }
+            catch ( NullPointerException npe )
+            {
+                log.error( "npe for ce = " + ce + "ce.attr = " + ce.getElementAttributes(), npe );
+            }
+            catch ( Exception ex )
+            {
+                // event queue will wait and retry
+                handleException( ex, "Failed to put [" + ce.getKey() + "] to " + ce.getCacheName(),
+                                 ICacheEventLogger.UPDATE_EVENT );
+            }
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "get only mode, not sending to remote server" );
+            }
+        }
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet()
+        throws java.rmi.RemoteException, IOException
+    {
+        return getRemoteCacheService().getKeySet(cacheName);
+    }
+
+    /**
+     * Allows other member of this package to access the listener. This is mainly needed for
+     * deregistering a listener.
+     * <p>
+     * @return IRemoteCacheListener, the listener for this remote server
+     */
+    @Override
+    public IRemoteCacheListener<K, V> getListener()
+    {
+        return getRemoteCacheListener();
+    }
+
+    /**
+     * let the remote cache set a listener_id. Since there is only one listener for all the regions
+     * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we
+     * assume that it is a reconnect.
+     * <p>
+     * @param id The new listenerId value
+     */
+    public void setListenerId( long id )
+    {
+        if ( getRemoteCacheListener() != null )
+        {
+            try
+            {
+                getRemoteCacheListener().setListenerId( id );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "set listenerId = " + id );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem setting listenerId", e );
+            }
+        }
+    }
+
+    /**
+     * Gets the listenerId attribute of the RemoteCacheListener object
+     * <p>
+     * @return The listenerId value
+     */
+    @Override
+    public long getListenerId()
+    {
+        if ( getRemoteCacheListener() != null )
+        {
+            try
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "get listenerId = " + getRemoteCacheListener().getListenerId() );
+                }
+                return getRemoteCacheListener().getListenerId();
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem getting listenerId", e );
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the current cache size.
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return 0;
+    }
+
+    /**
+     * Custom exception handling some children.  This should be used to initiate failover.
+     * <p>
+     * @param ex
+     * @param msg
+     * @param eventName
+     * @throws IOException
+     */
+    protected abstract void handleException( Exception ex, String msg, String eventName )
+        throws IOException;
+
+    /**
+     * Gets the stats attribute of the RemoteCache object.
+     * <p>
+     * @return The stats value
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * @return IStats object
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "AbstractRemoteAuxiliaryCache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<String>( "Remote Type", this.getRemoteCacheAttributes().getRemoteTypeName() ) );
+
+//      if ( this.getRemoteCacheAttributes().getRemoteType() == RemoteType.CLUSTER )
+//      {
+//          // something cluster specific
+//      }
+
+        elems.add(new StatElement<Boolean>( "UsePoolForGet", Boolean.valueOf(usePoolForGet) ) );
+
+        if ( pool != null )
+        {
+            elems.add(new StatElement<Integer>( "Pool Size", Integer.valueOf(pool.getPoolSize()) ) );
+            elems.add(new StatElement<Integer>( "Maximum Pool Size", Integer.valueOf(pool.getMaximumPoolSize()) ) );
+        }
+
+        if ( getRemoteCacheService() instanceof ZombieCacheServiceNonLocal )
+        {
+            elems.add(new StatElement<Integer>( "Zombie Queue Size",
+                    Integer.valueOf(( (ZombieCacheServiceNonLocal<K, V>) getRemoteCacheService() ).getQueueSize()) ) );
+        }
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * Returns the cache status. An error status indicates the remote connection is not available.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return getRemoteCacheService() instanceof IZombie ? CacheStatus.ERROR : CacheStatus.ALIVE;
+    }
+
+    /**
+     * Replaces the current remote cache service handle with the given handle. If the current remote
+     * is a Zombie, then it propagates any events that are queued to the restored service.
+     * <p>
+     * @param restoredRemote ICacheServiceNonLocal -- the remote server or proxy to the remote server
+     */
+    @Override
+    public void fixCache( ICacheServiceNonLocal<?, ?> restoredRemote )
+    {
+        @SuppressWarnings("unchecked") // Don't know how to do this properly
+        ICacheServiceNonLocal<K, V> remote = (ICacheServiceNonLocal<K, V>)restoredRemote;
+        if ( getRemoteCacheService() != null && getRemoteCacheService() instanceof ZombieCacheServiceNonLocal )
+        {
+            ZombieCacheServiceNonLocal<K, V> zombie = (ZombieCacheServiceNonLocal<K, V>) getRemoteCacheService();
+            setRemoteCacheService( remote );
+            try
+            {
+                zombie.propagateEvents( remote );
+            }
+            catch ( Exception e )
+            {
+                try
+                {
+                    handleException( e, "Problem propagating events from Zombie Queue to new Remote Service.",
+                                     "fixCache" );
+                }
+                catch ( IOException e1 )
+                {
+                    // swallow, since this is just expected kick back.  Handle always throws
+                }
+            }
+        }
+        else
+        {
+            setRemoteCacheService( remote );
+        }
+    }
+
+
+    /**
+     * Gets the cacheType attribute of the RemoteCache object
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.REMOTE_CACHE;
+    }
+
+    /**
+     * Gets the cacheName attribute of the RemoteCache object.
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+
+    /**
+     * @param remote the remote to set
+     */
+    protected void setRemoteCacheService( ICacheServiceNonLocal<K, V> remote )
+    {
+        this.remoteCacheService = remote;
+    }
+
+    /**
+     * @return the remote
+     */
+    protected ICacheServiceNonLocal<K, V> getRemoteCacheService()
+    {
+        return remoteCacheService;
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return getRemoteCacheAttributes();
+    }
+
+    /**
+     * @param remoteCacheAttributes the remoteCacheAttributes to set
+     */
+    protected void setRemoteCacheAttributes( IRemoteCacheAttributes remoteCacheAttributes )
+    {
+        this.remoteCacheAttributes = remoteCacheAttributes;
+    }
+
+    /**
+     * @return the remoteCacheAttributes
+     */
+    protected IRemoteCacheAttributes getRemoteCacheAttributes()
+    {
+        return remoteCacheAttributes;
+    }
+
+    /**
+     * @param remoteCacheListener the remoteCacheListener to set
+     */
+    protected void setRemoteCacheListener( IRemoteCacheListener<K, V> remoteCacheListener )
+    {
+        this.remoteCacheListener = remoteCacheListener;
+    }
+
+    /**
+     * @return the remoteCacheListener
+     */
+    protected IRemoteCacheListener<K, V> getRemoteCacheListener()
+    {
+        return remoteCacheListener;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheListener.java
new file mode 100644
index 0000000..1e79712
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheListener.java
@@ -0,0 +1,350 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.utils.net.HostNameUtil;
+import org.apache.commons.jcs.utils.serialization.SerializationConversionUtil;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.UnknownHostException;
+
+/** Shared listener base. */
+public abstract class AbstractRemoteCacheListener<K, V>
+    implements IRemoteCacheListener<K, V>, Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = 32442324243243L;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( AbstractRemoteCacheListener.class );
+
+    /** The cached name of the local host. The remote server gets this for logging purposes. */
+    private static String localHostName = null;
+
+    /**
+     * The cache manager used to put items in different regions. This is set lazily and should not
+     * be sent to the remote server.
+     */
+    private transient ICompositeCacheManager cacheMgr;
+
+    /** The remote cache configuration object. */
+    private final IRemoteCacheAttributes irca;
+
+    /** Number of put requests received. For debugging only. */
+    protected int puts = 0;
+
+    /** Number of remove requests received. For debugging only. */
+    protected int removes = 0;
+
+    /** This is set by the remote cache server. */
+    private long listenerId = 0;
+
+    /** Custom serializer. Standard by default. */
+    private transient IElementSerializer elementSerializer = new StandardSerializer();
+
+    /**
+     * Only need one since it does work for all regions, just reference by multiple region names.
+     * <p>
+     * The constructor exports this object, making it available to receive incoming calls. The
+     * callback port is anonymous unless a local port value was specified in the configuration.
+     * <p>
+     * @param irca
+     * @param cacheMgr
+     */
+    public AbstractRemoteCacheListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr )
+    {
+        this.irca = irca;
+        this.cacheMgr = cacheMgr;
+    }
+
+    /**
+     * Let the remote cache set a listener_id. Since there is only one listener for all the regions
+     * and every region gets registered? the id shouldn't be set if it isn't zero. If it is we
+     * assume that it is a reconnect.
+     * <p>
+     * @param id The new listenerId value
+     * @throws IOException
+     */
+    @Override
+    public void setListenerId( long id )
+        throws IOException
+    {
+        listenerId = id;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "set listenerId = [" + id + "]" );
+        }
+    }
+
+    /**
+     * Gets the listenerId attribute of the RemoteCacheListener object. This is stored in the
+     * object. The RemoteCache object contains a reference to the listener and get the id this way.
+     * <p>
+     * @return The listenerId value
+     * @throws IOException
+     */
+    @Override
+    public long getListenerId()
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "get listenerId = [" + listenerId + "]" );
+        }
+        return listenerId;
+
+    }
+
+    /**
+     * Gets the remoteType attribute of the RemoteCacheListener object <p.
+     * @return The remoteType value
+     * @throws IOException
+     */
+    @Override
+    public RemoteType getRemoteType()
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "getRemoteType = [" + irca.getRemoteType() + "]" );
+        }
+        return irca.getRemoteType();
+    }
+
+    /**
+     * If this is configured to remove on put, then remove the element since it has been updated
+     * elsewhere. cd should be incomplete for faster transmission. We don't want to pass data only
+     * invalidation. The next time it is used the local cache will get the new version from the
+     * remote store.
+     * <p>
+     * If remove on put is not configured, then update the item.
+     * @param cb
+     * @throws IOException
+     */
+    @Override
+    public void handlePut( ICacheElement<K, V> cb )
+        throws IOException
+    {
+        if ( irca.getRemoveUponRemotePut() )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "PUTTING ELEMENT FROM REMOTE, (  invalidating ) " );
+            }
+            handleRemove( cb.getCacheName(), cb.getKey() );
+        }
+        else
+        {
+            puts++;
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "PUTTING ELEMENT FROM REMOTE, ( updating ) " );
+                log.debug( "cb = " + cb );
+
+                if ( puts % 100 == 0 )
+                {
+                    log.debug( "puts = " + puts );
+                }
+            }
+
+            CompositeCache<K, V> cache = getCacheManager().getCache( cb.getCacheName() );
+
+            // Eventually the instance of will not be necessary.
+            if ( cb instanceof ICacheElementSerialized )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Object needs to be deserialized." );
+                }
+                try
+                {
+                    cb = SerializationConversionUtil.getDeSerializedCacheElement(
+                            (ICacheElementSerialized<K, V>) cb, this.elementSerializer );
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Deserialized result = " + cb );
+                    }
+                }
+                catch ( IOException e )
+                {
+                    throw e;
+                }
+                catch ( ClassNotFoundException e )
+                {
+                    log.error( "Received a serialized version of a class that we don't know about.", e );
+                }
+            }
+
+            cache.localUpdate( cb );
+        }
+    }
+
+    /**
+     * Calls localRemove on the CompositeCache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void handleRemove( String cacheName, K key )
+        throws IOException
+    {
+        removes++;
+        if ( log.isDebugEnabled() )
+        {
+            if ( removes % 100 == 0 )
+            {
+                log.debug( "removes = " + removes );
+            }
+
+            log.debug( "handleRemove> cacheName=" + cacheName + ", key=" + key );
+        }
+
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        cache.localRemove( key );
+    }
+
+    /**
+     * Calls localRemoveAll on the CompositeCache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleRemoveAll( String cacheName )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleRemoveAll> cacheName=" + cacheName );
+        }
+
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+        cache.localRemoveAll();
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleDispose( String cacheName )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "handleDispose> cacheName=" + cacheName );
+        }
+        // TODO consider what to do here, we really don't want to
+        // dispose, we just want to disconnect.
+        // just allow the cache to go into error recovery mode.
+        // getCacheManager().freeCache( cacheName, true );
+    }
+
+    /**
+     * Gets the cacheManager attribute of the RemoteCacheListener object. This is one of the few
+     * places that force the cache to be a singleton.
+     */
+    protected ICompositeCacheManager getCacheManager()
+    {
+        if ( cacheMgr == null )
+        {
+            try
+            {
+                cacheMgr = CompositeCacheManager.getInstance();
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "had to get cacheMgr" );
+                    log.debug( "cacheMgr = " + cacheMgr );
+                }
+            }
+            catch (CacheException e)
+            {
+                log.error( "Could not get cacheMgr", e );
+            }
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "already got cacheMgr = " + cacheMgr );
+            }
+        }
+
+        return cacheMgr;
+    }
+
+    /**
+     * This is for debugging. It allows the remote server to log the address of clients.
+     * <p>
+     * @return String
+     * @throws IOException
+     */
+    @Override
+    public synchronized String getLocalHostAddress()
+        throws IOException
+    {
+        if ( localHostName == null )
+        {
+            try
+            {
+                localHostName = HostNameUtil.getLocalHostAddress();
+            }
+            catch ( UnknownHostException uhe )
+            {
+                localHostName = "unknown";
+            }
+        }
+        return localHostName;
+    }
+
+    /**
+     * For easier debugging.
+     * <p>
+     * @return Basic info on this listener.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n AbstractRemoteCacheListener: " );
+        buf.append( "\n RemoteHost = " + irca.getRemoteHost() );
+        buf.append( "\n RemotePort = " + irca.getRemotePort() );
+        buf.append( "\n ListenerId = " + listenerId );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheNoWaitFacade.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheNoWaitFacade.java
new file mode 100644
index 0000000..623f3ed
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/AbstractRemoteCacheNoWaitFacade.java
@@ -0,0 +1,472 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/** An abstract base for the No Wait Facade.  Different implementations will failover differently. */
+public abstract class AbstractRemoteCacheNoWaitFacade<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( AbstractRemoteCacheNoWaitFacade.class );
+
+    /** The connection to a remote server, or a zombie. */
+    public RemoteCacheNoWait<K, V>[] noWaits; // TODO privatise if possible
+
+    /** The cache name */
+    private final String cacheName;
+
+    /** holds failover and cluster information */
+    private IRemoteCacheAttributes remoteCacheAttributes;
+
+    /** A cache manager */
+    private ICompositeCacheManager compositeCacheManager;
+
+    /**
+     * Constructs with the given remote cache, and fires events to any listeners.
+     * <p>
+     * @param noWaits
+     * @param rca
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    public AbstractRemoteCacheNoWaitFacade( RemoteCacheNoWait<K, V>[] noWaits, RemoteCacheAttributes rca,
+                                    ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger,
+                                    IElementSerializer elementSerializer )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "CONSTRUCTING NO WAIT FACADE" );
+        }
+        this.noWaits = noWaits;
+        this.remoteCacheAttributes = rca;
+        this.cacheName = rca.getCacheName();
+        setCompositeCacheManager( cacheMgr );
+        setCacheEventLogger( cacheEventLogger );
+        setElementSerializer( elementSerializer );
+    }
+
+    /**
+     * Put an element in the cache.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "updating through cache facade, noWaits.length = " + noWaits.length );
+        }
+        int i = 0;
+        try
+        {
+            for ( ; i < noWaits.length; i++ )
+            {
+                noWaits[i].update( ce );
+                // an initial move into a zombie will lock this to primary
+                // recovery. will not discover other servers until primary
+                // reconnect
+                // and subsequent error
+            }
+        }
+        catch ( Exception ex )
+        {
+            String message = "Problem updating no wait.  Will initiate failover if the noWait is in error.";
+            log.error( message, ex );
+
+            if ( getCacheEventLogger() != null )
+            {
+                getCacheEventLogger().logError(
+                                                "RemoteCacheNoWaitFacade",
+                                                ICacheEventLogger.UPDATE_EVENT,
+                                                message + ":" + ex.getMessage() + " REGION: " + ce.getCacheName()
+                                                    + " ELEMENT: " + ce );
+            }
+
+            // can handle failover here? Is it safe to try the others?
+            // check to see it the noWait is now a zombie
+            // if it is a zombie, then move to the next in the failover list
+            // will need to keep them in order or a count
+            failover( i );
+            // should start a failover thread
+            // should probably only failover if there is only one in the noWait
+            // list
+            // Should start a background thread to restore the original primary if we are in failover state.
+        }
+    }
+
+    /**
+     * Synchronously reads from the remote cache.
+     * <p>
+     * @param key
+     * @return Either an ICacheElement<K, V> or null if it is not found.
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+    {
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            try
+            {
+                ICacheElement<K, V> obj = noWaits[i].get( key );
+                if ( obj != null )
+                {
+                    return obj;
+                }
+            }
+            catch ( IOException ex )
+            {
+                log.debug( "Failed to get." );
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Synchronously read from the remote cache.
+     * <p>
+     * @param pattern
+     * @return map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
+        throws IOException
+    {
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            try
+            {
+                return noWaits[i].getMatching( pattern );
+            }
+            catch ( IOException ex )
+            {
+                log.debug( "Failed to getMatching." );
+            }
+        }
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
+    {
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                try
+                {
+                    return noWaits[i].getMultiple( keys );
+                }
+                catch ( IOException ex )
+                {
+                    log.debug( "Failed to get." );
+                }
+            }
+        }
+
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        HashSet<K> allKeys = new HashSet<K>();
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            AuxiliaryCache<K, V> aux = noWaits[i];
+            if ( aux != null )
+            {
+                Set<K> keys = aux.getKeySet();
+                if(keys != null)
+                {
+                    allKeys.addAll( keys );
+                }
+            }
+        }
+        return allKeys;
+    }
+
+    /**
+     * Adds a remove request to the remote cache.
+     * <p>
+     * @param key
+     * @return whether or not it was removed, right now it return false.
+     */
+    @Override
+    public boolean remove( K key )
+    {
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].remove( key );
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+        return false;
+    }
+
+    /**
+     * Adds a removeAll request to the remote cache.
+     */
+    @Override
+    public void removeAll()
+    {
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].removeAll();
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( ex );
+        }
+    }
+
+    /** Adds a dispose request to the remote cache. */
+    @Override
+    public void dispose()
+    {
+        try
+        {
+            for ( int i = 0; i < noWaits.length; i++ )
+            {
+                noWaits[i].dispose();
+            }
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Problem in dispose.", ex );
+        }
+    }
+
+    /**
+     * No remote invocation.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return 0;
+        // cache.getSize();
+    }
+
+    /**
+     * Gets the cacheType attribute of the RemoteCacheNoWaitFacade object.
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.REMOTE_CACHE;
+    }
+
+    /**
+     * Gets the cacheName attribute of the RemoteCacheNoWaitFacade object.
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return remoteCacheAttributes.getCacheName();
+    }
+
+    /**
+     * Gets the status attribute of the RemoteCacheNoWaitFacade object
+     * <p>
+     * Return ALIVE if any are alive.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        for ( int i = 0; i < noWaits.length; i++ )
+        {
+            if ( noWaits[i].getStatus() == CacheStatus.ALIVE )
+            {
+                return CacheStatus.ALIVE;
+            }
+        }
+
+        return CacheStatus.DISPOSED;
+    }
+
+    /**
+     * String form of some of the configuration information for the remote cache.
+     * <p>
+     * @return Some info for logging.
+     */
+    @Override
+    public String toString()
+    {
+        return "RemoteCacheNoWaitFacade: " + cacheName + ", rca = " + remoteCacheAttributes;
+    }
+
+    /**
+     * Begin the failover process if this is a local cache. Clustered remote caches do not failover.
+     * <p>
+     * @param i The no wait in error.
+     */
+    abstract void failover( int i );
+
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return this.remoteCacheAttributes;
+    }
+
+    /**
+     * getStats
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * @return statistics about the cache region
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Remote Cache No Wait Facade" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        if ( noWaits != null )
+        {
+            elems.add(new StatElement<Integer>( "Number of No Waits", Integer.valueOf(noWaits.length) ) );
+
+            for ( RemoteCacheNoWait<K, V> rcnw : noWaits )
+            {
+                // get the stats from the super too
+                IStats sStats = rcnw.getStatistics();
+                elems.addAll(sStats.getStatElements());
+            }
+        }
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * This typically returns end point info .
+     * <p>
+     * @return the name
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return "Remote Cache No Wait Facade";
+    }
+
+    /**
+     * Gets the remoteCacheAttributes attribute of the RemoteCacheNoWaitFacade object
+     * <p>
+     * @return The remoteCacheAttributes value
+     */
+    public IRemoteCacheAttributes getRemoteCacheAttributes()
+    {
+        return remoteCacheAttributes;
+    }
+
+    /**
+     * Sets the remoteCacheAttributes attribute of the RemoteCacheNoWaitFacade object.
+     * <p>
+     * @param rca The new remoteCacheAttributes value
+     */
+    public void setRemoteCacheAttributes( IRemoteCacheAttributes rca )
+    {
+        this.remoteCacheAttributes = rca;
+    }
+
+    /**
+     * @param compositeCacheManager the compositeCacheManager to set
+     */
+    protected void setCompositeCacheManager( ICompositeCacheManager compositeCacheManager )
+    {
+        this.compositeCacheManager = compositeCacheManager;
+    }
+
+    /**
+     * @return the compositeCacheManager
+     */
+    protected ICompositeCacheManager getCompositeCacheManager()
+    {
+        return compositeCacheManager;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/CommonRemoteCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/CommonRemoteCacheAttributes.java
new file mode 100644
index 0000000..59f74da
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/CommonRemoteCacheAttributes.java
@@ -0,0 +1,323 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.ICommonRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+
+/**
+ * Attributes common to remote cache client and server.
+ */
+public class CommonRemoteCacheAttributes
+    extends AbstractAuxiliaryCacheAttributes
+    implements ICommonRemoteCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -1555143736942374000L;
+
+    /** The service name */
+    private String remoteServiceName = IRemoteCacheConstants.REMOTE_CACHE_SERVICE_VAL;
+
+    /** server host */
+    private String remoteHost;
+
+    /** server port */
+    private int remotePort;
+
+    /** Cluster chain */
+    private String clusterServers = "";
+
+    /** THe type of remote cache, local or cluster */
+    private RemoteType remoteType = RemoteType.LOCAL;
+
+    /** Should we issue a local remove if we get a put from a remote server */
+    private boolean removeUponRemotePut = true;
+
+    /** Can we receive from or put to the remote. this probably shouldn't be used. Use receive. */
+    private boolean getOnly = false;
+
+    /** Should we put and get from the clusters. */
+    private boolean localClusterConsistency = false;
+
+    /** read and connect timeout */
+    private int rmiSocketFactoryTimeoutMillis = DEFAULT_RMI_SOCKET_FACTORY_TIMEOUT_MILLIS;
+
+    /** Default constructor for the RemoteCacheAttributes object */
+    public CommonRemoteCacheAttributes()
+    {
+        super();
+    }
+
+    /**
+     * Gets the remoteTypeName attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The remoteTypeName value
+     */
+    @Override
+    public String getRemoteTypeName()
+    {
+        return remoteType != null ? remoteType.toString() : RemoteType.LOCAL.toString();
+    }
+
+    /**
+     * Sets the remoteTypeName attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param s The new remoteTypeName value
+     */
+    @Override
+    public void setRemoteTypeName( String s )
+    {
+        RemoteType rt = RemoteType.valueOf(s);
+        if (rt != null)
+        {
+            this.remoteType = rt;
+        }
+    }
+
+    /**
+     * Gets the remoteType attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The remoteType value
+     */
+    @Override
+    public RemoteType getRemoteType()
+    {
+        return remoteType;
+    }
+
+    /**
+     * Sets the remoteType attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param p The new remoteType value
+     */
+    @Override
+    public void setRemoteType( RemoteType p )
+    {
+        this.remoteType = p;
+    }
+
+    /**
+     * @return AuxiliaryCacheAttributes
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            // swallow
+        }
+        return this;
+    }
+
+    /**
+     * Gets the remoteServiceName attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The remoteServiceName value
+     */
+    @Override
+    public String getRemoteServiceName()
+    {
+        return this.remoteServiceName;
+    }
+
+    /**
+     * Sets the remoteServiceName attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param s The new remoteServiceName value
+     */
+    @Override
+    public void setRemoteServiceName( String s )
+    {
+        this.remoteServiceName = s;
+    }
+
+    /**
+     * Gets the remoteHost attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The remoteHost value
+     */
+    @Override
+    public String getRemoteHost()
+    {
+        return this.remoteHost;
+    }
+
+    /**
+     * Sets the remoteHost attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param s The new remoteHost value
+     */
+    @Override
+    public void setRemoteHost( String s )
+    {
+        this.remoteHost = s;
+    }
+
+    /**
+     * Gets the remotePort attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The remotePort value
+     */
+    @Override
+    public int getRemotePort()
+    {
+        return this.remotePort;
+    }
+
+    /**
+     * Sets the remotePort attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param p The new remotePort value
+     */
+    @Override
+    public void setRemotePort( int p )
+    {
+        this.remotePort = p;
+    }
+
+    /**
+     * Gets the clusterServers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The clusterServers value
+     */
+    @Override
+    public String getClusterServers()
+    {
+        return this.clusterServers;
+    }
+
+    /**
+     * Sets the clusterServers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param s The new clusterServers value
+     */
+    @Override
+    public void setClusterServers( String s )
+    {
+        this.clusterServers = s;
+    }
+
+    /**
+     * Gets the removeUponRemotePut attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The removeUponRemotePut value
+     */
+    @Override
+    public boolean getRemoveUponRemotePut()
+    {
+        return this.removeUponRemotePut;
+    }
+
+    /**
+     * Sets the removeUponRemotePut attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param r The new removeUponRemotePut value
+     */
+    @Override
+    public void setRemoveUponRemotePut( boolean r )
+    {
+        this.removeUponRemotePut = r;
+    }
+
+    /**
+     * Gets the getOnly attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The getOnly value
+     */
+    @Override
+    public boolean getGetOnly()
+    {
+        return this.getOnly;
+    }
+
+    /**
+     * Sets the getOnly attribute of the RemoteCacheAttributes object
+     * @param r The new getOnly value
+     */
+    @Override
+    public void setGetOnly( boolean r )
+    {
+        this.getOnly = r;
+    }
+
+    /**
+     * Should cluster updates be propagated to the locals.
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    @Override
+    public boolean isLocalClusterConsistency()
+    {
+        return localClusterConsistency;
+    }
+
+    /**
+     * Should cluster updates be propagated to the locals.
+     * <p>
+     * @param r The new localClusterConsistency value
+     */
+    @Override
+    public void setLocalClusterConsistency( boolean r )
+    {
+        this.localClusterConsistency = r;
+    }
+
+    /**
+     * @param rmiSocketFactoryTimeoutMillis The rmiSocketFactoryTimeoutMillis to set.
+     */
+    @Override
+    public void setRmiSocketFactoryTimeoutMillis( int rmiSocketFactoryTimeoutMillis )
+    {
+        this.rmiSocketFactoryTimeoutMillis = rmiSocketFactoryTimeoutMillis;
+    }
+
+    /**
+     * @return Returns the rmiSocketFactoryTimeoutMillis.
+     */
+    @Override
+    public int getRmiSocketFactoryTimeoutMillis()
+    {
+        return rmiSocketFactoryTimeoutMillis;
+    }
+
+    /**
+     * @return String, all the important values that can be configured
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n RemoteCacheAttributes " );
+        buf.append( "\n remoteHost = [" + this.remoteHost + "]" );
+        buf.append( "\n remotePort = [" + this.remotePort + "]" );
+        buf.append( "\n cacheName = [" + super.getCacheName() + "]" );
+        buf.append( "\n remoteType = [" + remoteType + "]" );
+        buf.append( "\n removeUponRemotePut = [" + this.removeUponRemotePut + "]" );
+        buf.append( "\n getOnly = [" + getOnly + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCache.java
new file mode 100644
index 0000000..8e341f3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCache.java
@@ -0,0 +1,186 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Client proxy for an RMI remote cache.
+ * <p>
+ * This handles gets, updates, and removes. It also initiates failover recovery when an error is
+ * encountered.
+ */
+public class RemoteCache<K, V>
+    extends AbstractRemoteAuxiliaryCache<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( RemoteCache.class );
+
+    /**
+     * Constructor for the RemoteCache object. This object communicates with a remote cache server.
+     * One of these exists for each region. This also holds a reference to a listener. The same
+     * listener is used for all regions for one remote server. Holding a reference to the listener
+     * allows this object to know the listener id assigned by the remote cache.
+     * <p>
+     * @param cattr
+     * @param remote
+     * @param listener
+     */
+    public RemoteCache( IRemoteCacheAttributes cattr, ICacheServiceNonLocal<K, V> remote, IRemoteCacheListener<K, V> listener )
+    {
+        super( cattr, remote, listener );
+
+        RemoteUtils.configureGlobalCustomSocketFactory( getRemoteCacheAttributes().getRmiSocketFactoryTimeoutMillis() );
+    }
+
+    /**
+     * @return IStats object
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Remote Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<String>( "Remote Host:Port", getIPAddressForService() ) );
+        elems.add(new StatElement<String>( "Remote Type", this.getRemoteCacheAttributes().getRemoteTypeName() ) );
+
+//      if ( this.getRemoteCacheAttributes().getRemoteType() == RemoteType.CLUSTER )
+//      {
+//          // something cluster specific
+//      }
+
+        // get the stats from the super too
+        IStats sStats = super.getStatistics();
+        elems.addAll(sStats.getStatElements());
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * Handles exception by disabling the remote cache service before re-throwing the exception in
+     * the form of an IOException.
+     * <p>
+     * @param ex
+     * @param msg
+     * @param eventName
+     * @throws IOException
+     */
+    @Override
+    protected void handleException( Exception ex, String msg, String eventName )
+        throws IOException
+    {
+        String message = "Disabling remote cache due to error: " + msg;
+
+        logError( cacheName, "", message );
+        log.error( message, ex );
+
+        // we should not switch if the existing is a zombie.
+        if ( getRemoteCacheService() == null || !( getRemoteCacheService() instanceof ZombieCacheServiceNonLocal ) )
+        {
+            // TODO make configurable
+            setRemoteCacheService( new ZombieCacheServiceNonLocal<K, V>( getRemoteCacheAttributes().getZombieQueueMaxSize() ) );
+        }
+        // may want to flush if region specifies
+        // Notify the cache monitor about the error, and kick off the recovery
+        // process.
+        RemoteCacheMonitor.getInstance().notifyError();
+
+        // initiate failover if local
+        @SuppressWarnings("unchecked") // Need to cast because of common map for all facades
+        RemoteCacheNoWaitFacade<K, V> rcnwf = (RemoteCacheNoWaitFacade<K, V>)RemoteCacheFactory.getFacades()
+            .get( getRemoteCacheAttributes().getCacheName() );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Initiating failover, rcnf = " + rcnwf );
+        }
+
+        if ( rcnwf != null && rcnwf.getRemoteCacheAttributes().getRemoteType() == RemoteType.LOCAL )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Found facade, calling failover" );
+            }
+            // may need to remove the noWait index here. It will be 0 if it is
+            // local since there is only 1 possible listener.
+            rcnwf.failover( 0 );
+        }
+
+        if ( ex instanceof IOException )
+        {
+            throw (IOException) ex;
+        }
+        throw new IOException( ex.getMessage() );
+    }
+
+    /**
+     * Debugging info.
+     * <p>
+     * @return basic info about the RemoteCache
+     */
+    @Override
+    public String toString()
+    {
+        return "RemoteCache: " + cacheName + " attributes = " + getRemoteCacheAttributes();
+    }
+
+    /**
+     * Gets the extra info for the event log.
+     * <p>
+     * @return disk location
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return getIPAddressForService();
+    }
+
+    /**
+     * IP address for the service, if one is stored.
+     * <p>
+     * Protected for testing.
+     * <p>
+     * @return String
+     */
+    protected String getIPAddressForService()
+    {
+        String ipAddress = this.getRemoteCacheAttributes().getRemoteHost() + ":"
+            + this.getRemoteCacheAttributes().getRemotePort();
+        return ipAddress;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheAttributes.java
new file mode 100644
index 0000000..491e810
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheAttributes.java
@@ -0,0 +1,261 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+
+/**
+ * These objects are used to configure the remote cache client.
+ */
+public class RemoteCacheAttributes
+    extends CommonRemoteCacheAttributes
+    implements IRemoteCacheAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -1555143736942374000L;
+
+    /**
+     * Failover servers will be used by local caches one at a time. Listeners will be registered
+     * with all cluster servers. If we add a get from cluster attribute we will have the ability to
+     * chain clusters and have them get from each other.
+     */
+    private String failoverServers = "";
+
+    /** callback */
+    private int localPort = 0;
+
+    /** what failover server we are connected to. */
+    private int failoverIndex = 0;
+
+    /** Array of failover server addresses */
+    private String[] failovers;
+
+    /** default name is remote_cache_client */
+    private String threadPoolName = "remote_cache_client";
+
+    /** must be greater than 0 for a pool to be used. */
+    private int getTimeoutMillis = -1;
+
+    /**
+     * Can we receive from the server. You might have a 0 local store and keep everything on the
+     * remote. If so, you don't want to be notified of updates.
+     */
+    private boolean receive = DEFAULT_RECEIVE;
+
+    /** If the primary fails, we will queue items before reconnect.  This limits the number of items that can be queued. */
+    private int zombieQueueMaxSize = DEFAULT_ZOMBIE_QUEUE_MAX_SIZE;
+
+    /** Default constructor for the RemoteCacheAttributes object */
+    public RemoteCacheAttributes()
+    {
+        super();
+    }
+
+    /**
+     * Gets the failoverIndex attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The failoverIndex value
+     */
+    @Override
+    public int getFailoverIndex()
+    {
+        return failoverIndex;
+    }
+
+    /**
+     * Sets the failoverIndex attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param p The new failoverIndex value
+     */
+    @Override
+    public void setFailoverIndex( int p )
+    {
+        this.failoverIndex = p;
+    }
+
+    /**
+     * Gets the failovers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The failovers value
+     */
+    @Override
+    public String[] getFailovers()
+    {
+        return this.failovers;
+    }
+
+    /**
+     * Sets the failovers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param f The new failovers value
+     */
+    @Override
+    public void setFailovers( String[] f )
+    {
+        this.failovers = f;
+    }
+
+    /**
+     * Gets the failoverServers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The failoverServers value
+     */
+    @Override
+    public String getFailoverServers()
+    {
+        return this.failoverServers;
+    }
+
+    /**
+     * Sets the failoverServers attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @param s The new failoverServers value
+     */
+    @Override
+    public void setFailoverServers( String s )
+    {
+        this.failoverServers = s;
+    }
+
+    /**
+     * Gets the localPort attribute of the RemoteCacheAttributes object.
+     * <p>
+     * @return The localPort value
+     */
+    @Override
+    public int getLocalPort()
+    {
+        return this.localPort;
+    }
+
+    /**
+     * Sets the localPort attribute of the RemoteCacheAttributes object
+     * @param p The new localPort value
+     */
+    @Override
+    public void setLocalPort( int p )
+    {
+        this.localPort = p;
+    }
+
+    /**
+     * @return the name of the pool
+     */
+    @Override
+    public String getThreadPoolName()
+    {
+        return threadPoolName;
+    }
+
+    /**
+     * @param name
+     */
+    @Override
+    public void setThreadPoolName( String name )
+    {
+        threadPoolName = name;
+    }
+
+    /**
+     * @return getTimeoutMillis
+     */
+    @Override
+    public int getGetTimeoutMillis()
+    {
+        return getTimeoutMillis;
+    }
+
+    /**
+     * @param millis
+     */
+    @Override
+    public void setGetTimeoutMillis( int millis )
+    {
+        getTimeoutMillis = millis;
+    }
+
+    /**
+     * By default this option is true. If you set it to false, you will not receive updates or
+     * removes from the remote server.
+     * <p>
+     * @param receive
+     */
+    @Override
+    public void setReceive( boolean receive )
+    {
+        this.receive = receive;
+    }
+
+    /**
+     * If RECEIVE is false then the remote cache will not register a listener with the remote
+     * server. This allows you to configure a remote server as a repository from which you can get
+     * and to which you put, but from which you do not receive any notifications. That is, you will
+     * not receive updates or removes.
+     * <p>
+     * If you set this option to false, you should set your local memory size to 0.
+     * <p>
+     * The remote cache manager uses this value to decide whether or not to register a listener.
+     * @return the receive value.
+     */
+    @Override
+    public boolean isReceive()
+    {
+        return this.receive;
+    }
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @param zombieQueueMaxSize The zombieQueueMaxSize to set.
+     */
+    @Override
+    public void setZombieQueueMaxSize( int zombieQueueMaxSize )
+    {
+        this.zombieQueueMaxSize = zombieQueueMaxSize;
+    }
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @return Returns the zombieQueueMaxSize.
+     */
+    @Override
+    public int getZombieQueueMaxSize()
+    {
+        return zombieQueueMaxSize;
+    }
+
+    /**
+     * @return String, all the important values that can be configured
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder(super.toString());
+        buf.append( "\n receive = [" + isReceive() + "]" );
+        buf.append( "\n getTimeoutMillis = [" + getGetTimeoutMillis() + "]" );
+        buf.append( "\n threadPoolName = [" + getThreadPoolName() + "]" );
+        buf.append( "\n localClusterConsistency = [" + isLocalClusterConsistency() + "]" );
+        buf.append( "\n zombieQueueMaxSize = [" + getZombieQueueMaxSize() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFactory.java
new file mode 100644
index 0000000..b89b304
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFactory.java
@@ -0,0 +1,189 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+/**
+ * The RemoteCacheFactory creates remote caches for the cache hub. It returns a no wait facade which
+ * is a wrapper around a no wait. The no wait object is either an active connection to a remote
+ * cache or a balking zombie if the remote cache is not accessible. It should be transparent to the
+ * clients.
+ */
+public class RemoteCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** The name of this auxiliary */
+    private String name;
+
+    /** store reference of facades to initiate failover */
+    private static final HashMap<String, RemoteCacheNoWaitFacade<?, ?>> facades =
+        new HashMap<String, RemoteCacheNoWaitFacade<?, ?>>();
+
+    /**
+     * For LOCAL clients we get a handle to all the failovers, but we do not register a listener
+     * with them. We create the RemoteCacheManager, but we do not get a cache.
+     * <p>
+     * The failover runner will get a cache from the manager. When the primary is restored it will
+     * tell the manager for the failover to deregister the listener.
+     * <p>
+     * @param iaca
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache(
+            AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
+           ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        RemoteCacheAttributes rca = (RemoteCacheAttributes) iaca;
+
+        ArrayList<ICache<K, V>> noWaits = new ArrayList<ICache<K, V>>();
+
+        // if LOCAL
+        if ( rca.getRemoteType() == RemoteType.LOCAL )
+        {
+            // a list to be turned into an array of failover server information
+            ArrayList<String> failovers = new ArrayList<String>();
+
+            // not necessary if a failover list is defined
+            // REGISTER PRIMARY LISTENER
+            // if it is a primary
+            boolean primayDefined = false;
+            if ( rca.getRemoteHost() != null )
+            {
+                primayDefined = true;
+
+                failovers.add( rca.getRemoteHost() + ":" + rca.getRemotePort() );
+
+                RemoteCacheManager rcm = RemoteCacheManager.getInstance( rca, cacheMgr, cacheEventLogger,
+                                                                         elementSerializer );
+                ICache<K, V> ic = rcm.getCache( rca );
+                noWaits.add( ic );
+            }
+
+            // GET HANDLE BUT DONT REGISTER A LISTENER FOR FAILOVERS
+            String failoverList = rca.getFailoverServers();
+            if ( failoverList != null )
+            {
+                StringTokenizer fit = new StringTokenizer( failoverList, "," );
+                int fCnt = 0;
+                while ( fit.hasMoreElements() )
+                {
+                    fCnt++;
+
+                    String server = (String) fit.nextElement();
+                    failovers.add( server );
+
+                    rca.setRemoteHost( server.substring( 0, server.indexOf( ":" ) ) );
+                    rca.setRemotePort( Integer.parseInt( server.substring( server.indexOf( ":" ) + 1 ) ) );
+                    RemoteCacheManager rcm = RemoteCacheManager.getInstance( rca, cacheMgr, cacheEventLogger,
+                                                                             elementSerializer );
+                    // add a listener if there are none, need to tell rca what
+                    // number it is at
+                    if ( ( !primayDefined && fCnt == 1 ) || noWaits.size() <= 0 )
+                    {
+                        ICache<K, V> ic = rcm.getCache( rca );
+                        noWaits.add( ic );
+                    }
+                }
+                // end while
+            }
+            // end if failoverList != null
+
+            rca.setFailovers( failovers.toArray( new String[0] ) );
+
+            // if CLUSTER
+        }
+        else if ( rca.getRemoteType() == RemoteType.CLUSTER )
+        {
+            // REGISTER LISTENERS FOR EACH SYSTEM CLUSTERED CACHEs
+            StringTokenizer it = new StringTokenizer( rca.getClusterServers(), "," );
+            while ( it.hasMoreElements() )
+            {
+                // String server = (String)it.next();
+                String server = (String) it.nextElement();
+                // p( "tcp server = " + server );
+                rca.setRemoteHost( server.substring( 0, server.indexOf( ":" ) ) );
+                rca.setRemotePort( Integer.parseInt( server.substring( server.indexOf( ":" ) + 1 ) ) );
+                RemoteCacheManager rcm = RemoteCacheManager.getInstance( rca, cacheMgr, cacheEventLogger,
+                                                                         elementSerializer );
+                rca.setRemoteType( RemoteType.CLUSTER );
+                ICache<K, V> ic = rcm.getCache( rca );
+                noWaits.add( ic );
+            }
+
+        }
+        // end if CLUSTER
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        RemoteCacheNoWait<K, V>[] rcnwArray = noWaits.toArray( new RemoteCacheNoWait[0] );
+        RemoteCacheNoWaitFacade<K, V> rcnwf =
+            new RemoteCacheNoWaitFacade<K, V>(rcnwArray, rca, cacheMgr, cacheEventLogger, elementSerializer );
+
+        getFacades().put( rca.getCacheName(), rcnwf );
+
+        return rcnwf;
+    }
+
+    // end createCache
+
+    /**
+     * Gets the name attribute of the RemoteCacheFactory object
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * Sets the name attribute of the RemoteCacheFactory object
+     * @param name The new name value
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    /**
+     * The facades are what the cache hub talks to.
+     * @return Returns the facades.
+     */
+    public static HashMap<String, RemoteCacheNoWaitFacade<?, ?>> getFacades()
+    {
+        return facades;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFailoverRunner.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFailoverRunner.java
new file mode 100644
index 0000000..ad47dbc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheFailoverRunner.java
@@ -0,0 +1,492 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * The RemoteCacheFailoverRunner tries to establish a connection with a failover
+ * server, if any are defined. Once a failover connection is made, it will
+ * attempt to replace the failover with the primary remote server.
+ * <p>
+ * It works by switching out the RemoteCacheNoWait inside the Facade.
+ * <p>
+ * Client (i.e.) the CompositeCache has reference to a RemoteCacheNoWaitFacade.
+ * This facade is created by the RemoteCacheFactory. The factory maintains a set
+ * of managers, one for each remote server. Typically, there will only be one
+ * manager.
+ * <p>
+ * If you use multiple remote servers, you may want to set one or more as
+ * failovers. If a local cache cannot connect to the primary server, or looses
+ * its connection to the primary server, it will attempt to restore that
+ * Connection in the background. If failovers are defined, the Failover runner
+ * will try to connect to a failover until the primary is restored.
+ *
+ */
+public class RemoteCacheFailoverRunner<K, V>
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheFailoverRunner.class );
+
+    /** The facade returned to the composite cache. */
+    private final RemoteCacheNoWaitFacade<K, V> facade;
+
+    /** How long to wait between reconnect attempts. */
+    private static long idlePeriod = 20 * 1000;
+
+    /** Have we reconnected. */
+    private boolean alright = true;
+
+    /** The cache manager */
+    private final ICompositeCacheManager cacheMgr;
+
+    /** The event logger. */
+    private final ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /**
+     * Constructor for the RemoteCacheFailoverRunner object. This allows the
+     * FailoverRunner to modify the facade that the CompositeCache references.
+     *
+     * @param facade
+     *            the facade the CompositeCache talks to.
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    public RemoteCacheFailoverRunner( RemoteCacheNoWaitFacade<K, V> facade, ICompositeCacheManager cacheMgr,
+                                      ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        this.facade = facade;
+        this.cacheMgr = cacheMgr;
+        this.cacheEventLogger = cacheEventLogger;
+        this.elementSerializer = elementSerializer;
+    }
+
+    /**
+     * Notifies the cache monitor that an error occurred, and kicks off the
+     * error recovery process.
+     */
+    public void notifyError()
+    {
+        bad();
+        synchronized ( this )
+        {
+            notify();
+        }
+    }
+
+    /**
+     * Main processing method for the RemoteCacheFailoverRunner object.
+     * <p>
+     * If we do not have a connection with any failover server, this will try to
+     * connect one at a time. If no connection can be made, it goes to sleep for
+     * a while (20 seconds).
+     * <p>
+     * Once a connection with a failover is made, we will try to reconnect to
+     * the primary server.
+     * <p>
+     * The primary server is the first server defines in the FailoverServers
+     * list.
+     */
+    @Override
+    public void run()
+    {
+        // start the main work of connecting to a failover and then restoring
+        // the primary.
+        connectAndRestore();
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Exiting failover runner. Failover index = " + facade.getRemoteCacheAttributes().getFailoverIndex() );
+            if ( facade.getRemoteCacheAttributes().getFailoverIndex() <= 0 )
+            {
+                log.info( "Failover index is <= 0, meaning we are not " + "connected to a failover server." );
+            }
+            else if ( facade.getRemoteCacheAttributes().getFailoverIndex() > 0 )
+            {
+                log.info( "Failover index is > 0, meaning we are " + "connected to a failover server." );
+            }
+            // log if we are alright or not.
+        }
+    }
+
+    /**
+     * This is the main loop. If there are failovers defined, then this will
+     * continue until the primary is re-connected. If no failovers are defined,
+     * this will exit automatically.
+     */
+    @SuppressWarnings("unchecked") // No generic arrays in java
+    private void connectAndRestore()
+    {
+        do
+        {
+            log.info( "Remote cache FAILOVER RUNNING." );
+
+            // there is no active listener
+            if ( !alright )
+            {
+                // Monitor each RemoteCacheManager instance one after the other.
+                // Each RemoteCacheManager corresponds to one remote connection.
+                String[] failovers = facade.getRemoteCacheAttributes().getFailovers();
+                // we should probably check to see if there are any failovers,
+                // even though the caller
+                // should have already.
+
+                if ( failovers == null )
+                {
+                    log.warn( "Remote is misconfigured, failovers was null." );
+                    return;
+                }
+                else if ( failovers.length == 1 )
+                {
+                    // if there is only the primary, return out of this
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "No failovers defined, exiting failover runner." );
+                        return;
+                    }
+                }
+
+                int fidx = facade.getRemoteCacheAttributes().getFailoverIndex();
+                log.debug( "fidx = " + fidx + " failovers.length = " + failovers.length );
+
+                // shouldn't we see if the primary is backup?
+                // If we don't check the primary, if it gets connected in the
+                // background,
+                // we will disconnect it only to put it right back
+                int i = fidx; // + 1; // +1 skips the primary
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "stating at failover i = " + i );
+                }
+
+                // try them one at a time until successful
+                for ( ; i < failovers.length && !alright; i++ )
+                {
+                    String server = failovers[i];
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Trying server [" + server + "] at failover index i = " + i );
+                    }
+
+                    RemoteCacheAttributes rca = null;
+                    try
+                    {
+                        rca = (RemoteCacheAttributes) facade.getRemoteCacheAttributes().copy();
+                        rca.setRemoteHost( server.substring( 0, server.indexOf( ":" ) ) );
+                        rca.setRemotePort( Integer.parseInt( server.substring( server.indexOf( ":" ) + 1 ) ) );
+                        RemoteCacheManager rcm = RemoteCacheManager.getInstance( rca, cacheMgr, cacheEventLogger, elementSerializer );
+
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "RemoteCacheAttributes for failover = " + rca.toString() );
+                        }
+
+                        // add a listener if there are none, need to tell rca
+                        // what number it is at
+                        ICache<K, V> ic = rcm.getCache( rca.getCacheName() );
+                        if ( ic.getStatus() == CacheStatus.ALIVE )
+                        {
+                            // may need to do this more gracefully
+                            log.debug( "resetting no wait" );
+                            facade.noWaits = new RemoteCacheNoWait[1];
+                            facade.noWaits[0] = (RemoteCacheNoWait<K, V>) ic;
+                            facade.getRemoteCacheAttributes().setFailoverIndex( i );
+
+                            synchronized ( this )
+                            {
+                                if ( log.isDebugEnabled() )
+                                {
+                                    log.debug( "setting ALRIGHT to true" );
+                                    if ( i > 0 )
+                                    {
+                                        log.debug( "Moving to Primary Recovery Mode, failover index = " + i );
+                                    }
+                                    else
+                                    {
+                                        if ( log.isInfoEnabled() )
+                                        {
+                                            String message = "No need to connect to failover, the primary server is back up.";
+                                            log.info( message );
+                                        }
+                                    }
+                                }
+
+                                alright = true;
+
+                                if ( log.isInfoEnabled() )
+                                {
+                                    log.info( "CONNECTED to host = [" + rca.getRemoteHost() + "] port = ["
+                                        + rca.getRemotePort() + "]" );
+                                }
+                            }
+                        }
+                    }
+                    catch ( Exception ex )
+                    {
+                        bad();
+                        // Problem encountered in fixing the caches managed by a
+                        // RemoteCacheManager instance.
+                        // Soldier on to the next RemoteCacheManager instance.
+                        String remoteHost = (rca == null) ? "null" : rca.getRemoteHost();
+                        int remotePort = (rca == null) ? 0 : rca.getRemotePort();
+                        if ( i == 0 )
+                        {
+                            log.warn( "FAILED to connect, as expected, to primary" + remoteHost + ":"
+                                + remotePort, ex );
+                        }
+                        else
+                        {
+                            log.error( "FAILED to connect to failover [" + remoteHost + ":"
+                                + remotePort + "]", ex );
+                        }
+                    }
+                }
+            }
+            // end if !alright
+            // get here if while index >0 and alright, meaning that we are
+            // connected to some backup server.
+            else
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "ALRIGHT is true " );
+                }
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Failover runner is in primary recovery mode. Failover index = "
+                        + facade.getRemoteCacheAttributes().getFailoverIndex() + "\n" + "Will now try to reconnect to primary server." );
+                }
+            }
+
+            boolean primaryRestoredSuccessfully = false;
+            // if we are not connected to the primary, try.
+            if ( facade.getRemoteCacheAttributes().getFailoverIndex() > 0 )
+            {
+                primaryRestoredSuccessfully = restorePrimary();
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Primary recovery success state = " + primaryRestoredSuccessfully );
+                }
+            }
+
+            if ( !primaryRestoredSuccessfully )
+            {
+                // Time driven mode: sleep between each round of recovery
+                // attempt.
+                try
+                {
+                    log.warn( "Failed to reconnect to primary server. Cache failover runner is going to sleep for "
+                        + idlePeriod + " milliseconds." );
+                    Thread.sleep( idlePeriod );
+                }
+                catch ( InterruptedException ex )
+                {
+                    // ignore;
+                }
+            }
+
+            // try to bring the listener back to the primary
+        }
+        while ( facade.getRemoteCacheAttributes().getFailoverIndex() > 0 || !alright );
+        // continue if the primary is not restored or if things are not alright.
+
+    }
+
+    /**
+     * Try to restore the primary server.
+     * <p>
+     * Once primary is restored the failover listener must be deregistered.
+     * <p>
+     * The primary server is the first server defines in the FailoverServers
+     * list.
+     *
+     * @return boolean value indicating whether the restoration was successful
+     */
+    @SuppressWarnings("unchecked") // No generic arrays in java
+    private boolean restorePrimary()
+    {
+        // try to move back to the primary
+        String[] failovers = facade.getRemoteCacheAttributes().getFailovers();
+        String server = failovers[0];
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Trying to restore connection to primary remote server [" + server + "]" );
+        }
+
+        try
+        {
+            RemoteCacheAttributes rca = (RemoteCacheAttributes) facade.getRemoteCacheAttributes().copy();
+            rca.setRemoteHost( server.substring( 0, server.indexOf( ":" ) ) );
+            rca.setRemotePort( Integer.parseInt( server.substring( server.indexOf( ":" ) + 1 ) ) );
+            RemoteCacheManager rcm = RemoteCacheManager.getInstance( rca, cacheMgr, cacheEventLogger, elementSerializer );
+
+            // add a listener if there are none, need to tell rca what number it
+            // is at
+            ICache<K, V> ic = rcm.getCache( rca.getCacheName() );
+            // by default the listener id should be 0, else it will be the
+            // listener
+            // Originally associated with the remote cache. either way is fine.
+            // We just don't want the listener id from a failover being used.
+            // If the remote server was rebooted this could be a problem if new
+            // locals were also added.
+
+            if ( ic.getStatus() == CacheStatus.ALIVE )
+            {
+                try
+                {
+                    // we could have more than one listener registered right
+                    // now.
+                    // this will not result in a loop, only duplication
+                    // stop duplicate listening.
+                    if ( facade.noWaits[0] != null && facade.noWaits[0].getStatus() == CacheStatus.ALIVE )
+                    {
+                        int fidx = facade.getRemoteCacheAttributes().getFailoverIndex();
+
+                        if ( fidx > 0 )
+                        {
+                            String serverOld = failovers[fidx];
+
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "Failover Index = " + fidx + " the server at that index is ["
+                                    + serverOld + "]" );
+                            }
+
+                            if ( serverOld != null )
+                            {
+                                // create attributes that reflect the
+                                // previous failed over configuration.
+                                RemoteCacheAttributes rcaOld = (RemoteCacheAttributes) facade.getRemoteCacheAttributes().copy();
+                                rcaOld.setRemoteHost( serverOld.substring( 0, serverOld.indexOf( ":" ) ) );
+                                rcaOld.setRemotePort( Integer.parseInt( serverOld.substring( serverOld
+                                    .indexOf( ":" ) + 1 ) ) );
+                                RemoteCacheManager rcmOld = RemoteCacheManager.getInstance( rcaOld, cacheMgr, cacheEventLogger, elementSerializer );
+
+                                if ( rcmOld != null )
+                                {
+                                    // manager can remove by name if
+                                    // necessary
+                                    rcmOld.removeRemoteCacheListener( rcaOld );
+                                }
+                                if ( log.isInfoEnabled() )
+                                {
+                                    log.info( "Successfully deregistered from FAILOVER remote server = "
+                                        + serverOld );
+                                }
+                            }
+                        }
+                        else if ( fidx == 0 )
+                        {
+                            // this should never happen. If there are no
+                            // failovers this shouldn't get called.
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "No need to restore primary, it is already restored." );
+                                return true;
+                            }
+                        }
+                        else if ( fidx < 0 )
+                        {
+                            // this should never happen
+                            log.warn( "Failover index is less than 0, this shouldn't happen" );
+                        }
+                    }
+                }
+                catch ( IOException e )
+                {
+                    // TODO, should try again, or somehow stop the listener
+                    log.error(
+                               "Trouble trying to deregister old failover listener prior to restoring the primary = "
+                                   + server, e );
+                }
+
+                // Restore primary
+                // may need to do this more gracefully, letting the failover finish in the background
+                RemoteCacheNoWait<K, V> failoverNoWait = facade.noWaits[0];
+
+                // swap in a new one
+                facade.noWaits = new RemoteCacheNoWait[1];
+                facade.noWaits[0] = (RemoteCacheNoWait<K, V>) ic;
+                facade.getRemoteCacheAttributes().setFailoverIndex( 0 );
+
+                if ( log.isInfoEnabled() )
+                {
+                    String message = "Successfully reconnected to PRIMARY remote server.  Substituted primary for failoverNoWait [" + failoverNoWait + "]";
+                    log.info( message );
+
+                    if ( facade.getCacheEventLogger() != null )
+                    {
+                        facade.getCacheEventLogger().logApplicationEvent( "RemoteCacheFailoverRunner", "RestoredPrimary",
+                                                                          message );
+                    }
+                }
+                return true;
+            }
+
+            // else all right
+            // if the failover index was at 0 here, we would be in a bad
+            // situation, unless there were just
+            // no failovers configured.
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Primary server status in error, not connected." );
+            }
+        }
+        catch ( NumberFormatException ex )
+        {
+            log.error( ex );
+        }
+        return false;
+    }
+
+    /**
+     * Sets the "alright" flag to false in a critical section. This flag
+     * indicates whether or not we are connected to any server at all. If we are
+     * connected to a secondary server, then alright will be true, but we will
+     * continue to try to restore the connection with the primary server.
+     * <p>
+     * The primary server is the first server defines in the FailoverServers
+     * list.
+     */
+    private void bad()
+    {
+        if ( alright )
+        {
+            synchronized ( this )
+            {
+                alright = false;
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListener.java
new file mode 100644
index 0000000..ff79d7a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListener.java
@@ -0,0 +1,121 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+/**
+ * Registered with RemoteCache server. The server updates the local caches via this listener. Each
+ * server assigns a unique listener id for a listener.
+ * <p>
+ * One listener is used per remote cache server. The same listener is used for all the regions that
+ * talk to a particular server.
+ */
+public class RemoteCacheListener<K, V>
+    extends AbstractRemoteCacheListener<K, V>
+    implements IRemoteCacheConstants, Serializable, IRemoteCacheListener<K, V>
+{
+    /** Don't change */
+    private static final long serialVersionUID = 25345252345322345L;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheListener.class );
+
+    /** Has this client been shutdown. */
+    private boolean disposed = false;
+
+    /**
+     * Only need one since it does work for all regions, just reference by multiple region names.
+     * <p>
+     * The constructor exports this object, making it available to receive incoming calls. The
+     * callback port is anonymous unless a local port value was specified in the configuration.
+     * <p>
+     * @param irca
+     * @param cacheMgr
+     */
+    public RemoteCacheListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr )
+    {
+        super( irca, cacheMgr );
+
+        // Export this remote object to make it available to receive incoming
+        // calls.
+        try
+        {
+            UnicastRemoteObject.exportObject( this, irca.getLocalPort() );
+        }
+        catch ( RemoteException ex )
+        {
+            log.error( "Problem exporting object.", ex );
+            throw new IllegalStateException( ex.getMessage() );
+        }
+    }
+
+    /**
+     * Deregister itself.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public synchronized void dispose()
+        throws IOException
+    {
+        if ( !disposed )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Unexporting listener." );
+            }
+            try
+            {
+                UnicastRemoteObject.unexportObject( this, true );
+            }
+            catch ( RemoteException ex )
+            {
+                log.error( "Problem unexporting the listener.", ex );
+                throw new IllegalStateException( ex.getMessage() );
+            }
+            disposed = true;
+        }
+    }
+
+    /**
+     * For easier debugging.
+     * <p>
+     * @return Basic info on this listener.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n RemoteCacheListener: " );
+        buf.append( super.toString() );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheManager.java
new file mode 100644
index 0000000..7555875
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheManager.java
@@ -0,0 +1,611 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheManager;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.rmi.Naming;
+import java.rmi.registry.Registry;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An instance of RemoteCacheManager corresponds to one remote connection of a specific host and
+ * port. All RemoteCacheManager instances are monitored by the singleton RemoteCacheMonitor
+ * monitoring daemon for error detection and recovery.
+ * <p>
+ * Getting an instance of the remote cache has the effect of getting a handle on the remote server.
+ * Listeners are not registered with the server until a cache is requested from the manager.
+ */
+public class RemoteCacheManager
+    implements AuxiliaryCacheManager, IShutdownObserver
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheManager.class );
+
+    /** Contains mappings of Location instance to RemoteCacheManager instance. */
+    static final Map<Location, RemoteCacheManager> instances = new HashMap<Location, RemoteCacheManager>();
+
+    /** Monitors connections. */
+    private static RemoteCacheMonitor monitor;
+
+    /** Not so useful. How many getCaches over releases were called. */
+    private int clients;
+
+    /** Contains instances of RemoteCacheNoWait managed by a RemoteCacheManager instance. */
+    final Map<String, RemoteCacheNoWait<?, ?>> caches =
+        new HashMap<String, RemoteCacheNoWait<?, ?>>();
+
+    /** The remote host */
+    final String host;
+
+    /** The remote port */
+    final int port;
+
+    /** The service name */
+    final String service;
+
+    /** The configuration attributes. */
+    private IRemoteCacheAttributes remoteCacheAttributes;
+
+    /** The event logger. */
+    private final ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /** Handle to the remote cache service; or a zombie handle if failed to connect. */
+    private ICacheServiceNonLocal<?, ?> remoteService;
+
+    /**
+     * Wrapper of the remote cache watch service; or wrapper of a zombie service if failed to
+     * connect.
+     */
+    private RemoteCacheWatchRepairable remoteWatch;
+
+    /** The cache manager listeners will need to use to get a cache. */
+    private final ICompositeCacheManager cacheMgr;
+
+    /** The service found through lookup */
+    //private String registry;
+
+    /**
+     * Constructs an instance to with the given remote connection parameters. If the connection
+     * cannot be made, "zombie" services will be temporarily used until a successful re-connection
+     * is made by the monitoring daemon.
+     * <p>
+     * @param host
+     * @param port
+     * @param service
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private RemoteCacheManager( String host, int port, String service, ICompositeCacheManager cacheMgr,
+                                ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        this.host = host;
+        this.port = port;
+        this.service = service;
+        this.cacheMgr = cacheMgr;
+        this.cacheEventLogger = cacheEventLogger;
+        this.elementSerializer = elementSerializer;
+
+        // register shutdown observer
+        this.cacheMgr.registerShutdownObserver( this );
+
+        String registry = "//" + host + ":" + port + "/" + service;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Looking up server [" + registry + "]" );
+        }
+        try
+        {
+            Object obj = Naming.lookup( registry );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Server found: " + obj );
+            }
+
+            // Successful connection to the remote server.
+            remoteService = (ICacheServiceNonLocal<?, ?>) obj;
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "remoteService = " + remoteService );
+            }
+
+            remoteWatch = new RemoteCacheWatchRepairable();
+            remoteWatch.setCacheWatch( (IRemoteCacheObserver) obj );
+        }
+        catch ( Exception ex )
+        {
+            // Failed to connect to the remote server.
+            // Configure this RemoteCacheManager instance to use the "zombie"
+            // services.
+            log.error( "Problem finding server at [" + registry + "]", ex );
+            remoteService = new ZombieCacheServiceNonLocal<String, String>();
+            remoteWatch = new RemoteCacheWatchRepairable();
+            remoteWatch.setCacheWatch( new ZombieRemoteCacheWatch() );
+
+            // Notify the cache monitor about the error, and kick off the
+            // recovery process.
+            RemoteCacheMonitor.getInstance().notifyError();
+        }
+    }
+
+    /**
+     * Gets the defaultCattr attribute of the RemoteCacheManager object.
+     * <p>
+     * @return The defaultCattr value
+     */
+    public IRemoteCacheAttributes getDefaultCattr()
+    {
+        return this.remoteCacheAttributes;
+    }
+
+    /**
+     * Adds the remote cache listener to the underlying cache-watch service.
+     * <p>
+     * @param cattr The feature to be added to the RemoteCacheListener attribute
+     * @param listener The feature to be added to the RemoteCacheListener attribute
+     * @throws IOException
+     */
+    public <K, V> void addRemoteCacheListener( IRemoteCacheAttributes cattr, IRemoteCacheListener<K, V> listener )
+        throws IOException
+    {
+        if ( cattr.isReceive() )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "The remote cache is configured to receive events from the remote server.  "
+                    + "We will register a listener. remoteWatch = " + remoteWatch + " | IRemoteCacheListener = "
+                    + listener + " | cacheName " + cattr.getCacheName() );
+            }
+
+            synchronized ( caches )
+            {
+                remoteWatch.addCacheListener( cattr.getCacheName(), listener );
+            }
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "The remote cache is configured to NOT receive events from the remote server.  "
+                    + "We will NOT register a listener." );
+            }
+        }
+    }
+
+    /**
+     * Removes a listener. When the primary recovers the failover must deregister itself for a
+     * region. The failover runner will call this method to de-register. We do not want to deregister
+     * all listeners to a remote server, in case a failover is a primary of another region. Having
+     * one regions failover act as another servers primary is not currently supported.
+     * <p>
+     * @param cattr
+     * @param listener
+     * @throws IOException
+     */
+    public <K, V> void removeRemoteCacheListener( IRemoteCacheAttributes cattr, IRemoteCacheListener<K, V> listener )
+        throws IOException
+    {
+        synchronized ( caches )
+        {
+            remoteWatch.removeCacheListener( cattr.getCacheName(), listener );
+        }
+    }
+
+    /**
+     * Stops a listener. This is used to deregister a failover after primary reconnection.
+     * <p>
+     * @param cattr
+     * @throws IOException
+     */
+    public void removeRemoteCacheListener( IRemoteCacheAttributes cattr )
+        throws IOException
+    {
+        synchronized ( caches )
+        {
+            RemoteCacheNoWait<?, ?> cache = caches.get( cattr.getCacheName() );
+            if ( cache != null )
+            {
+                IRemoteCacheClient<?, ?> rc = cache.getRemoteCache();
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Found cache for[ " + cattr.getCacheName() + "], deregistering listener." );
+                }
+                // could also store the listener for a server in the manager.
+                IRemoteCacheListener<?, ?> listener = rc.getListener();
+                remoteWatch.removeCacheListener( cattr.getCacheName(), listener );
+            }
+            else
+            {
+                if ( cattr.isReceive() )
+                {
+                    log.warn( "Trying to deregister Cache Listener that was never registered." );
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Since the remote cache is configured to not receive, "
+                            + "there is no listener to deregister." );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Stops a listener. This is used to deregister a failover after primary reconnection.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    public void removeRemoteCacheListener( String cacheName )
+        throws IOException
+    {
+        synchronized ( caches )
+        {
+            RemoteCacheNoWait<?, ?> cache = caches.get( cacheName );
+            if ( cache != null )
+            {
+                IRemoteCacheClient<?, ?> rc = cache.getRemoteCache();
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Found cache for [" + cacheName + "], deregistering listener." );
+                }
+                // could also store the listener for a server in the manager.
+                IRemoteCacheListener<?, ?> listener = rc.getListener();
+                remoteWatch.removeCacheListener( cacheName, listener );
+            }
+        }
+    }
+
+    /**
+     * Returns an instance of RemoteCacheManager for the given connection parameters.
+     * <p>
+     * Host and Port uniquely identify a manager instance.
+     * <p>
+     * Also starts up the monitoring daemon, if not already started.
+     * <p>
+     * If the connection cannot be established, zombie objects will be used for future recovery
+     * purposes.
+     * <p>
+     * @param cattr
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public static RemoteCacheManager getInstance( IRemoteCacheAttributes cattr, ICompositeCacheManager cacheMgr,
+                                                  ICacheEventLogger cacheEventLogger,
+                                                  IElementSerializer elementSerializer )
+    {
+        String host = cattr.getRemoteHost();
+        int port = cattr.getRemotePort();
+        String service = cattr.getRemoteServiceName();
+        if ( host == null )
+        {
+            host = "";
+        }
+        if ( port < 1024 )
+        {
+            port = Registry.REGISTRY_PORT;
+        }
+        Location loc = new Location( host, port );
+
+        RemoteCacheManager ins = null;
+        synchronized ( instances )
+        {
+            ins = instances.get( loc );
+            if ( ins == null )
+            {
+                // Change to use cattr and to set defaults
+                ins = new RemoteCacheManager( host, port, service, cacheMgr, cacheEventLogger, elementSerializer );
+                ins.remoteCacheAttributes = cattr;
+                instances.put( loc, ins );
+            }
+        }
+
+        ins.clients++;
+        // Fires up the monitoring daemon.
+        if ( monitor == null )
+        {
+            monitor = RemoteCacheMonitor.getInstance();
+            // If the returned monitor is null, it means it's already started
+            // elsewhere.
+            if ( monitor != null )
+            {
+                Thread t = new Thread( monitor );
+                t.setDaemon( true );
+                t.start();
+            }
+        }
+        return ins;
+    }
+
+    /**
+     * Returns a remote cache for the given cache name.
+     * <p>
+     * @param cacheName
+     * @return The cache value
+     */
+    @Override
+    public <K, V> RemoteCacheNoWait<K, V> getCache( String cacheName )
+    {
+        IRemoteCacheAttributes ca = (IRemoteCacheAttributes) remoteCacheAttributes.copy();
+        ca.setCacheName( cacheName );
+        return getCache( ca );
+    }
+
+    /**
+     * Gets a RemoteCacheNoWait from the RemoteCacheManager. The RemoteCacheNoWait objects are
+     * identified by the cache name value of the RemoteCacheAttributes object.
+     * <p>
+     * If the client is configured to register a listener, this call results on a listener being
+     * created if one isn't already registered with the remote cache for this region.
+     * <p>
+     * @param cattr
+     * @return The cache value
+     */
+    public <K, V> RemoteCacheNoWait<K, V> getCache( IRemoteCacheAttributes cattr )
+    {
+        RemoteCacheNoWait<K, V> remoteCacheNoWait = null;
+
+        synchronized ( caches )
+        {
+            @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+            RemoteCacheNoWait<K, V> remoteCacheNoWait2 = (RemoteCacheNoWait<K, V>) caches.get( cattr.getCacheName() );
+            remoteCacheNoWait = remoteCacheNoWait2;
+            if ( remoteCacheNoWait == null )
+            {
+                // create a listener first and pass it to the remotecache
+                // sender.
+                RemoteCacheListener<K, V> listener = null;
+                try
+                {
+                    listener = new RemoteCacheListener<K, V>( cattr, cacheMgr );
+                    addRemoteCacheListener( cattr, listener );
+                }
+                catch ( IOException ioe )
+                {
+                    log.error( "IOException. Problem adding listener. Message: " + ioe.getMessage()
+                        + " | RemoteCacheListener = " + listener, ioe );
+                }
+                catch ( Exception e )
+                {
+                    log.error( "Problem adding listener. Message: " + e.getMessage() + " | RemoteCacheListener = "
+                        + listener, e );
+                }
+
+                @SuppressWarnings("unchecked") // Need to cast for specialized type
+                IRemoteCacheClient<K, V> remoteCacheClient = new RemoteCache<K, V>( cattr, (ICacheServiceNonLocal<K, V>) remoteService, listener );
+                remoteCacheClient.setCacheEventLogger( cacheEventLogger );
+                remoteCacheClient.setElementSerializer( elementSerializer );
+
+                remoteCacheNoWait = new RemoteCacheNoWait<K, V>( remoteCacheClient );
+                remoteCacheNoWait.setCacheEventLogger( cacheEventLogger );
+                remoteCacheNoWait.setElementSerializer( elementSerializer );
+
+                caches.put( cattr.getCacheName(), remoteCacheNoWait );
+            }
+
+            // might want to do some listener sanity checking here.
+        }
+
+        return remoteCacheNoWait;
+    }
+
+    /**
+     * Releases.
+     * <p>
+     * @param name
+     * @throws IOException
+     */
+    public void freeCache( String name )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "freeCache [" + name + "]" );
+        }
+        ICache<?, ?> c = null;
+        synchronized ( caches )
+        {
+            c = caches.get( name );
+        }
+        if ( c != null )
+        {
+            this.removeRemoteCacheListener( name );
+            c.dispose();
+        }
+    }
+
+    /**
+     * Gets the stats attribute of the RemoteCacheManager object
+     * <p>
+     * @return The stats value
+     */
+    public String getStats()
+    {
+        StringBuilder stats = new StringBuilder();
+        for (RemoteCacheNoWait<?, ?> c : caches.values())
+        {
+            if ( c != null )
+            {
+                stats.append( c.getCacheName() );
+            }
+        }
+        return stats.toString();
+    }
+
+    /** Shutdown all. */
+    public void release()
+    {
+        // Wait until called by the last client
+        if ( --clients != 0 )
+        {
+            return;
+        }
+        synchronized ( caches )
+        {
+            for (RemoteCacheNoWait<?, ?> c : caches.values())
+            {
+                if ( c != null )
+                {
+                    try
+                    {
+                        // c.dispose();
+                        freeCache( c.getCacheName() );
+                    }
+                    catch ( IOException ex )
+                    {
+                        log.error( "Problem in release.", ex );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Fixes up all the caches managed by this cache manager.
+     * <p>
+     * @param remoteService
+     * @param remoteWatch
+     */
+    public void fixCaches( ICacheServiceNonLocal<?, ?> remoteService, IRemoteCacheObserver remoteWatch )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Fixing caches. ICacheServiceNonLocal " + remoteService + " | IRemoteCacheObserver " + remoteWatch );
+        }
+        synchronized ( caches )
+        {
+            this.remoteService = remoteService;
+            this.remoteWatch.setCacheWatch( remoteWatch );
+            for (RemoteCacheNoWait<?, ?> c : caches.values())
+            {
+                c.fixCache( remoteService );
+            }
+        }
+    }
+
+    /**
+     * Location of the RMI registry.
+     */
+    private static final class Location
+    {
+        /** Description of the Field */
+        public final String host;
+
+        /** Description of the Field */
+        public final int port;
+
+        /**
+         * Constructor for the Location object
+         * <p>
+         * @param host
+         * @param port
+         */
+        public Location( String host, int port )
+        {
+            this.host = host;
+            this.port = port;
+        }
+
+        /**
+         * @param obj
+         * @return true if the host and port are equal
+         */
+        @Override
+        public boolean equals( Object obj )
+        {
+            if ( obj == this )
+            {
+                return true;
+            }
+            if ( obj == null || !( obj instanceof Location ) )
+            {
+                return false;
+            }
+            Location l = (Location) obj;
+            if ( this.host == null )
+            {
+                return l.host == null && port == l.port;
+            }
+            return host.equals( l.host ) && port == l.port;
+        }
+
+        /**
+         * @return int
+         */
+        @Override
+        public int hashCode()
+        {
+            return host == null ? port : host.hashCode() ^ port;
+        }
+    }
+
+    /**
+     * Shutdown callback from composite cache manager.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.IShutdownObserver#shutdown()
+     */
+    @Override
+    public void shutdown()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Observed shutdown request." );
+        }
+        release();
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheMonitor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheMonitor.java
new file mode 100644
index 0000000..cb6e6b5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheMonitor.java
@@ -0,0 +1,224 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Used to monitor and repair any failed connection for the remote cache service. By default the
+ * monitor operates in a failure driven mode. That is, it goes into a wait state until there is an
+ * error. TODO consider moving this into an active monitoring mode. Upon the notification of a
+ * connection error, the monitor changes to operate in a time driven mode. That is, it attempts to
+ * recover the connections on a periodic basis. When all failed connections are restored, it changes
+ * back to the failure driven mode.
+ */
+public class RemoteCacheMonitor
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheMonitor.class );
+
+    /** The remote cache that we are monitoring */
+    private static RemoteCacheMonitor instance;
+
+    /** Time between checks */
+    private static long idlePeriod = 30 * 1000;
+
+    // minimum 30 seconds.
+    //private static long idlePeriod = 3*1000; // for debugging.
+
+    /**
+     * Must make sure RemoteCacheMonitor is started before any remote error can be detected!
+     */
+    private boolean alright = true;
+
+    /** Time driven mode */
+    static final int TIME = 0;
+
+    /** Error driven mode -- only check on health if there is an error */
+    static final int ERROR = 1;
+
+    /** The mode to use */
+    static int mode = ERROR;
+
+    /**
+     * Configures the idle period between repairs.
+     * <p>
+     * @param idlePeriod The new idlePeriod value
+     */
+    public static void setIdlePeriod( long idlePeriod )
+    {
+        if ( idlePeriod > RemoteCacheMonitor.idlePeriod )
+        {
+            RemoteCacheMonitor.idlePeriod = idlePeriod;
+        }
+    }
+
+    /** Constructor for the RemoteCacheMonitor object */
+    private RemoteCacheMonitor()
+    {
+        super();
+    }
+
+    /**
+     * Returns the singleton instance.
+     * <p>
+     * @return The instance value
+     */
+    static RemoteCacheMonitor getInstance()
+    {
+        synchronized ( RemoteCacheMonitor.class )
+        {
+            if ( instance == null )
+            {
+                return instance = new RemoteCacheMonitor();
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * Notifies the cache monitor that an error occurred, and kicks off the error recovery process.
+     */
+    public void notifyError()
+    {
+        log.debug( "Notified of an error." );
+        bad();
+        synchronized ( this )
+        {
+            notify();
+        }
+    }
+
+    // Run forever.
+
+    // Avoid the use of any synchronization in the process of monitoring for
+    // performance reason.
+    // If exception is thrown owing to synchronization,
+    // just skip the monitoring until the next round.
+    /** Main processing method for the RemoteCacheMonitor object */
+    @Override
+    public void run()
+    {
+        log.debug( "Monitoring daemon started" );
+        do
+        {
+            if ( mode == ERROR )
+            {
+                synchronized ( this )
+                {
+                    if ( alright )
+                    {
+                        // make this configurable, comment out wait to enter
+                        // time driven mode
+                        // Failure driven mode.
+                        try
+                        {
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "FAILURE DRIVEN MODE: cache monitor waiting for error" );
+                            }
+                            wait();
+                            // wake up only if there is an error.
+                        }
+                        catch ( InterruptedException ignore )
+                        {
+                            // swallow
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "TIME DRIVEN MODE: cache monitor sleeping for " + idlePeriod );
+                }
+                // Time driven mode: sleep between each round of recovery
+                // attempt.
+                // will need to test not just check status
+            }
+
+            try
+            {
+                Thread.sleep( idlePeriod );
+            }
+            catch ( InterruptedException ex )
+            {
+                // ignore;
+            }
+
+            // The "alright" flag must be false here.
+            // Simply presume we can fix all the errors until proven otherwise.
+            synchronized ( this )
+            {
+                alright = true;
+            }
+            //p("cache monitor running.");
+            // Monitor each RemoteCacheManager instance one after the other.
+            // Each RemoteCacheManager corresponds to one remote connection.
+            for (RemoteCacheManager mgr : RemoteCacheManager.instances.values())
+            {
+                try
+                {
+                    // If any cache is in error, it strongly suggests all caches
+                    // managed by the
+                    // same RmicCacheManager instance are in error. So we fix
+                    // them once and for all.
+                    for (RemoteCacheNoWait<?, ?> c : mgr.caches.values())
+                    {
+                        if ( c.getStatus() == CacheStatus.ERROR )
+                        {
+                            RemoteCacheRestore repairer = new RemoteCacheRestore( mgr );
+                            // If we can't fix them, just skip and re-try in
+                            // the next round.
+                            if ( repairer.canFix() )
+                            {
+                                repairer.fix();
+                            }
+                            else
+                            {
+                                bad();
+                            }
+                            break;
+                        }
+                    }
+                }
+                catch ( Exception ex )
+                {
+                    bad();
+                    // Problem encountered in fixing the caches managed by a
+                    // RemoteCacheManager instance.
+                    // Soldier on to the next RemoteCacheManager instance.
+                    log.error( "Problem fixing caches for manager." + mgr, ex );
+                }
+            }
+        }
+        while ( true );
+    }
+
+    /** Sets the "aright" flag to false in a critical section. */
+    private synchronized void bad()
+    {
+        alright = false;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWait.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWait.java
new file mode 100644
index 0000000..33e9f5a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWait.java
@@ -0,0 +1,529 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
+import org.apache.commons.jcs.engine.CacheAdaptor;
+import org.apache.commons.jcs.engine.CacheEventQueueFactory;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.rmi.UnmarshalException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The RemoteCacheNoWait wraps the RemoteCacheClient. The client holds a handle on the
+ * RemoteCacheService.
+ * <p>
+ * Used to queue up update requests to the underlying cache. These requests will be processed in
+ * their order of arrival via the cache event queue processor.
+ * <p>
+ * Typically errors will be handled down stream. We only need to kill the queue if an error makes it
+ * to this level from the queue. That can only happen if the queue is damaged, since the events are
+ * Processed asynchronously.
+ * <p>
+ * There is no reason to create a queue on startup if the remote is not healthy.
+ * <p>
+ * If the remote cache encounters an error it will zombie--create a balking facade for the service.
+ * The Zombie will queue up items until the connection is restored. An alternative way to accomplish
+ * the same thing would be to stop, not destroy the queue at this level. That way items would be
+ * added to the queue and then when the connection is restored, we could start the worker threads
+ * again. This is a better long term solution, but it requires some significant changes to the
+ * complicated worker queues.
+ */
+public class RemoteCacheNoWait<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( RemoteCacheNoWait.class );
+
+    /** The remote cache client */
+    private final IRemoteCacheClient<K, V> remoteCacheClient;
+
+    /** Event queue for queuing up calls like put and remove. */
+    private ICacheEventQueue<K, V> cacheEventQueue;
+
+    /** how many times get has been called. */
+    private int getCount = 0;
+
+    /** how many times getMatching has been called. */
+    private int getMatchingCount = 0;
+
+    /** how many times getMultiple has been called. */
+    private int getMultipleCount = 0;
+
+    /** how many times remove has been called. */
+    private int removeCount = 0;
+
+    /** how many times put has been called. */
+    private int putCount = 0;
+
+    /**
+     * Constructs with the given remote cache, and fires up an event queue for asynchronous
+     * processing.
+     * <p>
+     * @param cache
+     */
+    public RemoteCacheNoWait( IRemoteCacheClient<K, V> cache )
+    {
+        remoteCacheClient = cache;
+
+        CacheEventQueueFactory<K, V> factory = new CacheEventQueueFactory<K, V>();
+        this.cacheEventQueue = factory.createCacheEventQueue( new CacheAdaptor<K, V>( remoteCacheClient ), remoteCacheClient
+            .getListenerId(), remoteCacheClient.getCacheName(), remoteCacheClient.getAuxiliaryCacheAttributes()
+            .getEventQueuePoolName(), remoteCacheClient.getAuxiliaryCacheAttributes().getEventQueueType() );
+
+        if ( remoteCacheClient.getStatus() == CacheStatus.ERROR )
+        {
+            cacheEventQueue.destroy();
+        }
+    }
+
+    /**
+     * Adds a put event to the queue.
+     * <p>
+     * @param element
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> element )
+        throws IOException
+    {
+        putCount++;
+        try
+        {
+            cacheEventQueue.addPutEvent( element );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem adding putEvent to queue.", e );
+            cacheEventQueue.destroy();
+            throw e;
+        }
+    }
+
+    /**
+     * Synchronously reads from the remote cache.
+     * <p>
+     * @param key
+     * @return element from the remote cache, or null if not present
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        getCount++;
+        try
+        {
+            return remoteCacheClient.get( key );
+        }
+        catch ( UnmarshalException ue )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Retrying the get owing to UnmarshalException." );
+            }
+
+            try
+            {
+                return remoteCacheClient.get( key );
+            }
+            catch ( IOException ex )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Failed in retrying the get for the second time. " + ex.getMessage() );
+                }
+            }
+        }
+        catch ( IOException ex )
+        {
+            // We don't want to destroy the queue on a get failure.
+            // The RemoteCache will Zombie and queue.
+            // Since get does not use the queue, I don't want to kill the queue.
+            throw ex;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param pattern
+     * @return Map
+     * @throws IOException
+     *
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
+        throws IOException
+    {
+        getMatchingCount++;
+        try
+        {
+            return remoteCacheClient.getMatching( pattern );
+        }
+        catch ( UnmarshalException ue )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Retrying the getMatching owing to UnmarshalException." );
+            }
+
+            try
+            {
+                return remoteCacheClient.getMatching( pattern );
+            }
+            catch ( IOException ex )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Failed in retrying the getMatching for the second time. " + ex.getMessage() );
+                }
+            }
+        }
+        catch ( IOException ex )
+        {
+            // We don't want to destroy the queue on a get failure.
+            // The RemoteCache will Zombie and queue.
+            // Since get does not use the queue, I don't want to kill the queue.
+            throw ex;
+        }
+
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys. Sends the getMultiple
+     * request on to the server rather than looping through the requested keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
+        throws IOException
+    {
+        getMultipleCount++;
+        try
+        {
+            return remoteCacheClient.getMultiple( keys );
+        }
+        catch ( UnmarshalException ue )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Retrying the getMultiple owing to UnmarshalException..." );
+            }
+
+            try
+            {
+                return remoteCacheClient.getMultiple( keys );
+            }
+            catch ( IOException ex )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Failed in retrying the getMultiple for the second time. " + ex.getMessage() );
+                }
+            }
+        }
+        catch ( IOException ex )
+        {
+            // We don't want to destroy the queue on a get failure.
+            // The RemoteCache will Zombie and queue.
+            // Since get does not use the queue, I don't want to kill the queue.
+            throw ex;
+        }
+
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        return remoteCacheClient.getKeySet();
+    }
+
+    /**
+     * Adds a remove request to the remote cache.
+     * <p>
+     * @param key
+     * @return if this was successful
+     * @throws IOException
+     */
+    @Override
+    public boolean remove( K key )
+        throws IOException
+    {
+        removeCount++;
+        try
+        {
+            cacheEventQueue.addRemoveEvent( key );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem adding RemoveEvent to queue.", e );
+            cacheEventQueue.destroy();
+            throw e;
+        }
+        return false;
+    }
+
+    /**
+     * Adds a removeAll request to the remote cache.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        try
+        {
+            cacheEventQueue.addRemoveAllEvent();
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem adding RemoveAllEvent to queue.", e );
+            cacheEventQueue.destroy();
+            throw e;
+        }
+    }
+
+    /** Adds a dispose request to the remote cache. */
+    @Override
+    public void dispose()
+    {
+        try
+        {
+            cacheEventQueue.addDisposeEvent();
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem adding DisposeEvent to queue.", e );
+            cacheEventQueue.destroy();
+        }
+    }
+
+    /**
+     * No remote invocation.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return remoteCacheClient.getSize();
+    }
+
+    /**
+     * No remote invocation.
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.REMOTE_CACHE;
+    }
+
+    /**
+     * Returns the asyn cache status. An error status indicates either the remote connection is not
+     * available, or the asyn queue has been unexpectedly destroyed. No remote invocation.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return cacheEventQueue.isWorking() ? remoteCacheClient.getStatus() : CacheStatus.ERROR;
+    }
+
+    /**
+     * Gets the cacheName attribute of the RemoteCacheNoWait object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return remoteCacheClient.getCacheName();
+    }
+
+    /**
+     * Replaces the remote cache service handle with the given handle and reset the event queue by
+     * starting up a new instance.
+     * <p>
+     * @param remote
+     */
+    public void fixCache( ICacheServiceNonLocal<?, ?> remote )
+    {
+        remoteCacheClient.fixCache( remote );
+        resetEventQ();
+    }
+
+    /**
+     * Resets the event q by first destroying the existing one and starting up new one.
+     * <p>
+     * There may be no good reason to kill the existing queue. We will sometimes need to set a new
+     * listener id, so we should create a new queue. We should let the old queue drain. If we were
+     * Connected to the failover, it would be best to finish sending items.
+     */
+    public void resetEventQ()
+    {
+        ICacheEventQueue<K, V> previousQueue = cacheEventQueue;
+
+        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
+        this.cacheEventQueue = fact.createCacheEventQueue( new CacheAdaptor<K, V>( remoteCacheClient ), remoteCacheClient
+            .getListenerId(), remoteCacheClient.getCacheName(), remoteCacheClient.getAuxiliaryCacheAttributes()
+            .getEventQueuePoolName(), remoteCacheClient.getAuxiliaryCacheAttributes().getEventQueueType() );
+
+        if ( previousQueue.isWorking() )
+        {
+            // we don't expect anything, it would have all gone to the zombie
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "resetEventQ, previous queue has [" + previousQueue.size() + "] items queued up." );
+            }
+            previousQueue.destroy();
+        }
+    }
+
+    /**
+     * This is temporary. It allows the manager to get the lister.
+     * <p>
+     * @return the instance of the remote cache client used by this object
+     */
+    protected IRemoteCacheClient<K, V> getRemoteCache()
+    {
+        return remoteCacheClient;
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return remoteCacheClient.getAuxiliaryCacheAttributes();
+    }
+
+    /**
+     * This is for testing only. It allows you to take a look at the event queue.
+     * <p>
+     * @return ICacheEventQueue
+     */
+    protected ICacheEventQueue<K, V> getCacheEventQueue()
+    {
+        return this.cacheEventQueue;
+    }
+
+    /**
+     * Returns the stats and the cache.toString().
+     * <p>
+     * (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString()
+    {
+        return getStats() + "\n" + remoteCacheClient.toString();
+    }
+
+    /**
+     * Returns the statistics in String form.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * @return statistics about this communication
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Remote Cache No Wait" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<CacheStatus>( "Status", getStatus() ) );
+
+        // get the stats from the cache queue too
+        IStats cStats = this.remoteCacheClient.getStatistics();
+        if ( cStats != null )
+        {
+            elems.addAll(cStats.getStatElements());
+        }
+
+        // get the stats from the event queue too
+        IStats eqStats = this.cacheEventQueue.getStatistics();
+        elems.addAll(eqStats.getStatElements());
+
+        elems.add(new StatElement<Integer>( "Get Count", Integer.valueOf(this.getCount) ) );
+        elems.add(new StatElement<Integer>( "GetMatching Count", Integer.valueOf(this.getMatchingCount) ) );
+        elems.add(new StatElement<Integer>( "GetMultiple Count", Integer.valueOf(this.getMultipleCount) ) );
+        elems.add(new StatElement<Integer>( "Remove Count", Integer.valueOf(this.removeCount) ) );
+        elems.add(new StatElement<Integer>( "Put Count", Integer.valueOf(this.putCount) ) );
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * this won't be called since we don't do ICache logging here.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return "Remote Cache No Wait";
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitFacade.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitFacade.java
new file mode 100644
index 0000000..74c71e7
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitFacade.java
@@ -0,0 +1,102 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Used to provide access to multiple services under nowait protection. Factory should construct
+ * NoWaitFacade to give to the composite cache out of caches it constructs from the varies manager
+ * to lateral services.
+ * <p>
+ * Typically, we only connect to one remote server per facade. We use a list of one
+ * RemoteCacheNoWait.
+ */
+public class RemoteCacheNoWaitFacade<K, V>
+    extends AbstractRemoteCacheNoWaitFacade<K, V>
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( RemoteCacheNoWaitFacade.class );
+
+    /**
+     * Constructs with the given remote cache, and fires events to any listeners.
+     * <p>
+     * @param noWaits
+     * @param rca
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    public RemoteCacheNoWaitFacade( RemoteCacheNoWait<K, V>[] noWaits, RemoteCacheAttributes rca,
+                                    ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger,
+                                    IElementSerializer elementSerializer )
+    {
+        super( noWaits, rca, cacheMgr, cacheEventLogger, elementSerializer );
+    }
+
+    /**
+     * Begin the failover process if this is a local cache. Clustered remote caches do not failover.
+     * <p>
+     * @param i The no wait in error.
+     */
+    @Override
+    protected void failover( int i )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.info( "in failover for " + i );
+        }
+
+        if ( getRemoteCacheAttributes().getRemoteType() == RemoteType.LOCAL )
+        {
+            if ( noWaits[i].getStatus() == CacheStatus.ERROR )
+            {
+                // start failover, primary recovery process
+                RemoteCacheFailoverRunner<K, V> runner =
+                    new RemoteCacheFailoverRunner<K, V>( this, getCompositeCacheManager(),
+                      super.getCacheEventLogger(), super.getElementSerializer() );
+                runner.notifyError();
+                Thread t = new Thread( runner );
+                t.setDaemon( true );
+                t.start();
+
+                if ( getCacheEventLogger() != null )
+                {
+                    getCacheEventLogger().logApplicationEvent( "RemoteCacheNoWaitFacade", "InitiatedFailover",
+                                                               noWaits[i] + " was in error." );
+                }
+            }
+            else
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "The noWait is not in error" );
+                }
+            }
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheRestore.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheRestore.java
new file mode 100644
index 0000000..e32f7eb
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheRestore.java
@@ -0,0 +1,127 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
+import org.apache.commons.jcs.engine.behavior.ICacheRestore;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.rmi.Naming;
+import java.rmi.NotBoundException;
+
+/**
+ * Used to repair the remote caches managed by the associated instance of RemoteCacheManager.
+ * <p>
+ * When there is an error the monitor kicks off. The Failover runner starts looks for a manager with
+ * a connection to a remote cache that is not in error. If a manager's connection to a remote cache
+ * is found to be in error, the restorer kicks off and tries to reconnect. When it is successful, the
+ * status of the manager changes.
+ * <p>
+ * When the failover runner finds that the primary is in good shape, it will switch back. Switching
+ * back involves setting the first no wait on the no wait facade.
+ */
+public class RemoteCacheRestore
+    implements ICacheRestore
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheRestore.class );
+
+    /** The manager */
+    private final RemoteCacheManager remoteCacheManager;
+
+    /** can it be restored */
+    private boolean canFix = true;
+
+    /** The remote handle */
+    private Object remoteObj;
+
+    /**
+     * Constructs with the given instance of RemoteCacheManager.
+     * <p>
+     * @param rcm
+     */
+    public RemoteCacheRestore( RemoteCacheManager rcm )
+    {
+        this.remoteCacheManager = rcm;
+    }
+
+    /**
+     * Returns true if the connection to the remote host for the corresponding cache manager can be
+     * successfully re-established.
+     * <p>
+     * @return true if we found a failover server
+     */
+    @Override
+    public boolean canFix()
+    {
+        if ( !canFix )
+        {
+            return canFix;
+        }
+        String registry = "//" + remoteCacheManager.host + ":" + remoteCacheManager.port + "/" + remoteCacheManager.service;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "looking up server [" + registry + "]" );
+        }
+        try
+        {
+            remoteObj = Naming.lookup( registry );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Found server " + remoteObj );
+            }
+        }
+        catch (IOException e)
+        {
+            log.error( "host=" + remoteCacheManager.host + "; port" + remoteCacheManager.port + "; service=" + remoteCacheManager.service );
+            canFix = false;
+        }
+        catch (NotBoundException e)
+        {
+            log.error( "host=" + remoteCacheManager.host + "; port" + remoteCacheManager.port + "; service=" + remoteCacheManager.service );
+            canFix = false;
+        }
+
+        return canFix;
+    }
+
+    /**
+     * Fixes up all the caches managed by the associated cache manager.
+     */
+    @Override
+    public void fix()
+    {
+        if ( !canFix )
+        {
+            return;
+        }
+        remoteCacheManager.fixCaches( (ICacheServiceNonLocal<?, ?>) remoteObj, (IRemoteCacheObserver) remoteObj );
+
+        if ( log.isInfoEnabled() )
+        {
+            String msg = "Remote connection to " + "//" + remoteCacheManager.host + ":" + remoteCacheManager.port + "/" + remoteCacheManager.service + " resumed.";
+            remoteCacheManager.logApplicationEvent( "RemoteCacheRestore", "fix", msg );
+            log.info( msg );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheWatchRepairable.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheWatchRepairable.java
new file mode 100644
index 0000000..a27574e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheWatchRepairable.java
@@ -0,0 +1,33 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
+import org.apache.commons.jcs.engine.CacheWatchRepairable;
+
+/**
+ * Same as CacheWatcherWrapper but implements the IRemoteCacheWatch interface.
+ */
+public class RemoteCacheWatchRepairable
+    extends CacheWatchRepairable
+    implements IRemoteCacheObserver
+{
+    // nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtils.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtils.java
new file mode 100644
index 0000000..1962840
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtils.java
@@ -0,0 +1,224 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMISocketFactory;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * This class provides some basic utilities for doing things such as starting the registry properly.
+ */
+public class RemoteUtils
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( RemoteUtils.class );
+
+    /** No instances please. */
+    private RemoteUtils()
+    {
+        super();
+    }
+
+    /**
+     * Creates and exports a registry on the specified port of the local host.
+     * <p>
+     * @param port
+     * @return the registry
+     */
+    public static Registry createRegistry( int port )
+    {
+    	Registry registry = null;
+
+//        if ( log.isInfoEnabled() )
+//        {
+//            log.info( "createRegistry> Setting security manager" );
+//        }
+//
+//        System.setSecurityManager( new RMISecurityManager() );
+
+        if ( port < 1024 )
+        {
+            if ( log.isWarnEnabled() )
+            {
+                log.warn( "createRegistry> Port chosen was less than 1024, will use default [" + Registry.REGISTRY_PORT + "] instead." );
+            }
+            port = Registry.REGISTRY_PORT;
+        }
+
+        try
+        {
+            registry = LocateRegistry.createRegistry( port );
+            log.info("createRegistry> Created the registry on port " + port);
+        }
+        catch ( RemoteException e )
+        {
+            log.warn( "createRegistry> Problem creating registry. It may already be started. " + e.getMessage() );
+        }
+        catch ( Throwable t )
+        {
+            log.error( "createRegistry> Problem creating registry.", t );
+        }
+
+        if (registry == null)
+        {
+        	try
+        	{
+            	registry = LocateRegistry.getRegistry( port );
+			}
+        	catch (RemoteException e)
+        	{
+                log.error( "createRegistry> Problem getting a registry reference.", e );
+			}
+        }
+
+        return registry;
+    }
+
+    /**
+     * Loads properties for the named props file.
+     * <p>
+     * @param propFile
+     * @return The properties object for the file
+     * @throws IOException
+     */
+    public static Properties loadProps( String propFile )
+        throws IOException
+    {
+        InputStream is = RemoteUtils.class.getResourceAsStream( propFile );
+        Properties props = new Properties();
+        try
+        {
+            props.load( is );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "props.size=" + props.size() );
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                Enumeration<Object> en = props.keys();
+                StringBuilder buf = new StringBuilder();
+                while ( en.hasMoreElements() )
+                {
+                    String key = (String) en.nextElement();
+                    buf.append( "\n" + key + " = " + props.getProperty( key ) );
+                }
+                log.debug( buf.toString() );
+            }
+
+        }
+        catch ( Exception ex )
+        {
+            log.error( "Error loading remote properties, for file name [" + propFile + "]", ex );
+        }
+        finally
+        {
+            if ( is != null )
+            {
+                is.close();
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Configure a custom socket factory to set the timeout value. This sets the global socket
+     * factory. It's used only if a custom factory is not configured for the specific object.
+     * <p>
+     * @param timeoutMillis
+     */
+    public static void configureGlobalCustomSocketFactory( final int timeoutMillis )
+    {
+        try
+        {
+            // Don't set a socket factory if the setting is -1
+            if ( timeoutMillis > 0 )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "RmiSocketFactoryTimeoutMillis [" + timeoutMillis + "]. "
+                        + " Configuring a custom socket factory." );
+                }
+
+                // use this socket factory to add a timeout.
+                RMISocketFactory.setSocketFactory( new RMISocketFactory()
+                {
+                    @Override
+                    public Socket createSocket( String host, int port )
+                        throws IOException
+                    {
+                        Socket socket = new Socket();
+                        socket.setSoTimeout( timeoutMillis );
+                        socket.setSoLinger( false, 0 );
+                        socket.connect( new InetSocketAddress( host, port ), timeoutMillis );
+                        return socket;
+                    }
+
+                    @Override
+                    public ServerSocket createServerSocket( int port )
+                        throws IOException
+                    {
+                        return new ServerSocket( port );
+                    }
+                } );
+            }
+        }
+        catch ( Exception e )
+        {
+            // Only try to do it once. Otherwise we
+            // Generate errors for each region on construction.
+            RMISocketFactory factoryInUse = RMISocketFactory.getSocketFactory();
+            if ( factoryInUse != null && !factoryInUse.getClass().getName().startsWith( "org.apache.commons.jcs" ) )
+            {
+                log.info( "Could not create new custom socket factory. " + e.getMessage() + " Factory in use = "
+                    + RMISocketFactory.getSocketFactory() );
+            }
+        }
+    }
+
+    /**
+     * Get the naming url used for RMI registration
+     *
+     * @param registryHost
+     * @param registryPort
+     * @param serviceName
+     * @return
+     */
+    public static String getNamingURL(final String registryHost, final int registryPort, final String serviceName)
+    {
+        if (registryHost.contains(":")) { // TODO improve this check? See also JCS-133
+            return "//[" + registryHost.replaceFirst("%", "%25") + "]:" + registryPort + "/" + serviceName;
+        }
+        final String registryURL = "//" + registryHost + ":" + registryPort + "/" + serviceName;
+        return registryURL;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheWatch.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheWatch.java
new file mode 100644
index 0000000..92c8443
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheWatch.java
@@ -0,0 +1,34 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
+import org.apache.commons.jcs.engine.ZombieCacheWatch;
+
+/**
+ * Description of the Class
+ *
+ */
+public class ZombieRemoteCacheWatch
+    extends ZombieCacheWatch
+    implements IRemoteCacheObserver
+{
+    // nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/ICommonRemoteCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/ICommonRemoteCacheAttributes.java
new file mode 100644
index 0000000..61e8bb8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/ICommonRemoteCacheAttributes.java
@@ -0,0 +1,177 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+
+/**
+ * This specifies what a remote cache configuration object should look like.
+ */
+public interface ICommonRemoteCacheAttributes
+    extends AuxiliaryCacheAttributes
+{
+    /** The default timeout for the custom RMI socket factory */
+    int DEFAULT_RMI_SOCKET_FACTORY_TIMEOUT_MILLIS = 10000;
+
+    /**
+     * Gets the remoteTypeName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The remoteTypeName value
+     */
+    String getRemoteTypeName();
+
+    /**
+     * Sets the remoteTypeName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new remoteTypeName value
+     */
+    void setRemoteTypeName( String s );
+
+    /**
+     * Gets the remoteType attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The remoteType value
+     */
+    RemoteType getRemoteType();
+
+    /**
+     * Sets the remoteType attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param p The new remoteType value
+     */
+    void setRemoteType( RemoteType p );
+
+    /**
+     * Gets the remoteServiceName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The remoteServiceName value
+     */
+    String getRemoteServiceName();
+
+    /**
+     * Sets the remoteServiceName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new remoteServiceName value
+     */
+    void setRemoteServiceName( String s );
+
+    /**
+     * Gets the remoteHost attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The remoteHost value
+     */
+    String getRemoteHost();
+
+    /**
+     * Sets the remoteHost attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new remoteHost value
+     */
+    void setRemoteHost( String s );
+
+    /**
+     * Gets the remotePort attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The remotePort value
+     */
+    int getRemotePort();
+
+    /**
+     * Sets the remotePort attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param p The new remotePort value
+     */
+    void setRemotePort( int p );
+
+    /**
+     * Gets the clusterServers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The clusterServers value
+     */
+    String getClusterServers();
+
+    /**
+     * Sets the clusterServers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new clusterServers value
+     */
+    void setClusterServers( String s );
+
+    /**
+     * Gets the removeUponRemotePut attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The removeUponRemotePut value
+     */
+    boolean getRemoveUponRemotePut();
+
+    /**
+     * Sets the removeUponRemotePut attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param r The new removeUponRemotePut value
+     */
+    void setRemoveUponRemotePut( boolean r );
+
+    /**
+     * Gets the getOnly attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The getOnly value
+     */
+    boolean getGetOnly();
+
+    /**
+     * Sets the getOnly attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param r The new getOnly value
+     */
+    void setGetOnly( boolean r );
+
+    /**
+     * Should cluster updates be propagated to the locals
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    boolean isLocalClusterConsistency();
+
+    /**
+     * Should cluster updates be propagated to the locals
+     * <p>
+     * @param r The new localClusterConsistency value
+     */
+    void setLocalClusterConsistency( boolean r );
+
+    /**
+     * This sets a general timeout on the rmi socket factory. By default the socket factory will
+     * block forever.
+     * <p>
+     * We have a default setting. The default rmi behavior should never be used.
+     * <p>
+     * @return int milliseconds
+     */
+    int getRmiSocketFactoryTimeoutMillis();
+
+    /**
+     * This sets a general timeout on the RMI socket factory. By default the socket factory will
+     * block forever.
+     * <p>
+     * @param rmiSocketFactoryTimeoutMillis
+     */
+    void setRmiSocketFactoryTimeoutMillis( int rmiSocketFactoryTimeoutMillis );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheAttributes.java
new file mode 100644
index 0000000..e9c2c4b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheAttributes.java
@@ -0,0 +1,178 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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 specifies what a remote cache configuration object should look like.
+ */
+public interface IRemoteCacheAttributes
+    extends ICommonRemoteCacheAttributes
+{
+    /**
+     * If RECEIVE is false then the remote cache will not register a listener with the remote
+     * server. This allows you to configure a remote server as a repository from which you can get
+     * and to which you put, but from which you do not receive any notifications. That is, you will
+     * not receive updates or removes.
+     * <p>
+     * If you set this option to false, you should set your local memory size to 0.
+     */
+    boolean DEFAULT_RECEIVE = true;
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     */
+    int DEFAULT_ZOMBIE_QUEUE_MAX_SIZE = 1000;
+
+    /**
+     * Gets the failoverIndex attribute of the IRemoteCacheAttributes object.
+     * <p>
+     * This specifies which server in the list we are listening to if the number is greater than 0
+     * we will try to move to 0 position the primary is added as position 1 if it is present
+     * <p>
+     * @return The failoverIndex value
+     */
+    int getFailoverIndex();
+
+    /**
+     * Sets the failoverIndex attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param p The new failoverIndex value
+     */
+    void setFailoverIndex( int p );
+
+    /**
+     * Gets the failovers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The failovers value
+     */
+    String[] getFailovers();
+
+    /**
+     * Sets the failovers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param f The new failovers value
+     */
+    void setFailovers( String[] f );
+
+    /**
+     * Gets the localPort attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The localPort value
+     */
+    int getLocalPort();
+
+    /**
+     * Sets the localPort attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param p The new localPort value
+     */
+    void setLocalPort( int p );
+
+    /**
+     * Gets the failoverServers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The failoverServers value
+     */
+    String getFailoverServers();
+
+    /**
+     * Sets the failoverServers attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new failoverServers value
+     */
+    void setFailoverServers( String s );
+
+    /**
+     * The thread pool the remote cache should use. At first this will only be for gets.
+     * <p>
+     * The default name is "remote_cache_client"
+     * <p>
+     * @return the name of the pool
+     */
+    String getThreadPoolName();
+
+    /**
+     * Set the name of the pool to use. Pools should be defined in the cache.ccf.
+     * <p>
+     * @param name
+     */
+    void setThreadPoolName( String name );
+
+    /**
+     * -1 and 0 mean no timeout, this is the default if the timeout is -1 or 0, no threadpool will
+     * be used.
+     * <p>
+     * @return the time in millis
+     */
+    int getGetTimeoutMillis();
+
+    /**
+     * -1 means no timeout, this is the default if the timeout is -1 or 0, no threadpool will be
+     * used. If the timeout is greater than 0 a threadpool will be used for get requests.
+     * <p>
+     * @param millis
+     */
+    void setGetTimeoutMillis( int millis );
+
+    /**
+     * By default this option is true. If you set it to false, you will not receive updates or
+     * removes from the remote server.
+     * <p>
+     * @param receive
+     */
+    void setReceive( boolean receive );
+
+    /**
+     * If RECEIVE is false then the remote cache will not register a listener with the remote
+     * server. This allows you to configure a remote server as a repository from which you can get
+     * and to which you put, but from which you do not receive any notifications. That is, you will
+     * not receive updates or removes.
+     * <p>
+     * If you set this option to false, you should set your local memory size to 0.
+     * <p>
+     * The remote cache manager uses this value to decide whether or not to register a listener.
+     * <p>
+     * It makes no sense to configure a cluster remote cache to no receive.
+     * <p>
+     * Since a non-receiving remote cache client will not register a listener, it will not have a
+     * listener id assigned from the server. As such the remote server cannot determine if it is a
+     * cluster or a normal client. It will assume that it is a normal client.
+     * <p>
+     * @return the receive value.
+     */
+    boolean isReceive();
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @param zombieQueueMaxSize The zombieQueueMaxSize to set.
+     */
+    void setZombieQueueMaxSize( int zombieQueueMaxSize );
+
+    /**
+     * The number of elements the zombie queue will hold. This queue is used to store events if we
+     * loose our connection with the server.
+     * <p>
+     * @return Returns the zombieQueueMaxSize.
+     */
+    int getZombieQueueMaxSize();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheClient.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheClient.java
new file mode 100644
index 0000000..acb68f6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheClient.java
@@ -0,0 +1,61 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+/**
+ * This defines the behavior expected of a remote cache client. This extends Auxiliary cache which
+ * in turn extends ICache.
+ * <p>
+ * I'd like generalize this a bit.
+ * <p>
+ * @author Aaron Smuts
+ */
+public interface IRemoteCacheClient<K, V>
+    extends AuxiliaryCache<K, V>
+{
+    /**
+     * Replaces the current remote cache service handle with the given handle. If the current remote
+     * is a Zombie, the propagate the events that may be queued to the restored service.
+     * <p>
+     * @param remote ICacheServiceNonLocal -- the remote server or proxy to the remote server
+     */
+    void fixCache( ICacheServiceNonLocal<?, ?> remote );
+
+    /**
+     * Gets the listenerId attribute of the RemoteCacheListener object.
+     * <p>
+     * All requests to the remote cache must include a listener id. This allows the server to avoid
+     * sending updates the the listener associated with this client.
+     * <p>
+     * @return The listenerId value
+     */
+    long getListenerId();
+
+    /**
+     * This returns the listener associated with this remote cache. TODO we should try to get this
+     * out of the interface.
+     * <p>
+     * @return IRemoteCacheListener
+     */
+    IRemoteCacheListener<K, V> getListener();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheConstants.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheConstants.java
new file mode 100644
index 0000000..0dd06a9
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheConstants.java
@@ -0,0 +1,70 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+
+/**
+ * This holds constants that are used by the remote cache.
+ */
+public interface IRemoteCacheConstants
+{
+    /** Mapping to props file value */
+    String REMOTE_CACHE_SERVICE_VAL = ICacheServiceNonLocal.class.getName();
+
+    /** The prefix for cache server config. */
+    String CACHE_SERVER_PREFIX = "jcs.remotecache";
+
+    /**
+     * I'm trying to migrate everything to use this prefix. All those below will be replaced. Any of
+     * the RemoteCacheServerAttributes can be configured this way.
+     */
+    String CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX = CACHE_SERVER_PREFIX + ".serverattributes";
+
+    /**
+     * This is the name of the class that will be used for an object specific socket factory.
+     */
+    String CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX = CACHE_SERVER_PREFIX + ".customrmisocketfactory";
+
+    /** Property prefix, should be jcs.remote but this would break existing config. */
+    String PROPERTY_PREFIX = "remote";
+
+    /** Mapping to props file value */
+    String SOCKET_TIMEOUT_MILLIS = PROPERTY_PREFIX + ".cache.rmiSocketFactoryTimeoutMillis";
+
+    /** Mapping to props file value */
+    String REMOTE_CACHE_SERVICE_NAME = PROPERTY_PREFIX + ".cache.service.name";
+
+    /** Mapping to props file value */
+    String TOMCAT_XML = PROPERTY_PREFIX + ".tomcat.xml";
+
+    /** Mapping to props file value */
+    String TOMCAT_ON = PROPERTY_PREFIX + ".tomcat.on";
+
+    /** Mapping to props file value */
+    String REMOTE_CACHE_SERVICE_PORT = PROPERTY_PREFIX + ".cache.service.port";
+
+    /** Mapping to props file value */
+    String REMOTE_LOCAL_CLUSTER_CONSISTENCY = PROPERTY_PREFIX + ".cluster.LocalClusterConsistency";
+
+    /** Mapping to props file value */
+    String REMOTE_ALLOW_CLUSTER_GET = PROPERTY_PREFIX + ".cluster.AllowClusterGet";
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheDispatcher.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheDispatcher.java
new file mode 100644
index 0000000..c5d0b0d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheDispatcher.java
@@ -0,0 +1,46 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+
+import java.io.IOException;
+
+/**
+ * In the future, this can be used as a generic dispatcher abstraction.
+ * <p>
+ * At the time of creation, only the http remote cache uses it. The RMI remote could be converted to
+ * use it as well.
+ */
+public interface IRemoteCacheDispatcher
+{
+    /**
+     * All requests will go through this method. The dispatcher implementation will send the request
+     * remotely.
+     * <p>
+     * @param remoteCacheRequest
+     * @return RemoteCacheResponse
+     * @throws IOException
+     */
+    <K, V, T>
+        RemoteCacheResponse<T> dispatchRequest( RemoteCacheRequest<K, V> remoteCacheRequest )
+            throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheListener.java
new file mode 100644
index 0000000..b673383
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheListener.java
@@ -0,0 +1,81 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+
+import java.io.IOException;
+import java.rmi.Remote;
+
+/**
+ * Listens for remote cache event notification ( rmi callback ).
+ */
+public interface IRemoteCacheListener<K, V>
+    extends ICacheListener<K, V>, Remote
+{
+    /**
+     * Get the id to be used by this manager.
+     * <p>
+     * @return long
+     * @throws IOException
+     */
+    @Override
+    long getListenerId()
+        throws IOException;
+
+    /**
+     * Set the id to be used by this manager. The remote cache server identifies clients by this id.
+     * The value will be set by the server through the remote cache listener.
+     * <p>
+     * @param id
+     * @throws IOException
+     */
+    @Override
+    void setListenerId( long id )
+        throws IOException;
+
+    /**
+     * Gets the remoteType attribute of the IRemoteCacheListener object
+     * <p>
+     * @return The remoteType value
+     * @throws IOException
+     */
+    RemoteType getRemoteType()
+        throws IOException;
+
+    /**
+     * This is for debugging. It allows the remote cache server to log the address of any listeners
+     * that register.
+     * <p>
+     * @return the local host address.
+     * @throws IOException
+     */
+    String getLocalHostAddress()
+        throws IOException;
+
+    /**
+     * Deregisters itself.
+     * <p>
+     * @throws IOException
+     */
+    void dispose()
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheObserver.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheObserver.java
new file mode 100644
index 0000000..71324bd
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/behavior/IRemoteCacheObserver.java
@@ -0,0 +1,34 @@
+package org.apache.commons.jcs.auxiliary.remote.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheObserver;
+
+import java.rmi.Remote;
+
+/**
+ * Used to register interest in receiving remote cache changes.
+ *
+ */
+public interface IRemoteCacheObserver
+    extends Remote, ICacheObserver
+{
+    // nothing
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/behavior/IRemoteHttpCacheConstants.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/behavior/IRemoteHttpCacheConstants.java
new file mode 100644
index 0000000..49515f3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/behavior/IRemoteHttpCacheConstants.java
@@ -0,0 +1,31 @@
+package org.apache.commons.jcs.auxiliary.remote.http.behavior;
+
+/*
+ * 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.
+ */
+
+/** Constants used throughout the HTTP remote cache. */
+public interface IRemoteHttpCacheConstants
+{
+    /** The prefix for cache server config. */
+    String HTTP_CACHE_SERVER_PREFIX = "jcs.remotehttpcache";
+
+    /** All of the RemoteHttpCacheServiceAttributes can be configured this way. */
+    String HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX = HTTP_CACHE_SERVER_PREFIX
+        + ".serverattributes";
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/AbstractHttpClient.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/AbstractHttpClient.java
new file mode 100644
index 0000000..a68f31a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/AbstractHttpClient.java
@@ -0,0 +1,193 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.HttpVersion;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * This class simply configures the http multithreaded connection manager.
+ * <p>
+ * This is abstract because it can't do anything. Child classes can overwrite whatever they want.
+ */
+public abstract class AbstractHttpClient
+{
+    /** The connection manager. */
+    private MultiThreadedHttpConnectionManager connectionManager;
+
+    /** The client */
+    private HttpClient httpClient;
+
+    /** Configuration settings. */
+    private RemoteHttpCacheAttributes remoteHttpCacheAttributes;
+
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( AbstractHttpClient.class );
+
+    /**
+     * Sets the default Properties File and Heading, and creates the HttpClient and connection
+     * manager.
+     * <p>
+     * @param remoteHttpCacheAttributes
+     */
+    public AbstractHttpClient( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
+    {
+        setRemoteHttpCacheAttributes( remoteHttpCacheAttributes );
+        setConnectionManager( new MultiThreadedHttpConnectionManager() );
+
+        // THIS IS NOT THREAD SAFE:
+        // setHttpClient( new HttpClient() );
+        // THIS IS:
+        setHttpClient( new HttpClient( getConnectionManager() ) );
+
+        configureClient();
+    }
+
+    /**
+     * Configures the http client.
+     */
+    public void configureClient()
+    {
+        if ( getRemoteHttpCacheAttributes().getMaxConnectionsPerHost() > 0 )
+        {
+            getConnectionManager().getParams().setMaxTotalConnections(
+                                                                       getRemoteHttpCacheAttributes()
+                                                                           .getMaxConnectionsPerHost() );
+        }
+
+        getConnectionManager().getParams().setSoTimeout( getRemoteHttpCacheAttributes().getSocketTimeoutMillis() );
+
+        String httpVersion = getRemoteHttpCacheAttributes().getHttpVersion();
+        if ( httpVersion != null )
+        {
+            if ( "1.1".equals( httpVersion ) )
+            {
+                getHttpClient().getParams().setParameter( "http.protocol.version", HttpVersion.HTTP_1_1 );
+            }
+            else if ( "1.0".equals( httpVersion ) )
+            {
+                getHttpClient().getParams().setParameter( "http.protocol.version", HttpVersion.HTTP_1_0 );
+            }
+            else
+            {
+                log.warn( "Unrecognized value for 'httpVersion': [" + httpVersion + "]" );
+            }
+        }
+
+        getConnectionManager().getParams().setConnectionTimeout(
+                                                                 getRemoteHttpCacheAttributes()
+                                                                     .getConnectionTimeoutMillis() );
+
+        // By default we instruct HttpClient to ignore cookies.
+        String cookiePolicy = CookiePolicy.IGNORE_COOKIES;
+        getHttpClient().getParams().setCookiePolicy( cookiePolicy );
+    }
+
+    /**
+     * Extracted method that can be overwritten to do additional things to the post before the call
+     * is made.
+     * <p>
+     * @param post the post that is about to get executed.
+     * @throws IOException on i/o error
+     */
+    protected final void doWebserviceCall( HttpMethod post )
+        throws IOException
+    {
+        HttpState httpState = preProcessWebserviceCall( post );
+        getHttpClient().executeMethod( null, post, httpState );
+        postProcessWebserviceCall( post, httpState );
+    }
+
+    /**
+     * Called before the executeMethod on the client.
+     * <p>
+     * @param post http method
+     * @return HttpState
+     * @throws IOException
+     */
+    public abstract HttpState preProcessWebserviceCall( HttpMethod post )
+        throws IOException;
+
+    /**
+     * Called after the executeMethod on the client.
+     * <p>
+     * @param post http method
+     * @param httpState state
+     * @throws IOException
+     */
+    public abstract void postProcessWebserviceCall( HttpMethod post, HttpState httpState )
+        throws IOException;
+
+    /**
+     * @return Returns the httpClient.
+     */
+    private HttpClient getHttpClient()
+    {
+        return httpClient;
+    }
+
+    /**
+     * @param httpClient The httpClient to set.
+     */
+    private void setHttpClient( HttpClient httpClient )
+    {
+        this.httpClient = httpClient;
+    }
+
+    /**
+     * @return Returns the connectionManager.
+     */
+    public MultiThreadedHttpConnectionManager getConnectionManager()
+    {
+        return connectionManager;
+    }
+
+    /**
+     * @param connectionManager The connectionManager to set.
+     */
+    public void setConnectionManager( MultiThreadedHttpConnectionManager connectionManager )
+    {
+        this.connectionManager = connectionManager;
+    }
+
+    /**
+     * @param remoteHttpCacheAttributes the remoteHttpCacheAttributes to set
+     */
+    public void setRemoteHttpCacheAttributes( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
+    {
+        this.remoteHttpCacheAttributes = remoteHttpCacheAttributes;
+    }
+
+    /**
+     * @return the remoteHttpCacheAttributes
+     */
+    public RemoteHttpCacheAttributes getRemoteHttpCacheAttributes()
+    {
+        return remoteHttpCacheAttributes;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCache.java
new file mode 100644
index 0000000..f156fab
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCache.java
@@ -0,0 +1,116 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.AbstractRemoteAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * This uses an http client as the service.
+ */
+public class RemoteHttpCache<K, V>
+    extends AbstractRemoteAuxiliaryCache<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( RemoteHttpCache.class );
+
+    /** Keep the child copy here for the restore process. */
+    private RemoteHttpCacheAttributes remoteHttpCacheAttributes;
+
+    /**
+     * Constructor for the RemoteCache object. This object communicates with a remote cache server.
+     * One of these exists for each region. This also holds a reference to a listener. The same
+     * listener is used for all regions for one remote server. Holding a reference to the listener
+     * allows this object to know the listener id assigned by the remote cache.
+     * <p>
+     * @param remoteHttpCacheAttributes
+     * @param remote
+     * @param listener
+     */
+    public RemoteHttpCache( RemoteHttpCacheAttributes remoteHttpCacheAttributes, ICacheServiceNonLocal<K, V> remote,
+                            IRemoteCacheListener<K, V> listener )
+    {
+        super( remoteHttpCacheAttributes, remote, listener );
+
+        setRemoteHttpCacheAttributes( remoteHttpCacheAttributes );
+    }
+
+    /**
+     * Nothing right now. This should setup a zombie and initiate recovery.
+     * <p>
+     * @param ex
+     * @param msg
+     * @param eventName
+     * @throws IOException
+     */
+    @Override
+    protected void handleException( Exception ex, String msg, String eventName )
+        throws IOException
+    {
+        // we should not switch if the existing is a zombie.
+        if ( !( getRemoteCacheService() instanceof ZombieCacheServiceNonLocal ) )
+        {
+            String message = "Disabling remote cache due to error: " + msg;
+            logError( cacheName, "", message );
+            log.error( message, ex );
+
+            setRemoteCacheService( new ZombieCacheServiceNonLocal<K, V>( getRemoteCacheAttributes().getZombieQueueMaxSize() ) );
+
+            RemoteHttpCacheMonitor.getInstance().notifyError( this );
+        }
+
+        if ( ex instanceof IOException )
+        {
+            throw (IOException) ex;
+        }
+        throw new IOException( ex.getMessage() );
+    }
+
+    /**
+     * @return url of service
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return null;
+    }
+
+    /**
+     * @param remoteHttpCacheAttributes the remoteHttpCacheAttributes to set
+     */
+    public void setRemoteHttpCacheAttributes( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
+    {
+        this.remoteHttpCacheAttributes = remoteHttpCacheAttributes;
+    }
+
+    /**
+     * @return the remoteHttpCacheAttributes
+     */
+    public RemoteHttpCacheAttributes getRemoteHttpCacheAttributes()
+    {
+        return remoteHttpCacheAttributes;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheAttributes.java
new file mode 100644
index 0000000..c68670d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheAttributes.java
@@ -0,0 +1,228 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes;
+
+/** Http client specific settings. */
+public class RemoteHttpCacheAttributes
+    extends RemoteCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -5944327125140505212L;
+
+    /** http verison to use. */
+    private static final String DEFAULT_HTTP_VERSION = "1.1";
+
+    /** The max connections allowed per host */
+    private int maxConnectionsPerHost = 100;
+
+    /** The socket timeout. */
+    private int socketTimeoutMillis = 3000;
+
+    /** The socket connections timeout */
+    private int connectionTimeoutMillis = 5000;
+
+    /** http verison to use. */
+    private String httpVersion = DEFAULT_HTTP_VERSION;
+
+    /** The cache name will be included on the parameters */
+    private boolean includeCacheNameAsParameter = true;
+
+    /** keys and patterns will be included in the parameters */
+    private boolean includeKeysAndPatternsAsParameter = true;
+
+    /** keys and patterns will be included in the parameters */
+    private boolean includeRequestTypeasAsParameter = true;
+
+    /** The complete URL to the service. */
+    private String url;
+
+    /** The default classname for the client.  */
+    public static final String DEFAULT_REMOTE_HTTP_CLIENT_CLASS_NAME = RemoteHttpCacheClient.class.getName();
+
+    /** This allows users to inject their own client implementation. */
+    private String remoteHttpClientClassName = DEFAULT_REMOTE_HTTP_CLIENT_CLASS_NAME;
+
+    /**
+     * @param maxConnectionsPerHost the maxConnectionsPerHost to set
+     */
+    public void setMaxConnectionsPerHost( int maxConnectionsPerHost )
+    {
+        this.maxConnectionsPerHost = maxConnectionsPerHost;
+    }
+
+    /**
+     * @return the maxConnectionsPerHost
+     */
+    public int getMaxConnectionsPerHost()
+    {
+        return maxConnectionsPerHost;
+    }
+
+    /**
+     * @param socketTimeoutMillis the socketTimeoutMillis to set
+     */
+    public void setSocketTimeoutMillis( int socketTimeoutMillis )
+    {
+        this.socketTimeoutMillis = socketTimeoutMillis;
+    }
+
+    /**
+     * @return the socketTimeoutMillis
+     */
+    public int getSocketTimeoutMillis()
+    {
+        return socketTimeoutMillis;
+    }
+
+    /**
+     * @param httpVersion the httpVersion to set
+     */
+    public void setHttpVersion( String httpVersion )
+    {
+        this.httpVersion = httpVersion;
+    }
+
+    /**
+     * @return the httpVersion
+     */
+    public String getHttpVersion()
+    {
+        return httpVersion;
+    }
+
+    /**
+     * @param connectionTimeoutMillis the connectionTimeoutMillis to set
+     */
+    public void setConnectionTimeoutMillis( int connectionTimeoutMillis )
+    {
+        this.connectionTimeoutMillis = connectionTimeoutMillis;
+    }
+
+    /**
+     * @return the connectionTimeoutMillis
+     */
+    public int getConnectionTimeoutMillis()
+    {
+        return connectionTimeoutMillis;
+    }
+
+    /**
+     * @param includeCacheNameInURL the includeCacheNameInURL to set
+     */
+    public void setIncludeCacheNameAsParameter( boolean includeCacheNameInURL )
+    {
+        this.includeCacheNameAsParameter = includeCacheNameInURL;
+    }
+
+    /**
+     * @return the includeCacheNameInURL
+     */
+    public boolean isIncludeCacheNameAsParameter()
+    {
+        return includeCacheNameAsParameter;
+    }
+
+    /**
+     * @param includeKeysAndPatternsInURL the includeKeysAndPatternsInURL to set
+     */
+    public void setIncludeKeysAndPatternsAsParameter( boolean includeKeysAndPatternsInURL )
+    {
+        this.includeKeysAndPatternsAsParameter = includeKeysAndPatternsInURL;
+    }
+
+    /**
+     * @return the includeKeysAndPatternsInURL
+     */
+    public boolean isIncludeKeysAndPatternsAsParameter()
+    {
+        return includeKeysAndPatternsAsParameter;
+    }
+
+    /**
+     * @param includeRequestTypeasAsParameter the includeRequestTypeasAsParameter to set
+     */
+    public void setIncludeRequestTypeasAsParameter( boolean includeRequestTypeasAsParameter )
+    {
+        this.includeRequestTypeasAsParameter = includeRequestTypeasAsParameter;
+    }
+
+    /**
+     * @return the includeRequestTypeasAsParameter
+     */
+    public boolean isIncludeRequestTypeasAsParameter()
+    {
+        return includeRequestTypeasAsParameter;
+    }
+
+    /**
+     * @param url the url to set
+     */
+    public void setUrl( String url )
+    {
+        this.url = url;
+    }
+
+    /**
+     * @return the url
+     */
+    public String getUrl()
+    {
+        return url;
+    }
+
+    /**
+     * @param remoteHttpClientClassName the remoteHttpClientClassName to set
+     */
+    public void setRemoteHttpClientClassName( String remoteHttpClientClassName )
+    {
+        this.remoteHttpClientClassName = remoteHttpClientClassName;
+    }
+
+    /**
+     * @return the remoteHttpClientClassName
+     */
+    public String getRemoteHttpClientClassName()
+    {
+        return remoteHttpClientClassName;
+    }
+
+    /**
+     * @return String details
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n RemoteHttpCacheAttributes" );
+        buf.append( "\n maxConnectionsPerHost = [" + getMaxConnectionsPerHost() + "]" );
+        buf.append( "\n socketTimeoutMillis = [" + getSocketTimeoutMillis() + "]" );
+        buf.append( "\n httpVersion = [" + getHttpVersion() + "]" );
+        buf.append( "\n connectionTimeoutMillis = [" + getConnectionTimeoutMillis() + "]" );
+        buf.append( "\n includeCacheNameAsParameter = [" + isIncludeCacheNameAsParameter() + "]" );
+        buf.append( "\n includeKeysAndPatternsAsParameter = [" + isIncludeKeysAndPatternsAsParameter() + "]" );
+        buf.append( "\n includeRequestTypeasAsParameter = [" + isIncludeRequestTypeasAsParameter() + "]" );
+        buf.append( "\n url = [" + getUrl() + "]" );
+        buf.append( "\n remoteHttpClientClassName = [" + getRemoteHttpClientClassName() + "]" );
+        buf.append( super.toString() );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClient.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClient.java
new file mode 100644
index 0000000..ba84d5d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClient.java
@@ -0,0 +1,496 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheDispatcher;
+import org.apache.commons.jcs.auxiliary.remote.http.client.behavior.IRemoteHttpCacheClient;
+import org.apache.commons.jcs.auxiliary.remote.util.RemoteCacheRequestFactory;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/** This is the service used by the remote http auxiliary cache. */
+public class RemoteHttpCacheClient<K, V>
+    implements IRemoteHttpCacheClient<K, V>
+{
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheClient.class );
+
+    /** The internal client. */
+    private IRemoteCacheDispatcher remoteDispatcher;
+
+    /** The remote attributes */
+    private RemoteHttpCacheAttributes remoteHttpCacheAttributes;
+
+    /** Set to true when initialize is called */
+    private boolean initialized = false;
+
+    /** For factory construction. */
+    public RemoteHttpCacheClient()
+    {
+        // does nothing
+    }
+
+    /**
+     * Constructs a client.
+     * <p>
+     * @param attributes
+     */
+    public RemoteHttpCacheClient( RemoteHttpCacheAttributes attributes )
+    {
+        setRemoteHttpCacheAttributes( attributes );
+        initialize( attributes );
+    }
+
+    /**
+     * The provides an extension point. If you want to extend this and use a special dispatcher,
+     * here is the place to do it.
+     * <p>
+     * @param attributes
+     */
+    @Override
+    public void initialize( RemoteHttpCacheAttributes attributes )
+    {
+        setRemoteDispatcher( new RemoteHttpCacheDispatcher( attributes ) );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Created remote Dispatcher." + getRemoteDispatcher() );
+        }
+        setInitialized( true );
+    }
+
+    /**
+     * Create a request, process, extract the payload.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+        throws IOException
+    {
+        return get( cacheName, key, 0 );
+    }
+
+    /**
+     * Create a request, process, extract the payload.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+        RemoteCacheRequest<K, Serializable> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createGetRequest( cacheName, key, requesterId );
+
+        RemoteCacheResponse<ICacheElement<K, V>> remoteHttpCacheResponse =
+            getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Get [" + key + "] = " + remoteHttpCacheResponse );
+        }
+
+        if ( remoteHttpCacheResponse != null)
+        {
+            return remoteHttpCacheResponse.getPayload();
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets multiple items from the cache matching the pattern.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, 0 );
+    }
+
+    /**
+     * Gets multiple items from the cache matching the pattern.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createGetMatchingRequest( cacheName, pattern, requesterId );
+
+        RemoteCacheResponse<Map<K, ICacheElement<K, V>>> remoteHttpCacheResponse =
+            getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "GetMatching [" + pattern + "] = " + remoteHttpCacheResponse );
+        }
+
+        return remoteHttpCacheResponse.getPayload();
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+        throws IOException
+    {
+        return getMultiple( cacheName, keys, 0 );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createGetMultipleRequest( cacheName, keys, requesterId );
+
+        RemoteCacheResponse<Map<K, ICacheElement<K, V>>> remoteHttpCacheResponse =
+            getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "GetMultiple [" + keys + "] = " + remoteHttpCacheResponse );
+        }
+
+        return remoteHttpCacheResponse.getPayload();
+    }
+
+    /**
+     * Removes the given key from the specified cache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key )
+        throws IOException
+    {
+        remove( cacheName, key, 0 );
+    }
+
+    /**
+     * Removes the given key from the specified cache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createRemoveRequest( cacheName, key, requesterId );
+
+        getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+    }
+
+    /**
+     * Remove all keys from the specified cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName )
+        throws IOException
+    {
+        removeAll( cacheName, 0 );
+    }
+
+    /**
+     * Remove all keys from the sepcified cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createRemoveAllRequest( cacheName, requesterId );
+
+        getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+    }
+
+    /**
+     * Puts a cache item to the cache.
+     * <p>
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+        throws IOException
+    {
+        update( item, 0 );
+    }
+
+    /**
+     * Puts a cache item to the cache.
+     * <p>
+     * @param cacheElement
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> cacheElement, long requesterId )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createUpdateRequest( cacheElement, requesterId );
+
+        getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+    }
+
+    /**
+     * Frees the specified cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void dispose( String cacheName )
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createDisposeRequest( cacheName, 0 );
+
+        getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+    }
+
+    /**
+     * Frees the specified cache.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void release()
+        throws IOException
+    {
+        // noop
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @param cacheName the name of the cache
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet( String cacheName ) throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<String, String> remoteHttpCacheRequest =
+            RemoteCacheRequestFactory.createGetKeySetRequest(cacheName, 0 );
+
+        RemoteCacheResponse<Set<K>> remoteHttpCacheResponse = getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+
+        if ( remoteHttpCacheResponse != null && remoteHttpCacheResponse.getPayload() != null )
+        {
+            return remoteHttpCacheResponse.getPayload();
+        }
+
+        return Collections.emptySet();
+    }
+
+    /**
+     * Make and alive request.
+     * <p>
+     * @return true if we make a successful alive request.
+     * @throws IOException
+     */
+    @Override
+    public boolean isAlive()
+        throws IOException
+    {
+        if ( !isInitialized() )
+        {
+            String message = "The Remote Http Client is not initialized.  Cannot process request.";
+            log.warn( message );
+            throw new IOException( message );
+        }
+
+        RemoteCacheRequest<K, V> remoteHttpCacheRequest = RemoteCacheRequestFactory.createAliveCheckRequest( 0 );
+        RemoteCacheResponse<String> remoteHttpCacheResponse =
+            getRemoteDispatcher().dispatchRequest( remoteHttpCacheRequest );
+
+        if ( remoteHttpCacheResponse != null )
+        {
+            return remoteHttpCacheResponse.isSuccess();
+        }
+
+        return false;
+    }
+
+    /**
+     * @param remoteDispatcher the remoteDispatcher to set
+     */
+    public void setRemoteDispatcher( IRemoteCacheDispatcher remoteDispatcher )
+    {
+        this.remoteDispatcher = remoteDispatcher;
+    }
+
+    /**
+     * @return the remoteDispatcher
+     */
+    public IRemoteCacheDispatcher getRemoteDispatcher()
+    {
+        return remoteDispatcher;
+    }
+
+    /**
+     * @param remoteHttpCacheAttributes the remoteHttpCacheAttributes to set
+     */
+    public void setRemoteHttpCacheAttributes( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
+    {
+        this.remoteHttpCacheAttributes = remoteHttpCacheAttributes;
+    }
+
+    /**
+     * @return the remoteHttpCacheAttributes
+     */
+    public RemoteHttpCacheAttributes getRemoteHttpCacheAttributes()
+    {
+        return remoteHttpCacheAttributes;
+    }
+
+    /**
+     * @param initialized the initialized to set
+     */
+    protected void setInitialized( boolean initialized )
+    {
+        this.initialized = initialized;
+    }
+
+    /**
+     * @return the initialized
+     */
+    protected boolean isInitialized()
+    {
+        return initialized;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcher.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcher.java
new file mode 100644
index 0000000..a427f26
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcher.java
@@ -0,0 +1,231 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpState;
+import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheDispatcher;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/** Calls the service. */
+public class RemoteHttpCacheDispatcher
+    extends AbstractHttpClient
+    implements IRemoteCacheDispatcher
+{
+    /** Named of the parameter */
+    private static final String PARAMETER_REQUEST_TYPE = "RequestType";
+
+    /** Named of the parameter */
+    private static final String PARAMETER_KEY = "Key";
+
+    /** Named of the parameter */
+    private static final String PARAMETER_CACHE_NAME = "CacheName";
+
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheDispatcher.class );
+
+    /** This needs to be standard, since the other side is standard */
+    private StandardSerializer serializer = new StandardSerializer();
+
+    /**
+     * @param remoteHttpCacheAttributes
+     */
+    public RemoteHttpCacheDispatcher( RemoteHttpCacheAttributes remoteHttpCacheAttributes )
+    {
+        super( remoteHttpCacheAttributes );
+    }
+
+    /**
+     * All requests will go through this method.
+     * <p>
+     * TODO consider taking in a URL instead of using the one in the configuration.
+     * <p>
+     * @param remoteCacheRequest
+     * @return RemoteCacheResponse
+     * @throws IOException
+     */
+    @Override
+    public <K, V, T>
+        RemoteCacheResponse<T> dispatchRequest( RemoteCacheRequest<K, V> remoteCacheRequest )
+        throws IOException
+    {
+        try
+        {
+            byte[] requestAsByteArray = serializer.serialize( remoteCacheRequest );
+
+            String url = addParameters( remoteCacheRequest, getRemoteHttpCacheAttributes().getUrl() );
+
+            byte[] responseAsByteArray = processRequest( requestAsByteArray, url );
+
+            RemoteCacheResponse<T> remoteCacheResponse = null;
+            try
+            {
+                remoteCacheResponse = serializer.deSerialize( responseAsByteArray, null );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                log.error( "Couldn't deserialize the response.", e );
+            }
+            return remoteCacheResponse;
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem dispatching request.", e );
+            throw new IOException( e.getMessage() );
+        }
+    }
+
+    /**
+     * @param requestAsByteArray
+     * @param url
+     * @return byte[] - the response
+     * @throws IOException
+     * @throws HttpException
+     */
+    protected byte[] processRequest( byte[] requestAsByteArray, String url )
+        throws IOException, HttpException
+    {
+        PostMethod post = new PostMethod( url );
+        RequestEntity requestEntity = new ByteArrayRequestEntity( requestAsByteArray );
+        post.setRequestEntity( requestEntity );
+        doWebserviceCall( post );
+        byte[] response = post.getResponseBody();
+        return response;
+    }
+
+    /**
+     * @param remoteCacheRequest
+     * @param baseUrl
+     * @return String
+     */
+    protected <K, V> String addParameters( RemoteCacheRequest<K, V> remoteCacheRequest, String baseUrl )
+    {
+        StringBuilder url = new StringBuilder( baseUrl );
+
+        try
+        {
+            if ( baseUrl != null && baseUrl.indexOf( "?" ) == -1 )
+            {
+                url.append( "?" );
+            }
+            else
+            {
+                url.append( "&" );
+            }
+
+            if ( getRemoteHttpCacheAttributes().isIncludeCacheNameAsParameter() )
+            {
+                if ( remoteCacheRequest.getCacheName() != null )
+                {
+                    url.append( PARAMETER_CACHE_NAME + "="
+                        + URLEncoder.encode( remoteCacheRequest.getCacheName(), "UTF-8" ) );
+                }
+            }
+            if ( getRemoteHttpCacheAttributes().isIncludeKeysAndPatternsAsParameter() )
+            {
+                String keyValue = "";
+                switch ( remoteCacheRequest.getRequestType() )
+                {
+                    case GET:
+                        keyValue = remoteCacheRequest.getKey() + "";
+                        break;
+                    case REMOVE:
+                        keyValue = remoteCacheRequest.getKey() + "";
+                        break;
+                    case GET_MATCHING:
+                        keyValue = remoteCacheRequest.getPattern();
+                        break;
+                    case GET_MULTIPLE:
+                        keyValue = remoteCacheRequest.getKeySet() + "";
+                        break;
+                    case GET_KEYSET:
+                        keyValue = remoteCacheRequest.getKey() + "";
+                        break;
+                    case UPDATE:
+                        keyValue = remoteCacheRequest.getCacheElement().getKey() + "";
+                        break;
+                    default:
+                        break;
+                }
+                String encodedKeyValue = URLEncoder.encode( keyValue, "UTF-8" );
+                url.append( "&" + PARAMETER_KEY + "=" + encodedKeyValue );
+            }
+            if ( getRemoteHttpCacheAttributes().isIncludeRequestTypeasAsParameter() )
+            {
+                url.append( "&"
+                    + PARAMETER_REQUEST_TYPE
+                    + "="
+                    + URLEncoder.encode( remoteCacheRequest.getRequestType().toString(), "UTF-8" ) );
+            }
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            log.error( "Couldn't encode URL.", e );
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Url: " + url.toString() );
+        }
+
+        return url.toString();
+    }
+
+    /**
+     * Called before the executeMethod on the client.
+     * <p>
+     * @param post http method
+     * @return HttpState
+     * @throws IOException
+     */
+    @Override
+    public HttpState preProcessWebserviceCall( HttpMethod post )
+        throws IOException
+    {
+        // do nothing. Child can override.
+        return null;
+    }
+
+    /**
+     * Called after the executeMethod on the client.
+     * <p>
+     * @param post http method
+     * @param httpState state
+     * @throws IOException
+     */
+    @Override
+    public void postProcessWebserviceCall( HttpMethod post, HttpState httpState )
+        throws IOException
+    {
+        // do nothing. Child can override.
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheFactory.java
new file mode 100644
index 0000000..688ae47
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheFactory.java
@@ -0,0 +1,119 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheNoWait;
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheNoWaitFacade;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * The RemoteCacheFactory creates remote caches for the cache hub. It returns a no wait facade which
+ * is a wrapper around a no wait. The no wait object is either an active connection to a remote
+ * cache or a balking zombie if the remote cache is not accessible. It should be transparent to the
+ * clients.
+ */
+public class RemoteHttpCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** The name of this auxiliary */
+    private String name;
+
+    /** store reference of facades to initiate failover */
+    private static final HashMap<String, RemoteCacheNoWaitFacade<?, ?>> facades =
+        new HashMap<String, RemoteCacheNoWaitFacade<?, ?>>();
+
+    /**
+     * For LOCAL clients we get a handle to all the failovers, but we do not register a listener
+     * with them. We create the RemoteCacheManager, but we do not get a cache.
+     * <p>
+     * The failover runner will get a cache from the manager. When the primary is restored it will
+     * tell the manager for the failover to deregister the listener.
+     * <p>
+     * @param iaca
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes iaca, ICompositeCacheManager cacheMgr,
+                                       ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        RemoteHttpCacheAttributes rca = (RemoteHttpCacheAttributes) iaca;
+
+        ArrayList<ICache<K, V>> noWaits = new ArrayList<ICache<K, V>>();
+
+        RemoteHttpCacheManager rcm = RemoteHttpCacheManager.getInstance( cacheMgr, cacheEventLogger, elementSerializer );
+        // TODO, use the configured value.
+        rca.setRemoteType( RemoteType.LOCAL );
+        ICache<K, V> ic = rcm.getCache( rca );
+        noWaits.add( ic );
+
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        RemoteCacheNoWait<K, V>[] rcnwArray = noWaits.toArray( new RemoteCacheNoWait[0] );
+        RemoteCacheNoWaitFacade<K, V> rcnwf =
+            new RemoteCacheNoWaitFacade<K, V>(rcnwArray, rca, cacheMgr, cacheEventLogger, elementSerializer );
+
+        getFacades().put( rca.getCacheName(), rcnwf );
+
+        return rcnwf;
+    }
+
+    /**
+     * Gets the name attribute of the RemoteCacheFactory object
+     * <p>
+     * @return The name value
+     */
+    @Override
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * Sets the name attribute of the RemoteCacheFactory object
+     * <p>
+     * @param name The new name value
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    /**
+     * The facades are what the cache hub talks to.
+     * @return Returns the facades.
+     */
+    public static HashMap<String, RemoteCacheNoWaitFacade<?, ?>> getFacades()
+    {
+        return facades;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManager.java
new file mode 100644
index 0000000..0b6398a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManager.java
@@ -0,0 +1,267 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheManager;
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheNoWait;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
+import org.apache.commons.jcs.auxiliary.remote.http.client.behavior.IRemoteHttpCacheClient;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.config.OptionConverter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is a very crude copy of the RMI remote manager. It needs a lot of work!
+ */
+public class RemoteHttpCacheManager
+    implements AuxiliaryCacheManager, IShutdownObserver
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheManager.class );
+
+    /** Contains mappings of Location instance to RemoteCacheManager instance. */
+    private static RemoteHttpCacheManager instance;
+
+    /** Contains instances of RemoteCacheNoWait managed by a RemoteCacheManager instance. */
+    static final Map<String, RemoteCacheNoWait<?, ?>> caches =
+        new HashMap<String, RemoteCacheNoWait<?, ?>>();
+
+    /** The configuration attributes. */
+    private IRemoteCacheAttributes remoteCacheAttributes;
+
+    /** The event logger. */
+    private final ICacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    private final IElementSerializer elementSerializer;
+
+    /** The cache manager listeners will need to use to get a cache. */
+    private final ICompositeCacheManager cacheMgr;
+
+    /** Remote cache monitor. */
+    private static RemoteHttpCacheMonitor monitor;
+
+    /**
+     * Constructs an instance to with the given remote connection parameters. If the connection
+     * cannot be made, "zombie" services will be temporarily used until a successful re-connection
+     * is made by the monitoring daemon.
+     * <p>
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     */
+    private RemoteHttpCacheManager( ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger,
+                                    IElementSerializer elementSerializer )
+    {
+        this.cacheMgr = cacheMgr;
+        this.cacheEventLogger = cacheEventLogger;
+        this.elementSerializer = elementSerializer;
+
+        // register shutdown observer
+        this.cacheMgr.registerShutdownObserver( this );
+    }
+
+    /**
+     * Gets the defaultCattr attribute of the RemoteCacheManager object.
+     * <p>
+     * @return The defaultCattr value
+     */
+    public IRemoteCacheAttributes getDefaultCattr()
+    {
+        return this.remoteCacheAttributes;
+    }
+
+    /** @return Returns an instance if it exists. else null. */
+    public synchronized static RemoteHttpCacheManager getInstance()
+    {
+        return instance;
+    }
+
+    /**
+     * Get the singleton instance.
+     * <p>
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return The instance value
+     */
+    public synchronized static RemoteHttpCacheManager getInstance( ICompositeCacheManager cacheMgr,
+                                                                   ICacheEventLogger cacheEventLogger,
+                                                                   IElementSerializer elementSerializer )
+    {
+        if ( instance == null )
+        {
+            instance = new RemoteHttpCacheManager( cacheMgr, cacheEventLogger, elementSerializer );
+        }
+
+        // Fires up the monitoring daemon.
+        if ( monitor == null )
+        {
+            monitor = RemoteHttpCacheMonitor.getInstance();
+            // If the returned monitor is null, it means it's already started
+            // elsewhere.
+            if ( monitor != null )
+            {
+                Thread t = new Thread( monitor );
+                t.setDaemon( true );
+                t.start();
+            }
+        }
+
+        return instance;
+    }
+
+    /**
+     * Returns a remote cache for the given cache name.
+     * <p>
+     * @param cacheName
+     * @return The cache value
+     */
+    @Override
+    public <K, V> RemoteCacheNoWait<K, V> getCache( String cacheName )
+    {
+        // TODO get some defaults!
+        // Perhaps we will need a manager per URL????
+        RemoteHttpCacheAttributes ca = new RemoteHttpCacheAttributes();
+        ca.setCacheName( cacheName );
+        return getCache( ca );
+    }
+
+    /**
+     * Gets a RemoteCacheNoWait from the RemoteCacheManager. The RemoteCacheNoWait objects are
+     * identified by the cache name value of the RemoteCacheAttributes object.
+     * <p>
+     * If the client is configured to register a listener, this call results on a listener being
+     * created if one isn't already registered with the remote cache for this region.
+     * <p>
+     * @param cattr
+     * @return The cache value
+     */
+    public <K, V> RemoteCacheNoWait<K, V> getCache( RemoteHttpCacheAttributes cattr )
+    {
+        RemoteCacheNoWait<K, V> remoteCacheNoWait = null;
+
+        synchronized ( caches )
+        {
+            @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+            RemoteCacheNoWait<K, V> remoteCacheNoWait2 = (RemoteCacheNoWait<K, V>) caches.get( cattr.getCacheName() + cattr.getUrl() );
+            remoteCacheNoWait = remoteCacheNoWait2;
+            if ( remoteCacheNoWait == null )
+            {
+                RemoteHttpClientListener<K, V> listener = new RemoteHttpClientListener<K, V>( cattr, cacheMgr );
+
+                IRemoteHttpCacheClient<K, V> remoteService = createRemoteHttpCacheClientForAttributes( cattr );
+
+                IRemoteCacheClient<K, V> remoteCacheClient = new RemoteHttpCache<K, V>( cattr, remoteService, listener );
+                remoteCacheClient.setCacheEventLogger( cacheEventLogger );
+                remoteCacheClient.setElementSerializer( elementSerializer );
+
+                remoteCacheNoWait = new RemoteCacheNoWait<K, V>( remoteCacheClient );
+                remoteCacheNoWait.setCacheEventLogger( cacheEventLogger );
+                remoteCacheNoWait.setElementSerializer( elementSerializer );
+
+                caches.put( cattr.getCacheName() + cattr.getUrl(), remoteCacheNoWait );
+            }
+            // might want to do some listener sanity checking here.
+        }
+
+        return remoteCacheNoWait;
+    }
+
+    /**
+     * This is an extension point. The manager and other classes will only create
+     * RemoteHttpCacheClient through this method.
+     * <p>
+     * @param cattr
+     * @return IRemoteHttpCacheClient
+     */
+    protected <K, V> IRemoteHttpCacheClient<K, V> createRemoteHttpCacheClientForAttributes( RemoteHttpCacheAttributes cattr )
+    {
+        IRemoteHttpCacheClient<K, V> client = OptionConverter.instantiateByClassName( cattr
+            .getRemoteHttpClientClassName(), null );
+
+        if ( client == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Creating the default client." );
+            }
+            client = new RemoteHttpCacheClient<K, V>( );
+        }
+        client.initialize( cattr );
+        return client;
+    }
+
+    /**
+     * Gets the stats attribute of the RemoteCacheManager object
+     * <p>
+     * @return The stats value
+     */
+    public String getStats()
+    {
+        StringBuilder stats = new StringBuilder();
+        for (RemoteCacheNoWait<?, ?> c : caches.values())
+        {
+            if ( c != null )
+            {
+                stats.append( c.getCacheName() );
+            }
+        }
+        return stats.toString();
+    }
+
+    /**
+     * Shutdown callback from composite cache manager.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.IShutdownObserver#shutdown()
+     */
+    @Override
+    public void shutdown()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Observed shutdown request." );
+        }
+        //release();
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheMonitor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheMonitor.java
new file mode 100644
index 0000000..337eb1d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheMonitor.java
@@ -0,0 +1,248 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.http.client.behavior.IRemoteHttpCacheClient;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Upon the notification of a connection error, the monitor changes to operate in a time driven
+ * mode. That is, it attempts to recover the connections on a periodic basis. When all failed
+ * connections are restored, it changes back to the failure driven mode.
+ */
+public class RemoteHttpCacheMonitor
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheMonitor.class );
+
+    /** The remote cache that we are monitoring */
+    private static RemoteHttpCacheMonitor instance;
+
+    /** Time between checks */
+    private static long idlePeriod = 3 * 1000;
+
+    /** Set of remote caches to monitor. This are added on error, if not before. */
+    private final Set<RemoteHttpCache<?, ?>> remoteHttpCaches = new HashSet<RemoteHttpCache<?, ?>>();
+
+    /**
+     * Must make sure RemoteCacheMonitor is started before any remote error can be detected!
+     */
+    private boolean alright = true;
+
+    /** Time driven mode */
+    static final int TIME = 0;
+
+    /** Error driven mode -- only check on health if there is an error */
+    static final int ERROR = 1;
+
+    /** The mode to use */
+    static int mode = ERROR;
+
+    /**
+     * Configures the idle period between repairs.
+     * <p>
+     * @param idlePeriod The new idlePeriod value
+     */
+    public static void setIdlePeriod( long idlePeriod )
+    {
+        if ( idlePeriod > RemoteHttpCacheMonitor.idlePeriod )
+        {
+            RemoteHttpCacheMonitor.idlePeriod = idlePeriod;
+        }
+    }
+
+    /** Constructor for the RemoteCacheMonitor object */
+    private RemoteHttpCacheMonitor()
+    {
+        super();
+    }
+
+    /**
+     * Returns the singleton instance.
+     * <p>
+     * @return The instance value
+     */
+    static RemoteHttpCacheMonitor getInstance()
+    {
+        synchronized ( RemoteHttpCacheMonitor.class )
+        {
+            if ( instance == null )
+            {
+                return instance = new RemoteHttpCacheMonitor();
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * Notifies the cache monitor that an error occurred, and kicks off the error recovery process.
+     * <p>
+     * @param remoteCache
+     */
+    public void notifyError( RemoteHttpCache<?, ?> remoteCache )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Notified of an error. " + remoteCache );
+        }
+        bad();
+        synchronized ( this )
+        {
+            remoteHttpCaches.add( remoteCache );
+            notify();
+        }
+    }
+
+    // Run forever.
+
+    // Avoid the use of any synchronization in the process of monitoring for
+    // performance reasons.
+    // If exception is thrown owing to synchronization,
+    // just skip the monitoring until the next round.
+    /** Main processing method for the RemoteCacheMonitor object */
+    @Override
+    public void run()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Monitoring daemon started" );
+        }
+        do
+        {
+            if ( mode == ERROR )
+            {
+                synchronized ( this )
+                {
+                    if ( alright )
+                    {
+                        // make this configurable, comment out wait to enter
+                        // time driven mode
+                        // Failure driven mode.
+                        try
+                        {
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "FAILURE DRIVEN MODE: cache monitor waiting for error" );
+                            }
+                            wait();
+                            // wake up only if there is an error.
+                        }
+                        catch ( InterruptedException ignore )
+                        {
+                            // swallow
+                        }
+                    }
+                }
+            }
+            else
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "TIME DRIVEN MODE: cache monitor sleeping for " + idlePeriod );
+                }
+                // Time driven mode: sleep between each round of recovery
+                // attempt.
+                // will need to test not just check status
+            }
+
+            try
+            {
+                Thread.sleep( idlePeriod );
+            }
+            catch ( InterruptedException ex )
+            {
+                // ignore;
+            }
+
+            // The "allright" flag must be false here.
+            // Simply presume we can fix all the errors until proven otherwise.
+            synchronized ( this )
+            {
+                alright = true;
+            }
+
+            // Make a copy
+            Set<RemoteHttpCache<?, ?>> remoteCachesToExamine =
+                new HashSet<RemoteHttpCache<?, ?>>();
+            synchronized ( this )
+            {
+                for (RemoteHttpCache<?, ?> remoteCache : this.remoteHttpCaches)
+                {
+                    remoteCachesToExamine.add( remoteCache );
+                }
+            }
+            // If any cache is in error, it strongly suggests all caches
+            // managed by the
+            // same RmicCacheManager instance are in error. So we fix
+            // them once and for all.
+            for (RemoteHttpCache<?, ?> remoteCache : remoteCachesToExamine)
+            {
+                try
+                {
+                    if ( remoteCache.getStatus() == CacheStatus.ERROR )
+                    {
+                        RemoteHttpCacheAttributes attributes = remoteCache.getRemoteHttpCacheAttributes();
+
+                        IRemoteHttpCacheClient<Serializable, Serializable> remoteService = RemoteHttpCacheManager.getInstance()
+                            .createRemoteHttpCacheClientForAttributes( attributes );
+
+                        if ( log.isInfoEnabled() )
+                        {
+                            log.info( "Performing Alive check on service " + remoteService );
+                        }
+                        // If we can't fix them, just skip and re-try in
+                        // the next round.
+                        if ( remoteService.isAlive() )
+                        {
+                            remoteCache.fixCache( remoteService );
+                        }
+                        else
+                        {
+                            bad();
+                        }
+                        break;
+                    }
+                }
+                catch ( Exception ex )
+                {
+                    bad();
+                    // Problem encountered in fixing the caches managed by a
+                    // RemoteCacheManager instance.
+                    // Soldier on to the next RemoteHttpCache.
+                    log.error( ex );
+                }
+            }
+        }
+        while ( true );
+    }
+
+    /** Sets the "aright" flag to false in a critical section. */
+    private synchronized void bad()
+    {
+        alright = false;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpClientListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpClientListener.java
new file mode 100644
index 0000000..28ecd2e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpClientListener.java
@@ -0,0 +1,53 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.AbstractRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+
+/** Does nothing */
+public class RemoteHttpClientListener<K, V>
+    extends AbstractRemoteCacheListener<K, V>
+{
+    /** Serial version */
+    private static final long serialVersionUID = -9078366610772128010L;
+
+    /**
+     * Only need one since it does work for all regions, just reference by multiple region names.
+     * <p>
+     * The constructor exports this object, making it available to receive incoming calls. The
+     * callback port is anonymous unless a local port value was specified in the configuration.
+     * <p>
+     * @param irca
+     * @param cacheMgr
+     */
+    public RemoteHttpClientListener( IRemoteCacheAttributes irca, ICompositeCacheManager cacheMgr )
+    {
+        super( irca, cacheMgr );
+    }
+
+    /** Nothing */
+    @Override
+    public void dispose()
+    {
+        // noop
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/behavior/IRemoteHttpCacheClient.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/behavior/IRemoteHttpCacheClient.java
new file mode 100644
index 0000000..9949b7e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/client/behavior/IRemoteHttpCacheClient.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.http.client.RemoteHttpCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+import java.io.IOException;
+
+
+/**
+ * It's not entirely clear that this interface is needed. I simply wanted the initialization method.
+ * This could be added to the ICacheSerice method.
+ */
+public interface IRemoteHttpCacheClient<K, V>
+    extends ICacheServiceNonLocal<K, V>
+{
+    /**
+     * The provides an extension point. If you want to extend this and use a special dispatcher,
+     * here is the place to do it.
+     * <p>
+     * @param attributes
+     */
+    void initialize( RemoteHttpCacheAttributes attributes );
+
+    /**
+     * Make and alive request.
+     * <p>
+     * @return true if we make a successful alive request.
+     * @throws IOException
+     */
+    boolean isAlive()
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/AbstractRemoteCacheService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/AbstractRemoteCacheService.java
new file mode 100644
index 0000000..866ce4e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/AbstractRemoteCacheService.java
@@ -0,0 +1,603 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.logging.CacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class contains common methods for remote cache services. Eventually I hope to extract out
+ * much of the RMI server to use this as well. I'm starting with the Http service.
+ */
+public abstract class AbstractRemoteCacheService<K extends Serializable, V extends Serializable>
+    implements ICacheServiceNonLocal<K, V>
+{
+    /** An optional event logger */
+    private transient ICacheEventLogger cacheEventLogger;
+
+    /** The central hub */
+    private ICompositeCacheManager cacheManager;
+
+    /** Name of the event log source. */
+    private String eventLogSourceName = "AbstractRemoteCacheService";
+
+    /** Number of puts into the cache. */
+    private int puts = 0;
+
+    /** The interval at which we will log updates. */
+    private final int logInterval = 100;
+
+    /** log instance */
+    private static final Log log = LogFactory.getLog( AbstractRemoteCacheService.class );
+
+    /**
+     * Creates the super with the needed items.
+     * <p>
+     * @param cacheManager
+     * @param cacheEventLogger
+     */
+    public AbstractRemoteCacheService( ICompositeCacheManager cacheManager, ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheManager = cacheManager;
+        this.cacheEventLogger = cacheEventLogger;
+    }
+
+    /**
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+        throws IOException
+    {
+        update( item, 0 );
+    }
+
+    /**
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<ICacheElement<K, V>> cacheEvent = createICacheEvent( item, requesterId, ICacheEventLogger.UPDATE_EVENT );
+        try
+        {
+            logUpdateInfo( item );
+
+            processUpdate( item, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @throws IOException
+     */
+    abstract void processUpdate( ICacheElement<K, V> item, long requesterId )
+        throws IOException;
+
+    /**
+     * Log some details.
+     * <p>
+     * @param item
+     */
+    private void logUpdateInfo( ICacheElement<K, V> item )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            // not thread safe, but it doesn't have to be accurate
+            puts++;
+            if ( puts % logInterval == 0 )
+            {
+                log.info( "puts = " + puts );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "In update, put [" + item.getKey() + "] in [" + item.getCacheName() + "]" );
+        }
+    }
+
+    /**
+     * Returns a cache value from the specified remote cache; or null if the cache or key does not
+     * exist.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+        throws IOException
+    {
+        return this.get( cacheName, key, 0 );
+    }
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * Adding the requestor id, allows the cache to determine the source of the get.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        ICacheElement<K, V> element = null;
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.GET_EVENT );
+        try
+        {
+            element = processGet( cacheName, key, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+        return element;
+    }
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * Adding the requestor id, allows the cache to determine the source of the get.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    abstract ICacheElement<K, V> processGet( String cacheName, K key, long requesterId )
+        throws IOException;
+
+    /**
+     * Gets all matching items.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return Map of keys and wrapped objects
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, 0 );
+    }
+
+    /**
+     * Retrieves all matching keys.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map of keys and wrapped objects
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, pattern, requesterId,
+                                                    ICacheEventLogger.GETMATCHING_EVENT );
+        try
+        {
+            return processGetMatching( cacheName, pattern, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Retrieves all matching keys.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map of keys and wrapped objects
+     * @throws IOException
+     */
+    abstract Map<K, ICacheElement<K, V>> processGetMatching( String cacheName, String pattern, long requesterId )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+        throws IOException
+    {
+        return this.getMultiple( cacheName, keys, 0 );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<Serializable> cacheEvent = createICacheEvent( cacheName, (Serializable) keys, requesterId,
+                                                    ICacheEventLogger.GETMULTIPLE_EVENT );
+        try
+        {
+            return processGetMultiple( cacheName, keys, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    abstract Map<K, ICacheElement<K, V>> processGetMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException;
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet( String cacheName )
+    {
+        return processGetKeySet( cacheName );
+    }
+
+    /**
+     * Gets the set of keys of objects currently in the cache.
+     * <p>
+     * @param cacheName
+     * @return Set
+     */
+    public Set<K> processGetKeySet( String cacheName )
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        return cache.getKeySet();
+    }
+
+    /**
+     * Removes the given key from the specified remote cache. Defaults the listener id to 0.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key )
+        throws IOException
+    {
+        remove( cacheName, key, 0 );
+    }
+
+    /**
+     * Remove the key from the cache region and don't tell the source listener about it.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.REMOVE_EVENT );
+        try
+        {
+            processRemove( cacheName, key, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Remove the key from the cache region and don't tell the source listener about it.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    abstract void processRemove( String cacheName, K key, long requesterId )
+        throws IOException;
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName )
+        throws IOException
+    {
+        removeAll( cacheName, 0 );
+    }
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "all", requesterId, ICacheEventLogger.REMOVEALL_EVENT );
+        try
+        {
+            processRemoveAll( cacheName, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    abstract void processRemoveAll( String cacheName, long requesterId )
+        throws IOException;
+
+    /**
+     * Frees the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void dispose( String cacheName )
+        throws IOException
+    {
+        dispose( cacheName, 0 );
+    }
+
+    /**
+     * Frees the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    public void dispose( String cacheName, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "none", requesterId, ICacheEventLogger.DISPOSE_EVENT );
+        try
+        {
+            processDispose( cacheName, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    abstract void processDispose( String cacheName, long requesterId )
+        throws IOException;
+
+    /**
+     * Gets the stats attribute of the RemoteCacheServer object.
+     * <p>
+     * @return The stats value
+     * @throws IOException
+     */
+    public String getStats()
+        throws IOException
+    {
+        return cacheManager.getStats();
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @param eventName
+     * @return ICacheEvent
+     */
+    protected ICacheEvent<ICacheElement<K, V>> createICacheEvent( ICacheElement<K, V> item, long requesterId, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<ICacheElement<K, V>>();
+        }
+        String ipAddress = getExtraInfoForRequesterId( requesterId );
+        return cacheEventLogger.createICacheEvent( getEventLogSourceName(), item.getCacheName(), eventName, ipAddress,
+                                                   item );
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @param eventName
+     * @return ICacheEvent
+     */
+    protected <T extends Serializable> ICacheEvent<T> createICacheEvent( String cacheName, T key, long requesterId, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<T>();
+        }
+        String ipAddress = getExtraInfoForRequesterId( requesterId );
+        return cacheEventLogger.createICacheEvent( getEventLogSourceName(), cacheName, eventName, ipAddress, key );
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
+        }
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param cacheEvent
+     */
+    protected <T extends Serializable> void logICacheEvent( ICacheEvent<T> cacheEvent )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Ip address for the client, if one is stored.
+     * <p>
+     * Protected for testing.
+     * <p>
+     * @param requesterId
+     * @return String
+     */
+    protected abstract String getExtraInfoForRequesterId( long requesterId );
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @param cacheEventLogger
+     */
+    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheEventLogger = cacheEventLogger;
+    }
+
+    /**
+     * @param cacheManager the cacheManager to set
+     */
+    protected void setCacheManager( ICompositeCacheManager cacheManager )
+    {
+        this.cacheManager = cacheManager;
+    }
+
+    /**
+     * @return the cacheManager
+     */
+    protected ICompositeCacheManager getCacheManager()
+    {
+        return cacheManager;
+    }
+
+    /**
+     * @param eventLogSourceName the eventLogSourceName to set
+     */
+    protected void setEventLogSourceName( String eventLogSourceName )
+    {
+        this.eventLogSourceName = eventLogSourceName;
+    }
+
+    /**
+     * @return the eventLogSourceName
+     */
+    protected String getEventLogSourceName()
+    {
+        return eventLogSourceName;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptor.java
new file mode 100644
index 0000000..63a78f1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptor.java
@@ -0,0 +1,174 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The Servlet deserializes the request object. The request object is passed to the processor. The
+ * processor then calls the service which does the work of talking to the cache.
+ * <p>
+ * This is essentially an adaptor on top of the service.
+ */
+public class RemoteCacheServiceAdaptor<K extends Serializable, V extends Serializable>
+{
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( RemoteCacheServiceAdaptor.class );
+
+    /** The service that does the work. */
+    private ICacheServiceNonLocal<K, V> remoteCacheService;
+
+    /** This is for testing without the factory. */
+    protected RemoteCacheServiceAdaptor()
+    {
+        // for testing.
+    }
+
+    /**
+     * Create a process with a cache manager.
+     * <p>
+     * @param cacheManager
+     */
+    public RemoteCacheServiceAdaptor( ICompositeCacheManager cacheManager )
+    {
+        ICacheServiceNonLocal<K, V> rcs = RemoteHttpCacheSeviceFactory.createRemoteHttpCacheService( cacheManager );
+        setRemoteCacheService( rcs );
+    }
+
+    /**
+     * Processes the request. It will call the appropriate method on the service
+     * <p>
+     * @param request
+     * @return RemoteHttpCacheResponse, never null
+     */
+    @SuppressWarnings( "unchecked" ) // need to cast to correct return type
+    public <T> RemoteCacheResponse<T> processRequest( RemoteCacheRequest<K, V> request )
+    {
+        RemoteCacheResponse<Object> response = new RemoteCacheResponse<Object>();
+
+        if ( request == null )
+        {
+            String message = "The request is null. Cannot process";
+            log.warn( message );
+            response.setSuccess( false );
+            response.setErrorMessage( message );
+        }
+        else
+        {
+            try
+            {
+                switch ( request.getRequestType() )
+                {
+                    case GET:
+                        ICacheElement<K, V> element = getRemoteCacheService().get( request.getCacheName(), request.getKey(),
+                                                                             request.getRequesterId() );
+                        response.setPayload(element);
+                        break;
+                    case GET_MULTIPLE:
+                        Map<K, ICacheElement<K, V>> elementMap = getRemoteCacheService().getMultiple( request.getCacheName(),
+                                                                              request.getKeySet(),
+                                                                              request.getRequesterId() );
+                        if ( elementMap != null )
+                        {
+                            Map<K, ICacheElement<K, V>> map = new HashMap<K, ICacheElement<K,V>>();
+                            map.putAll(elementMap);
+                            response.setPayload(map);
+                        }
+                        break;
+                    case GET_MATCHING:
+                        Map<K, ICacheElement<K, V>> elementMapMatching = getRemoteCacheService().getMatching( request.getCacheName(),
+                                                                                      request.getPattern(),
+                                                                                      request.getRequesterId() );
+                        if ( elementMapMatching != null )
+                        {
+                            Map<K, ICacheElement<K, V>> map = new HashMap<K, ICacheElement<K,V>>();
+                            map.putAll(elementMapMatching);
+                            response.setPayload(map);
+                        }
+                        break;
+                    case REMOVE:
+                        getRemoteCacheService().remove( request.getCacheName(), request.getKey(),
+                                                        request.getRequesterId() );
+                        break;
+                    case REMOVE_ALL:
+                        getRemoteCacheService().removeAll( request.getCacheName(), request.getRequesterId() );
+                        break;
+                    case UPDATE:
+                        getRemoteCacheService().update( request.getCacheElement(), request.getRequesterId() );
+                        break;
+                    case ALIVE_CHECK:
+                        response.setSuccess( true );
+                        break;
+                    case DISPOSE:
+                        response.setSuccess( true );
+                        // DO NOTHING
+                        break;
+                    case GET_KEYSET:
+                        Set<K> keys = getRemoteCacheService().getKeySet( request.getCacheName() );
+                        response.setPayload( keys );
+                        break;
+                    default:
+                        String message = "Unknown event type.  Cannot process " + request;
+                        log.warn( message );
+                        response.setSuccess( false );
+                        response.setErrorMessage( message );
+                        break;
+                }
+            }
+            catch ( IOException e )
+            {
+                String message = "Problem processing request. " + request + " Error: " + e.getMessage();
+                log.error( message, e );
+                response.setSuccess( false );
+                response.setErrorMessage( message );
+            }
+        }
+
+        return (RemoteCacheResponse<T>)response;
+    }
+
+    /**
+     * @param remoteHttpCacheService the remoteHttpCacheService to set
+     */
+    public void setRemoteCacheService( ICacheServiceNonLocal<K, V> remoteHttpCacheService )
+    {
+        this.remoteCacheService = remoteHttpCacheService;
+    }
+
+    /**
+     * @return the remoteHttpCacheService
+     */
+    public ICacheServiceNonLocal<K, V> getRemoteCacheService()
+    {
+        return remoteCacheService;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServerAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServerAttributes.java
new file mode 100644
index 0000000..8b80bcb
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServerAttributes.java
@@ -0,0 +1,115 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+
+/**
+ * Configuration for the RemoteHttpCacheServer. Most of these properties are used only by the
+ * service.
+ */
+public class RemoteHttpCacheServerAttributes
+    extends AbstractAuxiliaryCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -3987239306108780496L;
+
+    /** Can a cluster remote put to other remotes */
+    private boolean localClusterConsistency = true;
+
+    /** Can a cluster remote get from other remotes */
+    private boolean allowClusterGet = true;
+
+    /**
+     * clones
+     * <p>
+     * @return AuxiliaryCacheAttributes clone
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        try
+        {
+            return (AuxiliaryCacheAttributes) this.clone();
+        }
+        catch ( Exception e )
+        {
+            // swallow
+        }
+        return this;
+    }
+
+    /**
+     * Should cluster updates be propagated to the locals
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    public boolean isLocalClusterConsistency()
+    {
+        return localClusterConsistency;
+    }
+
+    /**
+     * Should cluster updates be propagated to the locals
+     * <p>
+     * @param r The new localClusterConsistency value
+     */
+    public void setLocalClusterConsistency( boolean r )
+    {
+        this.localClusterConsistency = r;
+    }
+
+    /**
+     * Should gets from non-cluster clients be allowed to get from other remote auxiliaries.
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    public boolean isAllowClusterGet()
+    {
+        return allowClusterGet;
+    }
+
+    /**
+     * Should we try to get from other cluster servers if we don't find the items locally.
+     * <p>
+     * @param r The new localClusterConsistency value
+     */
+    public void setAllowClusterGet( boolean r )
+    {
+        allowClusterGet = r;
+    }
+
+    /**
+     * @return String details
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nRemoteHttpCacheServiceAttributes" );
+        buf.append( "\n cacheName = [" + this.getCacheName() + "]" );
+        buf.append( "\n allowClusterGet = [" + this.isAllowClusterGet() + "]" );
+        buf.append( "\n localClusterConsistency = [" + this.isLocalClusterConsistency() + "]" );
+        buf.append( "\n eventQueueType = [" + this.getEventQueueType() + "]" );
+        buf.append( "\n eventQueuePoolName = [" + this.getEventQueuePoolName() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheService.java
new file mode 100644
index 0000000..5c89014
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheService.java
@@ -0,0 +1,270 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This does the work. It's called by the processor. The base class wraps the processing calls in
+ * event logs, if an event logger is present.
+ * <p>
+ * For now we assume that all clients are non-cluster clients. And listener notification is not
+ * supported.
+ */
+public class RemoteHttpCacheService<K extends Serializable, V extends Serializable>
+    extends AbstractRemoteCacheService<K, V>
+{
+    /** The name used in the event logs. */
+    private static final String EVENT_LOG_SOURCE_NAME = "RemoteHttpCacheServer";
+
+    /** The configuration */
+    private final RemoteHttpCacheServerAttributes remoteHttpCacheServerAttributes;
+
+    /**
+     * Create a process with a cache manager.
+     * <p>
+     * @param cacheManager
+     * @param remoteHttpCacheServerAttributes
+     * @param cacheEventLogger
+     */
+    public RemoteHttpCacheService( ICompositeCacheManager cacheManager,
+                                   RemoteHttpCacheServerAttributes remoteHttpCacheServerAttributes,
+                                   ICacheEventLogger cacheEventLogger )
+    {
+        super( cacheManager, cacheEventLogger );
+        setEventLogSourceName( EVENT_LOG_SOURCE_NAME );
+        this.remoteHttpCacheServerAttributes = remoteHttpCacheServerAttributes;
+    }
+
+    /**
+     * Processes a get request.
+     * <p>
+     * If isAllowClusterGet is enabled we will treat this as a normal request or non-remote origins.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> processGet( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isAllowClusterGet();
+        if ( keepLocal )
+        {
+            return cache.localGet( key );
+        }
+        else
+        {
+            return cache.get( key );
+        }
+    }
+
+    /**
+     * Processes a get request.
+     * <p>
+     * If isAllowClusterGet is enabled we will treat this as a normal request of non-remote
+     * origination.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isAllowClusterGet();
+        if ( keepLocal )
+        {
+            return cache.localGetMultiple( keys );
+        }
+        else
+        {
+            return cache.getMultiple( keys );
+        }
+    }
+
+    /**
+     * Processes a get request.
+     * <p>
+     * If isAllowClusterGet is enabled we will treat this as a normal request of non-remote
+     * origination.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> processGetMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isAllowClusterGet();
+        if ( keepLocal )
+        {
+            return cache.localGetMatching( pattern );
+        }
+        else
+        {
+            return cache.getMatching( pattern );
+        }
+    }
+
+    /**
+     * Processes an update request.
+     * <p>
+     * If isLocalClusterConsistency is enabled we will treat this as a normal request of non-remote
+     * origination.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void processUpdate( ICacheElement<K, V> item, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( item.getCacheName() );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isLocalClusterConsistency();
+        if ( keepLocal )
+        {
+            cache.localUpdate( item );
+        }
+        else
+        {
+            cache.update( item );
+        }
+    }
+
+    /**
+     * Processes a remove request.
+     * <p>
+     * If isLocalClusterConsistency is enabled we will treat this as a normal request of non-remote
+     * origination.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void processRemove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isLocalClusterConsistency();
+        if ( keepLocal )
+        {
+            cache.localRemove( key );
+        }
+        else
+        {
+            cache.remove( key );
+        }
+    }
+
+    /**
+     * Processes a removeAll request.
+     * <p>
+     * If isLocalClusterConsistency is enabled we will treat this as a normal request of non-remote
+     * origination.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void processRemoveAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+
+        boolean keepLocal = !remoteHttpCacheServerAttributes.isLocalClusterConsistency();
+        if ( keepLocal )
+        {
+            cache.localRemoveAll();
+        }
+        else
+        {
+            cache.removeAll();
+        }
+    }
+
+    /**
+     * Processes a shutdown request.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void processDispose( String cacheName, long requesterId )
+        throws IOException
+    {
+        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
+        cache.dispose();
+    }
+
+    /**
+     * This general method should be deprecated.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void release()
+        throws IOException
+    {
+        //nothing.
+    }
+
+    /**
+     * This is called by the event log.
+     * <p>
+     * @param requesterId
+     * @return requesterId + ""
+     */
+    @Override
+    protected String getExtraInfoForRequesterId( long requesterId )
+    {
+        return requesterId + "";
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServlet.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServlet.java
new file mode 100644
index 0000000..ed7946d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServlet.java
@@ -0,0 +1,264 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+
+/**
+ * This servlet simply reads and writes objects. The requests are packaged in a general wrapper. The
+ * processor works on the wrapper object and returns a response wrapper.
+ */
+public class RemoteHttpCacheServlet
+    extends HttpServlet
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 8752849397531933346L;
+
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheServlet.class );
+
+    /** The cache manager */
+    private static CompositeCacheManager cacheMgr;
+
+    /** Processes requests */
+    private RemoteCacheServiceAdaptor<Serializable, Serializable> remoteHttpCacheServiceAdaptor;
+
+    /** This needs to be standard, since the other side is standard */
+    private final StandardSerializer serializer = new StandardSerializer();
+
+    /** Number of service calls. */
+    private int serviceCalls = 0;
+
+    /** The interval at which we will log the count. */
+    private final int logInterval = 100;
+
+    /**
+     * Initializes the cache.
+     * <p>
+     * This provides an easy extension point. Simply extend this servlet and override the init
+     * method to change the way the properties are loaded.
+     * @param config
+     * @throws ServletException
+     */
+    @Override
+    public void init( ServletConfig config )
+        throws ServletException
+    {
+        try
+        {
+            ensureCacheManager();
+        }
+        catch (CacheException e)
+        {
+            throw new ServletException(e);
+        }
+
+        setRemoteHttpCacheServiceAdaptor( new RemoteCacheServiceAdaptor<Serializable, Serializable>( cacheMgr ) );
+
+        super.init( config );
+    }
+
+    /**
+     * Read the request, call the processor, write the response.
+     * <p>
+     * @param request
+     * @param response
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    public void service( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException, IOException
+    {
+        incrementServiceCallCount();
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Servicing a request. " + request );
+        }
+
+        RemoteCacheRequest<Serializable, Serializable> remoteRequest = readRequest( request );
+        RemoteCacheResponse<Serializable> cacheResponse =
+            getRemoteHttpCacheServiceAdaptor().processRequest( remoteRequest );
+
+        writeResponse( response, cacheResponse );
+    }
+
+    /**
+     * Read the request from the input stream.
+     * <p>
+     * @param request
+     * @return RemoteHttpCacheRequest
+     */
+    protected RemoteCacheRequest<Serializable, Serializable> readRequest( HttpServletRequest request )
+    {
+        RemoteCacheRequest<Serializable, Serializable> remoteRequest = null;
+        try
+        {
+            InputStream inputStream = request.getInputStream();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "After getting input stream and before reading it" );
+            }
+
+            remoteRequest = readRequestFromStream( inputStream );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Could not get a RemoteHttpCacheRequest object from the input stream.", e );
+        }
+        return remoteRequest;
+    }
+
+    /**
+     * Reads the response from the stream and then closes it.
+     * <p>
+     * @param inputStream
+     * @return RemoteHttpCacheRequest
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    protected RemoteCacheRequest<Serializable, Serializable> readRequestFromStream( InputStream inputStream )
+        throws IOException, ClassNotFoundException
+    {
+        ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( inputStream, null );
+
+        @SuppressWarnings("unchecked") // Need to cast from Object
+        RemoteCacheRequest<Serializable, Serializable> remoteRequest
+            = (RemoteCacheRequest<Serializable, Serializable>) ois.readObject();
+        ois.close();
+        return remoteRequest;
+    }
+
+    /**
+     * Write the response to the output stream.
+     * <p>
+     * @param response
+     * @param cacheResponse
+     */
+    protected void writeResponse( HttpServletResponse response, RemoteCacheResponse<Serializable> cacheResponse )
+    {
+        try
+        {
+            response.setContentType( "application/octet-stream" );
+
+            byte[] responseAsByteAray = serializer.serialize( cacheResponse );
+            response.setContentLength( responseAsByteAray.length );
+
+            OutputStream outputStream = response.getOutputStream();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Opened output stream.  Response size: " + responseAsByteAray.length );
+            }
+            // WRITE
+            outputStream.write( responseAsByteAray );
+            outputStream.flush();
+            outputStream.close();
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem writing response. " + cacheResponse, e );
+        }
+    }
+
+    /**
+     * Log some details.
+     */
+    private void incrementServiceCallCount()
+    {
+        // not thread safe, but it doesn't have to be accurate
+        serviceCalls++;
+        if ( log.isInfoEnabled() )
+        {
+            if ( serviceCalls % logInterval == 0 )
+            {
+                log.info( "serviceCalls = " + serviceCalls );
+            }
+        }
+    }
+
+    /**
+     * Make sure we have a cache manager. This should have happened in the init method.
+     *
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    protected synchronized void ensureCacheManager() throws CacheException
+    {
+        if ( cacheMgr == null || !cacheMgr.isInitialized() )
+        {
+            cacheMgr = CompositeCacheManager.getInstance();
+        }
+    }
+
+    /** Release the cache manager. */
+    @Override
+    public void destroy()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Servlet Destroyed, shutting down JCS." );
+        }
+        cacheMgr.shutDown();
+    }
+
+    /**
+     * Get servlet information
+     * <p>
+     * @return basic info
+     */
+    @Override
+    public String getServletInfo()
+    {
+        return "RemoteHttpCacheServlet";
+    }
+
+    /**
+     * @param remoteHttpCacheProcessor the remoteHttpCacheProcessor to set
+     */
+    public void setRemoteHttpCacheServiceAdaptor( RemoteCacheServiceAdaptor<Serializable, Serializable> remoteHttpCacheProcessor )
+    {
+        this.remoteHttpCacheServiceAdaptor = remoteHttpCacheProcessor;
+    }
+
+    /**
+     * @return the remoteHttpCacheProcessor
+     */
+    public RemoteCacheServiceAdaptor<Serializable, Serializable> getRemoteHttpCacheServiceAdaptor()
+    {
+        return remoteHttpCacheServiceAdaptor;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactory.java
new file mode 100644
index 0000000..af18ed8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactory.java
@@ -0,0 +1,91 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
+import org.apache.commons.jcs.auxiliary.remote.http.behavior.IRemoteHttpCacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.config.PropertySetter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+/** Creates the server. */
+public class RemoteHttpCacheSeviceFactory
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteHttpCacheSeviceFactory.class );
+
+    /**
+     * Configures the attributes and the event logger and constructs a service.
+     * <p>
+     * @param cacheManager
+     * @return RemoteHttpCacheService
+     */
+    public static <K extends Serializable, V extends Serializable> RemoteHttpCacheService<K, V> createRemoteHttpCacheService( ICompositeCacheManager cacheManager )
+    {
+        Properties props = cacheManager.getConfigurationProperties();
+        ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props );
+        RemoteHttpCacheServerAttributes attributes = configureRemoteHttpCacheServerAttributes( props );
+
+        RemoteHttpCacheService<K, V> service = new RemoteHttpCacheService<K, V>( cacheManager, attributes, cacheEventLogger );
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Created new RemoteHttpCacheService " + service );
+        }
+        return service;
+    }
+
+    /**
+     * Tries to get the event logger.
+     * <p>
+     * @param props
+     * @return ICacheEventLogger
+     */
+    protected static ICacheEventLogger configureCacheEventLogger( Properties props )
+    {
+        ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator
+            .parseCacheEventLogger( props, IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX );
+
+        return cacheEventLogger;
+    }
+
+    /**
+     * Configure.
+     * <p>
+     * jcs.remotehttpcache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE
+     * <p>
+     * @param prop
+     * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes
+     */
+    protected static RemoteHttpCacheServerAttributes configureRemoteHttpCacheServerAttributes( Properties prop )
+    {
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+
+        // configure automatically
+        PropertySetter.setProperties( rcsa, prop,
+                                      IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." );
+
+        return rcsa;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/package.html
new file mode 100644
index 0000000..e002b4e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    Root package for the remote auxiliary cache.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java
new file mode 100644
index 0000000..0949c83
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java
@@ -0,0 +1,189 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.RemoteUtils;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.rmi.Naming;
+import java.rmi.RemoteException;
+import java.rmi.registry.Registry;
+
+/**
+ * This class tries to keep the registry alive. If if is able to create a registry, it will also
+ * rebind the remote cache server.
+ */
+public class RegistryKeepAliveRunner
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RegistryKeepAliveRunner.class );
+
+    /** The URL of the service to look for. */
+    private String namingURL;
+
+    /** the port on which to start the registry */
+    private int registryPort;
+
+    /** An optional event logger */
+    private ICacheEventLogger cacheEventLogger;
+
+    /**
+     * @param registryHost - Hostname of the registry
+     * @param registryPort - the port on which to start the registry
+     * @param serviceName
+     */
+    public RegistryKeepAliveRunner( String registryHost, int registryPort, String serviceName )
+    {
+        this.namingURL = RemoteUtils.getNamingURL(registryHost, registryPort, serviceName);
+        this.registryPort = registryPort;
+    }
+
+    /**
+     * Tries to lookup the server. If unsuccessful it will rebind the server using the factory
+     * rebind method.
+     * <p>
+     */
+    @Override
+    public void run()
+    {
+        checkAndRestoreIfNeeded();
+    }
+
+    /**
+     * Tries to lookup the server. If unsuccessful it will rebind the server using the factory
+     * rebind method.
+     */
+    protected void checkAndRestoreIfNeeded()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "looking up server " + namingURL );
+        }
+        try
+        {
+            Object obj = Naming.lookup( namingURL );
+
+            // Successful connection to the remote server.
+            String message = "RMI registry looks fine.  Found [" + obj + "] in registry [" + namingURL + "]";
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logApplicationEvent( "RegistryKeepAliveRunner", "Naming.lookup", message );
+            }
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( message );
+            }
+            obj = null;
+        }
+        catch ( Exception ex )
+        {
+            // Failed to connect to the remote server.
+            String message = "Problem finding server at [" + namingURL
+                + "].  Will attempt to start registry and rebind.";
+            log.error( message, ex );
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logError( "RegistryKeepAliveRunner", "Naming.lookup", message + ":" + ex.getMessage() );
+            }
+            createAndRegister( namingURL );
+        }
+    }
+
+    /**
+     * Creates the registry and registers the server.
+     * <p>
+     * @param registry
+     */
+    protected void createAndRegister( String registry )
+    {
+        createReqistry( registry );
+        registerServer( registry );
+    }
+
+    /**
+     * Try to create the registry. Log errors
+     * <p>
+     * @param registry
+     */
+    protected void createReqistry( String registry )
+    {
+        Registry reg = RemoteUtils.createRegistry(registryPort);
+
+        if ( cacheEventLogger != null )
+        {
+            if (reg != null)
+            {
+                cacheEventLogger.logApplicationEvent( "RegistryKeepAliveRunner", "createRegistry",
+                        "Successfully created registry [" + registry + "]." );
+            }
+            else
+            {
+                cacheEventLogger.logError( "RegistryKeepAliveRunner", "createRegistry",
+                        "Could not start registry [" + registry + "]." );
+            }
+        }
+    }
+
+    /**
+     * Try to rebind the server.
+     * <p>
+     * @param registry
+     */
+    protected void registerServer( String registry )
+    {
+        try
+        {
+            // try to rebind anyway
+            RemoteCacheServerFactory.registerServer( registry, RemoteCacheServerFactory.getRemoteCacheServer() );
+            String message = "Successfully rebound server to registry [" + registry + "].";
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logApplicationEvent( "RegistryKeepAliveRunner", "registerServer", message );
+            }
+            if ( log.isInfoEnabled() )
+            {
+                log.info( message );
+            }
+        }
+        catch ( RemoteException e )
+        {
+            String message = "Could not rebind server to registry [" + registry + "].";
+            log.error( message, e );
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logError( "RegistryKeepAliveRunner", "registerServer", message + ":"
+                    + e.getMessage() );
+            }
+        }
+    }
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @param cacheEventLogger
+     */
+    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheEventLogger = cacheEventLogger;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServer.java
new file mode 100644
index 0000000..7fbbcbc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServer.java
@@ -0,0 +1,1707 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.rmi.RemoteException;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMISocketFactory;
+import java.rmi.server.UnicastRemoteObject;
+import java.rmi.server.Unreferenced;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.IRemoteCacheServer;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.IRemoteCacheServerAttributes;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.CacheEventQueueFactory;
+import org.apache.commons.jcs.engine.CacheListeners;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.logging.CacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class provides remote cache services. The remote cache server propagates events from local
+ * caches to other local caches. It can also store cached data, making it available to new clients.
+ * <p>
+ * Remote cache servers can be clustered. If the cache used by this remote cache is configured to
+ * use a remote cache of type cluster, the two remote caches will communicate with each other.
+ * Remote and put requests can be sent from one remote to another. If they are configured to
+ * broadcast such event to their client, then remove an puts can be sent to all locals in the
+ * cluster.
+ * <p>
+ * Get requests are made between clustered servers if AllowClusterGet is true. You can setup several
+ * clients to use one remote server and several to use another. The get local will be distributed
+ * between the two servers. Since caches are usually high get and low put, this should allow you to
+ * scale.
+ */
+public class RemoteCacheServer<K, V>
+    extends UnicastRemoteObject
+    implements IRemoteCacheServer<K, V>, Unreferenced
+{
+    /** For serialization. Don't change. */
+    private static final long serialVersionUID = -8072345435941473116L;
+
+    /** log instance */
+    private static final Log log = LogFactory.getLog( RemoteCacheServer.class );
+
+    /** timing -- if we should record operation times. */
+    private static final boolean timing = true;
+
+    /** Number of puts into the cache. */
+    private int puts = 0;
+
+    /** Maps cache name to CacheListeners object. association of listeners (regions). */
+    private final Map<String, CacheListeners<K, V>> cacheListenersMap =
+        new ConcurrentHashMap<String, CacheListeners<K, V>>();
+
+    /** maps cluster listeners to regions. */
+    private final Map<String, CacheListeners<K, V>> clusterListenersMap =
+        new ConcurrentHashMap<String, CacheListeners<K, V>>();
+
+    /** The central hub */
+    private transient CompositeCacheManager cacheManager;
+
+    /** relates listener id with a type */
+    private final Map<Long, RemoteType> idTypeMap = new ConcurrentHashMap<Long, RemoteType>();
+
+    /** relates listener id with an ip address */
+    private final Map<Long, String> idIPMap = new ConcurrentHashMap<Long, String>();
+
+    /** Used to get the next listener id. */
+    private final int[] listenerId = new int[1];
+
+    /** Configuration settings. */
+    // package protected for access by unit test code
+    final IRemoteCacheServerAttributes remoteCacheServerAttributes;
+
+    /** The interval at which we will log updates. */
+    private final int logInterval = 100;
+
+    /** An optional event logger */
+    private transient ICacheEventLogger cacheEventLogger;
+
+    /**
+     * Constructor for the RemoteCacheServer object. This initializes the server with the values
+     * from the config file.
+     * <p>
+     * @param rcsa
+     * @throws RemoteException
+     */
+    protected RemoteCacheServer( IRemoteCacheServerAttributes rcsa )
+        throws RemoteException
+    {
+        super( rcsa.getServicePort() );
+        this.remoteCacheServerAttributes = rcsa;
+        init( rcsa.getConfigFileName() );
+    }
+
+    /**
+     * Constructor for the RemoteCacheServer object. This initializes the server with the values
+     * from the config file.
+     * <p>
+     * @param rcsa
+     * @param customRMISocketFactory
+     * @throws RemoteException
+     */
+    protected RemoteCacheServer( IRemoteCacheServerAttributes rcsa, RMISocketFactory customRMISocketFactory )
+        throws RemoteException
+    {
+        super( rcsa.getServicePort(), customRMISocketFactory, customRMISocketFactory );
+        this.remoteCacheServerAttributes = rcsa;
+        init( rcsa.getConfigFileName() );
+    }
+
+    /**
+     * Initialize the RMI Cache Server from a properties file.
+     * <p>
+     * @param prop
+     * @throws RemoteException if the configuration of the cache manager instance fails
+     */
+    private void init( String prop ) throws RemoteException
+    {
+        try
+        {
+            cacheManager = createCacheManager( prop );
+        }
+        catch (CacheException e)
+        {
+            throw new RemoteException(e.getMessage(), e);
+        }
+
+        // cacheManager would have created a number of ICache objects.
+        // Use these objects to set up the cacheListenersMap.
+        String[] list = cacheManager.getCacheNames();
+        for ( int i = 0; i < list.length; i++ )
+        {
+            String name = list[i];
+            CompositeCache<K, V> cache = cacheManager.getCache( name );
+            cacheListenersMap.put( name, new CacheListeners<K, V>( cache ) );
+        }
+    }
+
+    /**
+     * Subclass can override this method to create the specific cache manager.
+     * <p>
+     * @param prop The name of the configuration file.
+     * @return The cache hub configured with this configuration file.
+     *
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    private CompositeCacheManager createCacheManager( String prop ) throws CacheException
+    {
+        CompositeCacheManager hub = CompositeCacheManager.getUnconfiguredInstance();
+
+        if ( prop == null )
+        {
+            hub.configure( "/remote.cache.ccf" );
+        }
+        else
+        {
+            hub.configure( prop );
+        }
+        return hub;
+    }
+
+    /**
+     * Puts a cache bean to the remote cache and notifies all listeners which <br>
+     * <ol>
+     * <li>have a different listener id than the originating host; <li>are currently subscribed to
+     * the related cache.
+     * </ol>
+     * <p>
+     * @param item
+     * @throws IOException
+     */
+    public void put( ICacheElement<K, V> item )
+        throws IOException
+    {
+        update( item );
+    }
+
+    /**
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+        throws IOException
+    {
+        update( item, 0 );
+    }
+
+    /**
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<ICacheElement<K, V>> cacheEvent = createICacheEvent( item, requesterId, ICacheEventLogger.UPDATE_EVENT );
+        try
+        {
+            processUpdate( item, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * An update can come from either a local cache's remote auxiliary, or it can come from a remote
+     * server. A remote server is considered a a source of type cluster.
+     * <p>
+     * If the update came from a cluster, then we should tell the cache manager that this was a
+     * remote put. This way, any lateral and remote auxiliaries configured for the region will not
+     * be updated. This is basically how a remote listener works when plugged into a local cache.
+     * <p>
+     * If the cluster is configured to keep local cluster consistency, then all listeners will be
+     * updated. This allows cluster server A to update cluster server B and then B to update its
+     * clients if it is told to keep local cluster consistency. Otherwise, server A will update
+     * server B and B will not tell its clients. If you cluster using lateral caches for instance,
+     * this is how it will work. Updates to a cluster node, will never get to the leaves. The remote
+     * cluster, with local cluster consistency, allows you to update leaves. This basically allows
+     * you to have a failover remote server.
+     * <p>
+     * Since currently a cluster will not try to get from other cluster servers, you can scale a bit
+     * with a cluster configuration. Puts and removes will be broadcasted to all clients, but the
+     * get load on a remote server can be reduced.
+     * <p>
+     * @param item
+     * @param requesterId
+     */
+    private void processUpdate( ICacheElement<K, V> item, long requesterId )
+    {
+        long start = 0;
+        if ( timing )
+        {
+            start = System.currentTimeMillis();
+        }
+
+        logUpdateInfo( item );
+
+        try
+        {
+            CacheListeners<K, V> cacheDesc = getCacheListeners( item.getCacheName() );
+            /* Object val = */item.getVal();
+
+            boolean fromCluster = isRequestFromCluster( requesterId );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "In update, requesterId = [" + requesterId + "] fromCluster = " + fromCluster );
+            }
+
+            // ordered cache item update and notification.
+            synchronized ( cacheDesc )
+            {
+                try
+                {
+                    CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+                    // If the source of this request was not from a cluster,
+                    // then consider it a local update. The cache manager will
+                    // try to
+                    // update all auxiliaries.
+                    //
+                    // This requires that two local caches not be connected to
+                    // two clustered remote caches. The failover runner will
+                    // have to make sure of this. ALos, the local cache needs
+                    // avoid updating this source. Will need to pass the source
+                    // id somehow. The remote cache should update all local
+                    // caches
+                    // but not update the cluster source. Cluster remote caches
+                    // should only be updated by the server and not the
+                    // RemoteCache.
+                    if ( fromCluster )
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Put FROM cluster, NOT updating other auxiliaries for region. "
+                                + " requesterId [" + requesterId + "]" );
+                        }
+                        c.localUpdate( item );
+                    }
+                    else
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Put NOT from cluster, updating other auxiliaries for region. "
+                                + " requesterId [" + requesterId + "]" );
+                        }
+                        c.update( item );
+                    }
+                }
+                catch ( Exception ce )
+                {
+                    // swallow
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "Exception caught updating item. requesterId [" + requesterId + "] "
+                            + ce.getMessage() );
+                    }
+                }
+
+                // UPDATE LOCALS IF A REQUEST COMES FROM A CLUSTER
+                // IF LOCAL CLUSTER CONSISTENCY IS CONFIGURED
+                if ( !fromCluster || ( fromCluster && remoteCacheServerAttributes.isLocalClusterConsistency() ) )
+                {
+                    ICacheEventQueue<K, V>[] qlist = getEventQList( cacheDesc, requesterId );
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "qlist.length = " + qlist.length );
+                    }
+                    for ( int i = 0; i < qlist.length; i++ )
+                    {
+                        qlist[i].addPutEvent( item );
+                    }
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logError( "RemoteCacheServer", ICacheEventLogger.UPDATE_EVENT, e.getMessage()
+                    + " REGION: " + item.getCacheName() + " ITEM: " + item );
+            }
+
+            log.error( "Trouble in Update. requesterId [" + requesterId + "]", e );
+        }
+
+        // TODO use JAMON for timing
+        if ( timing )
+        {
+            long end = System.currentTimeMillis();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "put took " + String.valueOf( end - start ) + " ms." );
+            }
+        }
+    }
+
+    /**
+     * Log some details.
+     * <p>
+     * @param item
+     */
+    private void logUpdateInfo( ICacheElement<K, V> item )
+    {
+        // not thread safe, but it doesn't have to be 100% accurate
+        puts++;
+
+        if ( log.isInfoEnabled() )
+        {
+            if ( puts % logInterval == 0 )
+            {
+                log.info( "puts = " + puts );
+            }
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "In update, put [" + item.getKey() + "] in [" + item.getCacheName() + "]" );
+        }
+    }
+
+    /**
+     * Returns a cache value from the specified remote cache; or null if the cache or key does not
+     * exist.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+        throws IOException
+    {
+        return this.get( cacheName, key, 0 );
+    }
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * Adding the requestor id, allows the cache to determine the source of the get.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        ICacheElement<K, V> element = null;
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.GET_EVENT );
+        try
+        {
+            element = processGet( cacheName, key, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+        return element;
+    }
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * Adding the requester id, allows the cache to determine the source of the get.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     */
+    private ICacheElement<K, V> processGet( String cacheName, K key, long requesterId )
+    {
+        boolean fromCluster = isRequestFromCluster( requesterId );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "get [" + key + "] from cache [" + cacheName + "] requesterId = [" + requesterId
+                + "] fromCluster = " + fromCluster );
+        }
+
+        CacheListeners<K, V> cacheDesc = null;
+        try
+        {
+            cacheDesc = getCacheListeners( cacheName );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem getting listeners.", e );
+
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logError( "RemoteCacheServer", ICacheEventLogger.GET_EVENT, e.getMessage() + cacheName
+                    + " KEY: " + key );
+            }
+        }
+
+        ICacheElement<K, V> element = null;
+
+        element = getFromCacheListeners( key, fromCluster, cacheDesc, element );
+        return element;
+    }
+
+    /**
+     * Gets the item from the associated cache listeners.
+     * <p>
+     * @param key
+     * @param fromCluster
+     * @param cacheDesc
+     * @param element
+     * @return ICacheElement
+     */
+    private ICacheElement<K, V> getFromCacheListeners( K key, boolean fromCluster, CacheListeners<K, V> cacheDesc,
+                                                 ICacheElement<K, V> element )
+    {
+        ICacheElement<K, V> returnElement = element;
+
+        if ( cacheDesc != null )
+        {
+            CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+            // If we have a get come in from a client and we don't have the item
+            // locally, we will allow the cache to look in other non local sources,
+            // such as a remote cache or a lateral.
+            //
+            // Since remote servers never get from clients and clients never go
+            // remote from a remote call, this
+            // will not result in any loops.
+            //
+            // This is the only instance I can think of where we allow a remote get
+            // from a remote call. The purpose is to allow remote cache servers to
+            // talk to each other. If one goes down, you want it to be able to get
+            // data from those that were up when the failed server comes back o
+            // line.
+
+            if ( !fromCluster && this.remoteCacheServerAttributes.isAllowClusterGet() )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "NonLocalGet. fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+                returnElement = c.get( key );
+            }
+            else
+            {
+                // Gets from cluster type remote will end up here.
+                // Gets from all clients will end up here if allow cluster get is
+                // false.
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "LocalGet.  fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+                returnElement = c.localGet( key );
+            }
+        }
+
+        return returnElement;
+    }
+
+    /**
+     * Gets all matching items.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return Map of keys and wrapped objects
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, 0 );
+    }
+
+    /**
+     * Retrieves all matching keys.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map of keys and wrapped objects
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, pattern, requesterId,
+                                                    ICacheEventLogger.GETMATCHING_EVENT );
+        try
+        {
+            return processGetMatching( cacheName, pattern, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Retrieves all matching keys.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map of keys and wrapped objects
+     */
+    protected Map<K, ICacheElement<K, V>> processGetMatching( String cacheName, String pattern, long requesterId )
+    {
+        boolean fromCluster = isRequestFromCluster( requesterId );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "getMatching [" + pattern + "] from cache [" + cacheName + "] requesterId = [" + requesterId
+                + "] fromCluster = " + fromCluster );
+        }
+
+        CacheListeners<K, V> cacheDesc = null;
+        try
+        {
+            cacheDesc = getCacheListeners( cacheName );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem getting listeners.", e );
+
+            if ( cacheEventLogger != null )
+            {
+                cacheEventLogger.logError( "RemoteCacheServer", ICacheEventLogger.GETMATCHING_EVENT, e.getMessage()
+                    + cacheName + " pattern: " + pattern );
+            }
+        }
+
+        return getMatchingFromCacheListeners( pattern, fromCluster, cacheDesc );
+    }
+
+    /**
+     * Gets the item from the associated cache listeners.
+     * <p>
+     * @param pattern
+     * @param fromCluster
+     * @param cacheDesc
+     * @return Map of keys to results
+     */
+    private Map<K, ICacheElement<K, V>> getMatchingFromCacheListeners( String pattern, boolean fromCluster, CacheListeners<K, V> cacheDesc )
+    {
+        Map<K, ICacheElement<K, V>> elements = null;
+        if ( cacheDesc != null )
+        {
+            CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+            // We always want to go remote and then merge the items.  But this can lead to inconsistencies after
+            // failover recovery.  Removed items may show up.  There is no good way to prevent this.
+            // We should make it configurable.
+
+            if ( !fromCluster && this.remoteCacheServerAttributes.isAllowClusterGet() )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "NonLocalGetMatching. fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+                elements = c.getMatching( pattern );
+            }
+            else
+            {
+                // Gets from cluster type remote will end up here.
+                // Gets from all clients will end up here if allow cluster get is
+                // false.
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "LocalGetMatching.  fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+                elements = c.localGetMatching( pattern );
+            }
+        }
+        return elements;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+        throws IOException
+    {
+        return this.getMultiple( cacheName, keys, 0 );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<Serializable> cacheEvent = createICacheEvent( cacheName, (Serializable) keys, requesterId,
+                                                    ICacheEventLogger.GETMULTIPLE_EVENT );
+        try
+        {
+            return processGetMultiple( cacheName, keys, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    private Map<K, ICacheElement<K, V>> processGetMultiple( String cacheName, Set<K> keys, long requesterId )
+    {
+        Map<K, ICacheElement<K, V>> elements = null;
+
+        boolean fromCluster = isRequestFromCluster( requesterId );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "getMultiple [" + keys + "] from cache [" + cacheName + "] requesterId = [" + requesterId
+                + "] fromCluster = " + fromCluster );
+        }
+
+        CacheListeners<K, V> cacheDesc = null;
+        try
+        {
+            cacheDesc = getCacheListeners( cacheName );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem getting listeners.", e );
+        }
+
+        elements = getMultipleFromCacheListeners( keys, elements, fromCluster, cacheDesc );
+        return elements;
+    }
+
+    /**
+     * Since a non-receiving remote cache client will not register a listener, it will not have a
+     * listener id assigned from the server. As such the remote server cannot determine if it is a
+     * cluster or a normal client. It will assume that it is a normal client.
+     * <p>
+     * @param requesterId
+     * @return true is from a cluster.
+     */
+    private boolean isRequestFromCluster( long requesterId )
+    {
+        RemoteType remoteTypeL = idTypeMap.get( Long.valueOf( requesterId ) );
+        return remoteTypeL == RemoteType.CLUSTER;
+    }
+
+    /**
+     * Gets the items from the associated cache listeners.
+     * <p>
+     * @param keys
+     * @param elements
+     * @param fromCluster
+     * @param cacheDesc
+     * @return Map
+     */
+    private Map<K, ICacheElement<K, V>> getMultipleFromCacheListeners( Set<K> keys, Map<K, ICacheElement<K, V>> elements, boolean fromCluster, CacheListeners<K, V> cacheDesc )
+    {
+        Map<K, ICacheElement<K, V>> returnElements = elements;
+
+        if ( cacheDesc != null )
+        {
+            CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+            // If we have a getMultiple come in from a client and we don't have the item
+            // locally, we will allow the cache to look in other non local sources,
+            // such as a remote cache or a lateral.
+            //
+            // Since remote servers never get from clients and clients never go
+            // remote from a remote call, this
+            // will not result in any loops.
+            //
+            // This is the only instance I can think of where we allow a remote get
+            // from a remote call. The purpose is to allow remote cache servers to
+            // talk to each other. If one goes down, you want it to be able to get
+            // data from those that were up when the failed server comes back on
+            // line.
+
+            if ( !fromCluster && this.remoteCacheServerAttributes.isAllowClusterGet() )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "NonLocalGetMultiple. fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+
+                returnElements = c.getMultiple( keys );
+            }
+            else
+            {
+                // Gets from cluster type remote will end up here.
+                // Gets from all clients will end up here if allow cluster get is
+                // false.
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "LocalGetMultiple.  fromCluster [" + fromCluster + "] AllowClusterGet ["
+                        + this.remoteCacheServerAttributes.isAllowClusterGet() + "]" );
+                }
+
+                returnElements = c.localGetMultiple( keys );
+            }
+        }
+
+        return returnElements;
+    }
+
+    /**
+     * Return the keys in the cache.
+     * <p>
+     * @param cacheName the name of the cache region
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet(String cacheName) throws IOException
+    {
+        return processGetKeySet( cacheName );
+    }
+
+    /**
+     * Gets the set of keys of objects currently in the cache.
+     * <p>
+     * @param cacheName
+     * @return Set
+     */
+    protected Set<K> processGetKeySet( String cacheName )
+    {
+        CacheListeners<K, V> cacheDesc = null;
+        try
+        {
+            cacheDesc = getCacheListeners( cacheName );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem getting listeners.", e );
+        }
+
+        if ( cacheDesc == null )
+        {
+            return Collections.emptySet();
+        }
+
+        CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+        return c.getKeySet();
+    }
+
+    /**
+     * Removes the given key from the specified remote cache. Defaults the listener id to 0.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key )
+        throws IOException
+    {
+        remove( cacheName, key, 0 );
+    }
+
+    /**
+     * Remove the key from the cache region and don't tell the source listener about it.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.REMOVE_EVENT );
+        try
+        {
+            processRemove( cacheName, key, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Remove the key from the cache region and don't tell the source listener about it.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    private void processRemove( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "remove [" + key + "] from cache [" + cacheName + "]" );
+        }
+
+        CacheListeners<K, V> cacheDesc = cacheListenersMap.get( cacheName );
+
+        boolean fromCluster = isRequestFromCluster( requesterId );
+
+        if ( cacheDesc != null )
+        {
+            // best attempt to achieve ordered cache item removal and
+            // notification.
+            synchronized ( cacheDesc )
+            {
+                boolean removeSuccess = false;
+
+                // No need to notify if it was not cached.
+                CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+                if ( fromCluster )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Remove FROM cluster, NOT updating other auxiliaries for region" );
+                    }
+                    removeSuccess = c.localRemove( key );
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Remove NOT from cluster, updating other auxiliaries for region" );
+                    }
+                    removeSuccess = c.remove( key );
+                }
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "remove [" + key + "] from cache [" + cacheName + "] success (was it found) = "
+                        + removeSuccess );
+                }
+
+                // UPDATE LOCALS IF A REQUEST COMES FROM A CLUSTER
+                // IF LOCAL CLUSTER CONSISTENCY IS CONFIGURED
+                if ( !fromCluster || ( fromCluster && remoteCacheServerAttributes.isLocalClusterConsistency() ) )
+                {
+                    ICacheEventQueue<K, V>[] qlist = getEventQList( cacheDesc, requesterId );
+
+                    for ( int i = 0; i < qlist.length; i++ )
+                    {
+                        qlist[i].addRemoveEvent( key );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName )
+        throws IOException
+    {
+        removeAll( cacheName, 0 );
+    }
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * The internal processing is wrapped in event logging calls.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "all", requesterId, ICacheEventLogger.REMOVEALL_EVENT );
+        try
+        {
+            processRemoveAll( cacheName, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Remove all keys from the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    private void processRemoveAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        CacheListeners<K, V> cacheDesc = cacheListenersMap.get( cacheName );
+
+        boolean fromCluster = isRequestFromCluster( requesterId );
+
+        if ( cacheDesc != null )
+        {
+            // best attempt to achieve ordered cache item removal and
+            // notification.
+            synchronized ( cacheDesc )
+            {
+                // No need to broadcast, or notify if it was not cached.
+                CompositeCache<K, V> c = (CompositeCache<K, V>) cacheDesc.cache;
+
+                if ( fromCluster )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "RemoveALL FROM cluster, NOT updating other auxiliaries for region" );
+                    }
+                    c.localRemoveAll();
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "RemoveALL NOT from cluster, updating other auxiliaries for region" );
+                    }
+                    c.removeAll();
+                }
+
+                // update registered listeners
+                if ( !fromCluster || ( fromCluster && remoteCacheServerAttributes.isLocalClusterConsistency() ) )
+                {
+                    ICacheEventQueue<K, V>[] qlist = getEventQList( cacheDesc, requesterId );
+
+                    for ( int i = 0; i < qlist.length; i++ )
+                    {
+                        qlist[i].addRemoveAllEvent();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * How many put events have we received.
+     * <p>
+     * @return puts
+     */
+    // Currently only intended for use by unit tests
+    int getPutCount()
+    {
+        return puts;
+    }
+
+    /**
+     * Frees the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void dispose( String cacheName )
+        throws IOException
+    {
+        dispose( cacheName, 0 );
+    }
+
+    /**
+     * Frees the specified remote cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    public void dispose( String cacheName, long requesterId )
+        throws IOException
+    {
+        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "none", requesterId, ICacheEventLogger.DISPOSE_EVENT );
+        try
+        {
+            processDispose( cacheName, requesterId );
+        }
+        finally
+        {
+            logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    private void processDispose( String cacheName, long requesterId )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Dispose request received from listener [" + requesterId + "]" );
+        }
+
+        CacheListeners<K, V> cacheDesc = cacheListenersMap.get( cacheName );
+
+        // this is dangerous
+        if ( cacheDesc != null )
+        {
+            // best attempt to achieve ordered free-cache-op and notification.
+            synchronized ( cacheDesc )
+            {
+                ICacheEventQueue<K, V>[] qlist = getEventQList( cacheDesc, requesterId );
+
+                for ( int i = 0; i < qlist.length; i++ )
+                {
+                    qlist[i].addDisposeEvent();
+                }
+                cacheManager.freeCache( cacheName );
+            }
+        }
+    }
+
+    /**
+     * Frees all remote caches.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void release()
+        throws IOException
+    {
+        synchronized ( cacheListenersMap )
+        {
+            for (CacheListeners<K, V> cacheDesc : cacheListenersMap.values())
+            {
+                ICacheEventQueue<K, V>[] qlist = getEventQList( cacheDesc, 0 );
+
+                for ( int i = 0; i < qlist.length; i++ )
+                {
+                    qlist[i].addDisposeEvent();
+                }
+            }
+            cacheManager.release();
+        }
+    }
+
+    /**
+     * Returns the cache listener for the specified cache. Creates the cache and the cache
+     * descriptor if they do not already exist.
+     * <p>
+     * @param cacheName
+     * @return The cacheListeners value
+     */
+    protected CacheListeners<K, V> getCacheListeners( String cacheName )
+    {
+        CacheListeners<K, V> cacheListeners = cacheListenersMap.get( cacheName );
+        synchronized ( cacheListenersMap )
+        {
+            if ( cacheListeners == null )
+            {
+                cacheListeners = cacheListenersMap.get( cacheName );
+                if ( cacheListeners == null )
+                {
+                    CompositeCache<K, V> cache = cacheManager.getCache( cacheName );
+                    cacheListeners = new CacheListeners<K, V>( cache );
+                    cacheListenersMap.put( cacheName, cacheListeners );
+                }
+            }
+        }
+        return cacheListeners;
+    }
+
+    /**
+     * Gets the clusterListeners attribute of the RemoteCacheServer object.
+     * <p>
+     * TODO may be able to remove this
+     * @param cacheName
+     * @return The clusterListeners value
+     */
+    protected CacheListeners<K, V> getClusterListeners( String cacheName )
+    {
+        CacheListeners<K, V> cacheListeners = clusterListenersMap.get( cacheName );
+        synchronized ( clusterListenersMap )
+        {
+            if ( cacheListeners == null )
+            {
+                cacheListeners = clusterListenersMap.get( cacheName );
+                if ( cacheListeners == null )
+                {
+                    CompositeCache<K, V> cache = cacheManager.getCache( cacheName );
+                    cacheListeners = new CacheListeners<K, V>( cache );
+                    clusterListenersMap.put( cacheName, cacheListeners );
+                }
+            }
+        }
+        return cacheListeners;
+    }
+
+    /**
+     * Gets the eventQList attribute of the RemoteCacheServer object. This returns the event queues
+     * stored in the cacheListeners object for a particular region, if the queue is not for this
+     * requester.
+     * <p>
+     * Basically, this makes sure that a request from a particular local cache, identified by its
+     * listener id, does not result in a call to that same listener.
+     * <p>
+     * @param cacheListeners
+     * @param requesterId
+     * @return The eventQList value
+     */
+    @SuppressWarnings("unchecked") // No generic arrays in java
+    private ICacheEventQueue<K, V>[] getEventQList( CacheListeners<K, V> cacheListeners, long requesterId )
+    {
+        ICacheEventQueue<K, V>[] list = null;
+        synchronized ( cacheListeners.eventQMap )
+        {
+            list = cacheListeners.eventQMap.values().toArray( new ICacheEventQueue[0] );
+        }
+        int count = 0;
+        // Set those not qualified to null; Count those qualified.
+        for ( int i = 0; i < list.length; i++ )
+        {
+            ICacheEventQueue<K, V> q = list[i];
+            if ( q.isWorking() && q.getListenerId() != requesterId )
+            {
+                count++;
+            }
+            else
+            {
+                list[i] = null;
+            }
+        }
+        if ( count == list.length )
+        {
+            // All qualified.
+            return list;
+        }
+
+        // Returns only the qualified.
+        ICacheEventQueue<K, V>[] qq = new ICacheEventQueue[count];
+        count = 0;
+        for ( int i = 0; i < list.length; i++ )
+        {
+            if ( list[i] != null )
+            {
+                qq[count++] = list[i];
+            }
+        }
+        return qq;
+    }
+
+    /**
+     * Removes dead event queues. Should clean out deregistered listeners.
+     * <p>
+     * @param eventQMap
+     */
+    private static <KK, VV> void cleanupEventQMap( Map<Long, ICacheEventQueue<KK, VV>> eventQMap )
+    {
+        synchronized ( eventQMap )
+        {
+            for (Iterator<Map.Entry<Long, ICacheEventQueue<KK, VV>>> itr = eventQMap.entrySet().iterator(); itr.hasNext(); )
+            {
+                Map.Entry<Long, ICacheEventQueue<KK, VV>> e = itr.next();
+                ICacheEventQueue<KK, VV> q = e.getValue();
+
+                // this does not care if the q is alive (i.e. if
+                // there are active threads; it cares if the queue
+                // is working -- if it has not encountered errors
+                // above the failure threshold
+                if ( !q.isWorking() )
+                {
+                    itr.remove();
+                    log.warn( "Cache event queue " + q + " is not working and removed from cache server." );
+                }
+            }
+        }
+    }
+
+    /**
+     * Subscribes to the specified remote cache.
+     * <p>
+     * If the client id is 0, then the remote cache server will increment it's local count and
+     * assign an id to the client.
+     * <p>
+     * @param cacheName the specified remote cache.
+     * @param listener object to notify for cache changes. must be synchronized since there are
+     *            remote calls involved.
+     * @throws IOException
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Need to cast to specific return type from getClusterListeners()
+    public <KK, VV> void addCacheListener( String cacheName, ICacheListener<KK, VV> listener )
+        throws IOException
+    {
+        if ( cacheName == null || listener == null )
+        {
+            throw new IllegalArgumentException( "cacheName and listener must not be null" );
+        }
+        CacheListeners<KK, VV> cacheListeners;
+
+        IRemoteCacheListener<KK, VV> ircl = (IRemoteCacheListener<KK, VV>) listener;
+
+        String listenerAddress = ircl.getLocalHostAddress();
+
+        RemoteType remoteType = ircl.getRemoteType();
+        if ( remoteType == RemoteType.CLUSTER )
+        {
+            log.debug( "adding cluster listener, listenerAddress [" + listenerAddress + "]" );
+            cacheListeners = (CacheListeners<KK, VV>)getClusterListeners( cacheName );
+        }
+        else
+        {
+            log.debug( "adding normal listener, listenerAddress [" + listenerAddress + "]" );
+            cacheListeners = (CacheListeners<KK, VV>)getCacheListeners( cacheName );
+        }
+        Map<Long, ICacheEventQueue<KK, VV>> eventQMap = cacheListeners.eventQMap;
+        cleanupEventQMap( eventQMap );
+
+        // synchronized ( listenerId )
+        synchronized ( ICacheListener.class )
+        {
+            long id = 0;
+            try
+            {
+                id = listener.getListenerId();
+                // clients probably shouldn't do this.
+                if ( id == 0 )
+                {
+                    // must start at one so the next gets recognized
+                    long listenerIdB = nextListenerId();
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "listener id=" + ( listenerIdB & 0xff ) + " addded for cache [" + cacheName
+                            + "], listenerAddress [" + listenerAddress + "]" );
+                    }
+                    listener.setListenerId( listenerIdB );
+                    id = listenerIdB;
+
+                    // in case it needs synchronization
+                    String message = "Adding vm listener under new id = [" + listenerIdB + "], listenerAddress ["
+                        + listenerAddress + "]";
+                    logApplicationEvent( "RemoteCacheServer", "addCacheListener", message );
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( message );
+                    }
+                }
+                else
+                {
+                    String message = "Adding listener under existing id = [" + id + "], listenerAddress ["
+                        + listenerAddress + "]";
+                    logApplicationEvent( "RemoteCacheServer", "addCacheListener", message );
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( message );
+                    }
+                    // should confirm the the host is the same as we have on
+                    // record, just in case a client has made a mistake.
+                }
+
+                // relate the type to an id
+                this.idTypeMap.put( Long.valueOf( id ), remoteType);
+                if ( listenerAddress != null )
+                {
+                    this.idIPMap.put( Long.valueOf( id ), listenerAddress );
+                }
+            }
+            catch ( IOException ioe )
+            {
+                String message = "Problem setting listener id, listenerAddress [" + listenerAddress + "]";
+                log.error( message, ioe );
+
+                if ( cacheEventLogger != null )
+                {
+                    cacheEventLogger.logError( "RemoteCacheServer", "addCacheListener", message + " - "
+                        + ioe.getMessage() );
+                }
+            }
+
+            CacheEventQueueFactory<KK, VV> fact = new CacheEventQueueFactory<KK, VV>();
+            ICacheEventQueue<KK, VV> q = fact.createCacheEventQueue( listener, id, cacheName, remoteCacheServerAttributes
+                .getEventQueuePoolName(), remoteCacheServerAttributes.getEventQueueType() );
+
+            eventQMap.put(Long.valueOf(listener.getListenerId()), q);
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( cacheListeners );
+            }
+        }
+    }
+
+    /**
+     * Subscribes to all remote caches.
+     * <p>
+     * @param listener The feature to be added to the CacheListener attribute
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void addCacheListener( ICacheListener<KK, VV> listener )
+        throws IOException
+    {
+        for (String cacheName : cacheListenersMap.keySet())
+        {
+            addCacheListener( cacheName, listener );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Adding listener for cache [" + cacheName + "]" );
+            }
+        }
+    }
+
+    /**
+     * Unsubscribe this listener from this region. If the listener is registered, it will be removed
+     * from the event queue map list.
+     * <p>
+     * @param cacheName
+     * @param listener
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void removeCacheListener( String cacheName, ICacheListener<KK, VV> listener )
+        throws IOException
+    {
+        removeCacheListener( cacheName, listener.getListenerId() );
+    }
+
+    /**
+     * Unsubscribe this listener from this region. If the listener is registered, it will be removed
+     * from the event queue map list.
+     * <p>
+     * @param cacheName
+     * @param listenerId
+     */
+    public void removeCacheListener( String cacheName, long listenerId )
+    {
+        String message = "Removing listener for cache region = [" + cacheName + "] and listenerId [" + listenerId + "]";
+        logApplicationEvent( "RemoteCacheServer", "removeCacheListener", message );
+        if ( log.isInfoEnabled() )
+        {
+            log.info( message );
+        }
+
+        boolean isClusterListener = isRequestFromCluster( listenerId );
+
+        CacheListeners<K, V> cacheDesc = null;
+
+        if ( isClusterListener )
+        {
+            cacheDesc = getClusterListeners( cacheName );
+        }
+        else
+        {
+            cacheDesc = getCacheListeners( cacheName );
+        }
+        Map<Long, ICacheEventQueue<K, V>> eventQMap = cacheDesc.eventQMap;
+        cleanupEventQMap( eventQMap );
+        ICacheEventQueue<K, V> q = eventQMap.remove( Long.valueOf( listenerId ) );
+
+        if ( q != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Found queue for cache region = [" + cacheName + "] and listenerId  [" + listenerId + "]" );
+            }
+            q.destroy();
+            cleanupEventQMap( eventQMap );
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Did not find queue for cache region = [" + cacheName + "] and listenerId [" + listenerId
+                    + "]" );
+            }
+        }
+
+        // cleanup
+        idTypeMap.remove( Long.valueOf( listenerId ) );
+        idIPMap.remove( Long.valueOf( listenerId ) );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "After removing listener [" + listenerId + "] cache region " + cacheName + "'s listener size ["
+                + cacheDesc.eventQMap.size() + "]" );
+        }
+    }
+
+    /**
+     * Unsubscribes from all remote caches.
+     * <p>
+     * @param listener
+     * @throws IOException
+     */
+    @Override
+    public <KK, VV> void removeCacheListener( ICacheListener<KK, VV> listener )
+        throws IOException
+    {
+        for (String cacheName : cacheListenersMap.keySet())
+        {
+            removeCacheListener( cacheName, listener );
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Removing listener for cache [" + cacheName + "]" );
+            }
+        }
+        return;
+    }
+
+    /**
+     * Shuts down the remote server.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void shutdown()
+        throws IOException
+    {
+        RemoteCacheServerFactory.shutdownImpl( "", Registry.REGISTRY_PORT );
+    }
+
+    /**
+     * Shuts down a server at a particular host and port. Then it calls shutdown on the cache
+     * itself.
+     * <p>
+     * @param host
+     * @param port
+     * @throws IOException
+     */
+    @Override
+    public void shutdown( String host, int port )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Received shutdown request.  Shutting down server." );
+        }
+        RemoteCacheServerFactory.shutdownImpl( host, port );
+        this.cacheManager.shutDown();
+    }
+
+    /**
+     * Called by the RMI runtime sometime after the runtime determines that the reference list, the
+     * list of clients referencing the remote object, becomes empty.
+     */
+    // TODO: test out the DGC.
+    @Override
+    public void unreferenced()
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "*** Server now unreferenced and subject to GC. ***" );
+        }
+    }
+
+    /**
+     * Returns the next generated listener id [0,255].
+     * <p>
+     * @return the listener id of a client. This should be unique for this server.
+     */
+    private long nextListenerId()
+    {
+        long id = 0;
+        if ( listenerId[0] == Integer.MAX_VALUE )
+        {
+            synchronized ( listenerId )
+            {
+                id = listenerId[0];
+                listenerId[0] = 0;
+                // TODO: record & check if the generated id is currently being
+                // used by a valid listener. Currently if the id wraps after
+                // Long.MAX_VALUE,
+                // we just assume it won't collide with an existing listener who
+                // is live.
+            }
+        }
+        else
+        {
+            synchronized ( listenerId )
+            {
+                id = ++listenerId[0];
+            }
+        }
+        return id;
+    }
+
+    /**
+     * Gets the stats attribute of the RemoteCacheServer object.
+     * <p>
+     * @return The stats value
+     * @throws IOException
+     */
+    @Override
+    public String getStats()
+        throws IOException
+    {
+        return cacheManager.getStats();
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @param eventName
+     * @return ICacheEvent
+     */
+    private ICacheEvent<ICacheElement<K, V>> createICacheEvent( ICacheElement<K, V> item, long requesterId, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<ICacheElement<K, V>>();
+        }
+        String ipAddress = getExtraInfoForRequesterId( requesterId );
+        return cacheEventLogger
+            .createICacheEvent( "RemoteCacheServer", item.getCacheName(), eventName, ipAddress, item );
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @param eventName
+     * @return ICacheEvent
+     */
+    private <T> ICacheEvent<T> createICacheEvent( String cacheName, T key, long requesterId, String eventName )
+    {
+        if ( cacheEventLogger == null )
+        {
+            return new CacheEvent<T>();
+        }
+        String ipAddress = getExtraInfoForRequesterId( requesterId );
+        return cacheEventLogger.createICacheEvent( "RemoteCacheServer", cacheName, eventName, ipAddress, key );
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
+        }
+    }
+
+    /**
+     * Logs an event if an event logger is configured.
+     * <p>
+     * @param cacheEvent
+     */
+    protected <T> void logICacheEvent( ICacheEvent<T> cacheEvent )
+    {
+        if ( cacheEventLogger != null )
+        {
+            cacheEventLogger.logICacheEvent( cacheEvent );
+        }
+    }
+
+    /**
+     * Ip address for the client, if one is stored.
+     * <p>
+     * Protected for testing.
+     * <p>
+     * @param requesterId
+     * @return String
+     */
+    protected String getExtraInfoForRequesterId( long requesterId )
+    {
+        String ipAddress = idIPMap.get( Long.valueOf( requesterId ) );
+        return ipAddress;
+    }
+
+    /**
+     * Allows it to be injected.
+     * <p>
+     * @param cacheEventLogger
+     */
+    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+    {
+        this.cacheEventLogger = cacheEventLogger;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java
new file mode 100644
index 0000000..351f5ac
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java
@@ -0,0 +1,211 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.CommonRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.IRemoteCacheServerAttributes;
+
+/**
+ * These attributes are used to configure the remote cache server.
+ */
+public class RemoteCacheServerAttributes
+    extends CommonRemoteCacheAttributes
+    implements IRemoteCacheServerAttributes
+{
+    /** Don't change */
+    private static final long serialVersionUID = -2741662082869155365L;
+
+    /** port the server will listen to */
+    private int servicePort = 0;
+
+    /** Can a cluster remote get from other remotes */
+    private boolean allowClusterGet = true;
+
+    /** The config file, the initialization is multistage. Remote cache then composite cache. */
+    private String configFileName = "";
+
+    /** Should we start the registry */
+    private boolean DEFAULT_START_REGISTRY = true;
+
+    /** Should we start the registry */
+    private boolean startRegistry = DEFAULT_START_REGISTRY;
+
+    /** Should we try to keep the registry alive */
+    private boolean DEFAULT_USE_REGISTRY_KEEP_ALIVE = true;
+
+    /** Should we try to keep the registry alive */
+    private boolean useRegistryKeepAlive = DEFAULT_USE_REGISTRY_KEEP_ALIVE;
+
+    /** The delay between runs */
+    private long registryKeepAliveDelayMillis = 15 * 1000;
+
+    /** Default constructor for the RemoteCacheAttributes object */
+    public RemoteCacheServerAttributes()
+    {
+        super();
+    }
+
+    /**
+     * Gets the localPort attribute of the RemoteCacheAttributes object
+     * <p>
+     * @return The localPort value
+     */
+    @Override
+    public int getServicePort()
+    {
+        return this.servicePort;
+    }
+
+    /**
+     * Sets the localPort attribute of the RemoteCacheAttributes object
+     * <p>
+     * @param p The new localPort value
+     */
+    @Override
+    public void setServicePort( int p )
+    {
+        this.servicePort = p;
+    }
+
+    /**
+     * Should gets from non-cluster clients be allowed to get from other remote auxiliaries.
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    @Override
+    public boolean isAllowClusterGet()
+    {
+        return allowClusterGet;
+    }
+
+    /**
+     * Should we try to get from other cluster servers if we don't find the items locally.
+     * <p>
+     * @param r The new localClusterConsistency value
+     */
+    @Override
+    public void setAllowClusterGet( boolean r )
+    {
+        allowClusterGet = r;
+    }
+
+    /**
+     * Gets the ConfigFileName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @return The clusterServers value
+     */
+    @Override
+    public String getConfigFileName()
+    {
+        return configFileName;
+    }
+
+    /**
+     * Sets the ConfigFileName attribute of the IRemoteCacheAttributes object
+     * <p>
+     * @param s The new clusterServers value
+     */
+    @Override
+    public void setConfigFileName( String s )
+    {
+        configFileName = s;
+    }
+
+    /**
+     * Should we try to keep the registry alive
+     * <p>
+     * @param useRegistryKeepAlive the useRegistryKeepAlive to set
+     */
+    @Override
+    public void setUseRegistryKeepAlive( boolean useRegistryKeepAlive )
+    {
+        this.useRegistryKeepAlive = useRegistryKeepAlive;
+    }
+
+    /**
+     * Should we start the registry
+     * <p>
+     * @param startRegistry the startRegistry to set
+     */
+    @Override
+    public void setStartRegistry( boolean startRegistry )
+    {
+        this.startRegistry = startRegistry;
+    }
+
+    /**
+     * Should we start the registry
+     * <p>
+     * @return the startRegistry
+     */
+    @Override
+    public boolean isStartRegistry()
+    {
+        return startRegistry;
+    }
+
+    /**
+     * Should we try to keep the registry alive
+     * <p>
+     * @return the useRegistryKeepAlive
+     */
+    @Override
+    public boolean isUseRegistryKeepAlive()
+    {
+        return useRegistryKeepAlive;
+    }
+
+    /**
+     * @param registryKeepAliveDelayMillis the registryKeepAliveDelayMillis to set
+     */
+    @Override
+    public void setRegistryKeepAliveDelayMillis( long registryKeepAliveDelayMillis )
+    {
+        this.registryKeepAliveDelayMillis = registryKeepAliveDelayMillis;
+    }
+
+    /**
+     * @return the registryKeepAliveDelayMillis
+     */
+    @Override
+    public long getRegistryKeepAliveDelayMillis()
+    {
+        return registryKeepAliveDelayMillis;
+    }
+
+    /**
+     * @return String details
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder(super.toString());
+        buf.append( "\n servicePort = [" + this.getServicePort() + "]" );
+        buf.append( "\n allowClusterGet = [" + this.isAllowClusterGet() + "]" );
+        buf.append( "\n configFileName = [" + this.getConfigFileName() + "]" );
+        buf.append( "\n rmiSocketFactoryTimeoutMillis = [" + this.getRmiSocketFactoryTimeoutMillis() + "]" );
+        buf.append( "\n startRegistry = [" + this.isStartRegistry() + "]" );
+        buf.append( "\n useRegistryKeepAlive = [" + this.isUseRegistryKeepAlive() + "]" );
+        buf.append( "\n registryKeepAliveDelayMillis = [" + this.getRegistryKeepAliveDelayMillis() + "]" );
+        buf.append( "\n eventQueueType = [" + this.getEventQueueType() + "]" );
+        buf.append( "\n eventQueuePoolName = [" + this.getEventQueuePoolName() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java
new file mode 100644
index 0000000..7c1cbb3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java
@@ -0,0 +1,539 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
+import org.apache.commons.jcs.auxiliary.remote.RemoteUtils;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceAdmin;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.config.OptionConverter;
+import org.apache.commons.jcs.utils.config.PropertySetter;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.rmi.Naming;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.Registry;
+import java.rmi.server.RMISocketFactory;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Properties;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides remote cache services. This creates remote cache servers and can proxy command line
+ * requests to a running server.
+ */
+public class RemoteCacheServerFactory
+    implements IRemoteCacheConstants
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheServerFactory.class );
+
+    /** The single instance of the RemoteCacheServer object. */
+    private static RemoteCacheServer<? extends Serializable, ? extends Serializable> remoteCacheServer;
+
+    /** The name of the service. */
+    private static String serviceName = IRemoteCacheConstants.REMOTE_CACHE_SERVICE_VAL;
+
+    /** Executes the registry keep alive. */
+    private static ScheduledExecutorService keepAliveDaemon;
+
+    /** A reference to the registry. */
+    private static Registry registry = null;
+
+    /** Constructor for the RemoteCacheServerFactory object. */
+    private RemoteCacheServerFactory()
+    {
+        super();
+    }
+
+    /**
+     * This will allow you to get stats from the server, etc. Perhaps we should provide methods on
+     * the factory to do this instead.
+     * <p>
+     * A remote cache is either a local cache or a cluster cache.
+     * </p>
+     * @return Returns the remoteCacheServer.
+     */
+    @SuppressWarnings("unchecked") // Need cast to specific RemoteCacheServer
+    public static <K, V> RemoteCacheServer<K, V> getRemoteCacheServer()
+    {
+        return (RemoteCacheServer<K, V>)remoteCacheServer;
+    }
+
+    // ///////////////////// Startup/shutdown methods. //////////////////
+    /**
+     * Starts up the remote cache server on this JVM, and binds it to the registry on the given host
+     * and port.
+     * <p>
+     * A remote cache is either a local cache or a cluster cache.
+     * <p>
+     * @param host
+     * @param port
+     * @param propFile
+     * @throws IOException
+     */
+    public static void startup( String host, int port, String propFile )
+        throws IOException
+    {
+        if ( remoteCacheServer != null )
+        {
+            throw new IllegalArgumentException( "Server already started." );
+        }
+
+        synchronized ( RemoteCacheServer.class )
+        {
+            if ( remoteCacheServer != null )
+            {
+                return;
+            }
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "ConfigFileName = [" + propFile + "]" );
+            }
+            Properties props = RemoteUtils.loadProps( propFile );
+            startup(host, port, props);
+        }
+    }
+
+    /**
+     * Starts up the remote cache server on this JVM, and binds it to the registry on the given host
+     * and port.
+     * <p>
+     * A remote cache is either a local cache or a cluster cache.
+     * <p>
+     * @param host
+     * @param port
+     * @param props
+     * @throws IOException
+     */
+    public static void startup( String host, int port, Properties props )
+        throws IOException
+    {
+        if ( remoteCacheServer != null )
+        {
+            throw new IllegalArgumentException( "Server already started." );
+        }
+
+        synchronized ( RemoteCacheServer.class )
+        {
+            if ( remoteCacheServer != null )
+            {
+                return;
+            }
+            if ( host == null )
+            {
+                host = "";
+            }
+
+            RemoteCacheServerAttributes rcsa = configureRemoteCacheServerAttributes(props);
+            //rcsa.setConfigFileName( propFile );
+
+            // These should come from the file!
+            rcsa.setRemotePort( port );
+            rcsa.setRemoteHost( host );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Creating server with these attributes: " + rcsa );
+            }
+
+            setServiceName( rcsa.getRemoteServiceName() );
+
+            RMISocketFactory customRMISocketFactory = configureObjectSpecificCustomFactory( props );
+
+            RemoteUtils.configureGlobalCustomSocketFactory( rcsa.getRmiSocketFactoryTimeoutMillis() );
+
+            // CONFIGURE THE EVENT LOGGER
+            ICacheEventLogger cacheEventLogger = configureCacheEventLogger( props );
+
+            // CREATE SERVER
+            if ( customRMISocketFactory != null )
+            {
+                remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa, customRMISocketFactory );
+            }
+            else
+            {
+                remoteCacheServer = new RemoteCacheServer<Serializable, Serializable>( rcsa );
+            }
+
+            remoteCacheServer.setCacheEventLogger( cacheEventLogger );
+
+            // START THE REGISTRY
+            if (rcsa.isStartRegistry())
+            {
+            	registry = RemoteUtils.createRegistry(port);
+            }
+
+            // REGISTER THE SERVER
+            registerServer( RemoteUtils.getNamingURL(host, port, serviceName), remoteCacheServer );
+
+            // KEEP THE REGISTRY ALIVE
+            if ( rcsa.isUseRegistryKeepAlive() )
+            {
+                if ( keepAliveDaemon == null )
+                {
+                    keepAliveDaemon = Executors.newScheduledThreadPool(1,
+                            new DaemonThreadFactory("JCS-RemoteCacheServerFactory-"));
+                }
+                RegistryKeepAliveRunner runner = new RegistryKeepAliveRunner( host, port, serviceName );
+                runner.setCacheEventLogger( cacheEventLogger );
+                keepAliveDaemon.scheduleAtFixedRate(runner, 0, rcsa.getRegistryKeepAliveDelayMillis(), TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    /**
+     * Tries to get the event logger by new and old config styles.
+     * <p>
+     * @param props
+     * @return ICacheEventLogger
+     */
+    protected static ICacheEventLogger configureCacheEventLogger( Properties props )
+    {
+        ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator
+            .parseCacheEventLogger( props, IRemoteCacheConstants.CACHE_SERVER_PREFIX );
+
+        // try the old way
+        if ( cacheEventLogger == null )
+        {
+            cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props,
+                                                                                 IRemoteCacheConstants.PROPERTY_PREFIX );
+        }
+        return cacheEventLogger;
+    }
+
+    /**
+     * This configures an object specific custom factory. This will be configured for just this
+     * object in the registry. This can be null.
+     * <p>
+     * @param props
+     * @return RMISocketFactory
+     */
+    protected static RMISocketFactory configureObjectSpecificCustomFactory( Properties props )
+    {
+        RMISocketFactory customRMISocketFactory =
+            OptionConverter.instantiateByKey( props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, null );
+
+        if ( customRMISocketFactory != null )
+        {
+            PropertySetter.setProperties( customRMISocketFactory, props, CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX
+                + "." );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Will use server specific custom socket factory. " + customRMISocketFactory );
+            }
+        }
+        else
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "No server specific custom socket factory defined." );
+            }
+        }
+        return customRMISocketFactory;
+    }
+
+    /**
+     * Registers the server with the registry. I broke this off because we might want to have code
+     * that will restart a dead registry. It will need to rebind the server.
+     * <p>
+     * @param namingURL
+     * @param server
+     * @throws RemoteException
+     */
+    protected static void registerServer( String namingURL, Remote server )
+        throws RemoteException
+    {
+        if ( server == null )
+        {
+            throw new RemoteException( "Cannot register the server until it is created." );
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Binding server to " + namingURL );
+        }
+
+        try
+        {
+            Naming.rebind( namingURL, server );
+        }
+        catch ( MalformedURLException ex )
+        {
+            // impossible case.
+            throw new IllegalArgumentException( ex.getMessage() + "; url=" + namingURL );
+        }
+    }
+
+    /**
+     * Configure.
+     * <p>
+     * jcs.remotecache.serverattributes.ATTRIBUTENAME=ATTRIBUTEVALUE
+     * <p>
+     * @param prop
+     * @return RemoteCacheServerAttributesconfigureRemoteCacheServerAttributes
+     */
+    protected static RemoteCacheServerAttributes configureRemoteCacheServerAttributes( Properties prop )
+    {
+        RemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+
+        // configure automatically
+        PropertySetter.setProperties( rcsa, prop, CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + "." );
+
+        configureManuallyIfValuesArePresent( prop, rcsa );
+
+        return rcsa;
+    }
+
+    /**
+     * This looks for the old config values.
+     * <p>
+     * @param prop
+     * @param rcsa
+     */
+    private static void configureManuallyIfValuesArePresent( Properties prop, RemoteCacheServerAttributes rcsa )
+    {
+        // DEPRECATED CONFIG
+        String servicePortStr = prop.getProperty( REMOTE_CACHE_SERVICE_PORT );
+        if ( servicePortStr != null )
+        {
+            try
+            {
+                int servicePort = Integer.parseInt( servicePortStr );
+                rcsa.setServicePort( servicePort );
+                log.debug( "Remote cache service uses port number " + servicePort + "." );
+            }
+            catch ( NumberFormatException ignore )
+            {
+                log.debug( "Remote cache service port property " + REMOTE_CACHE_SERVICE_PORT
+                    + " not specified.  An anonymous port will be used." );
+            }
+        }
+
+        String socketTimeoutMillisStr = prop.getProperty( SOCKET_TIMEOUT_MILLIS );
+        if ( socketTimeoutMillisStr != null )
+        {
+            try
+            {
+                int rmiSocketFactoryTimeoutMillis = Integer.parseInt( socketTimeoutMillisStr );
+                rcsa.setRmiSocketFactoryTimeoutMillis( rmiSocketFactoryTimeoutMillis );
+                log.debug( "Remote cache socket timeout " + rmiSocketFactoryTimeoutMillis + "ms." );
+            }
+            catch ( NumberFormatException ignore )
+            {
+                log.debug( "Remote cache socket timeout property " + SOCKET_TIMEOUT_MILLIS
+                    + " not specified.  The default will be used." );
+            }
+        }
+
+        String lccStr = prop.getProperty( REMOTE_LOCAL_CLUSTER_CONSISTENCY );
+        if ( lccStr != null )
+        {
+            boolean lcc = Boolean.valueOf( lccStr ).booleanValue();
+            rcsa.setLocalClusterConsistency( lcc );
+        }
+
+        String acgStr = prop.getProperty( REMOTE_ALLOW_CLUSTER_GET );
+        if ( acgStr != null )
+        {
+            boolean acg = Boolean.valueOf( acgStr ).booleanValue();
+            rcsa.setAllowClusterGet( acg );
+        }
+
+        // Register the RemoteCacheServer remote object in the registry.
+        rcsa.setRemoteServiceName( prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim() );
+    }
+
+    /**
+     * Unbinds the remote server.
+     * <p>
+     * @param host
+     * @param port
+     * @throws IOException
+     */
+    static void shutdownImpl( String host, int port )
+        throws IOException
+    {
+        synchronized ( RemoteCacheServer.class )
+        {
+            if ( remoteCacheServer == null )
+            {
+                return;
+            }
+            log.info( "Unbinding host=" + host + ", port=" + port + ", serviceName=" + getServiceName() );
+            try
+            {
+                Naming.unbind( RemoteUtils.getNamingURL(host, port, getServiceName()) );
+            }
+            catch ( MalformedURLException ex )
+            {
+                // impossible case.
+                throw new IllegalArgumentException( ex.getMessage() + "; host=" + host + ", port=" + port
+                    + ", serviceName=" + getServiceName() );
+            }
+            catch ( NotBoundException ex )
+            {
+                // ignore.
+            }
+            remoteCacheServer.release();
+            remoteCacheServer = null;
+
+            // Shut down keepalive scheduler
+            if ( keepAliveDaemon != null )
+            {
+                keepAliveDaemon.shutdownNow();
+                keepAliveDaemon = null;
+            }
+
+            // Try to release registry
+            if (registry != null)
+            {
+            	UnicastRemoteObject.unexportObject(registry, true);
+            	registry = null;
+            }
+        }
+    }
+
+    /**
+     * Creates an local RMI registry on the default port, starts up the remote cache server, and
+     * binds it to the registry.
+     * <p>
+     * A remote cache is either a local cache or a cluster cache.
+     * <p>
+     * @param args The command line arguments
+     * @throws Exception
+     */
+    public static void main( String[] args )
+        throws Exception
+    {
+        Properties prop = args.length > 0 ? RemoteUtils.loadProps( args[args.length - 1] ) : new Properties();
+
+        int port;
+        try
+        {
+            port = Integer.parseInt( prop.getProperty( "registry.port" ) );
+        }
+        catch ( NumberFormatException ex )
+        {
+            port = Registry.REGISTRY_PORT;
+        }
+
+        // shutdown
+        if ( args.length > 0 && args[0].toLowerCase().indexOf( "-shutdown" ) != -1 )
+        {
+            String remoteServiceName = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
+            String registry = RemoteUtils.getNamingURL("", port, remoteServiceName);
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "looking up server " + registry );
+            }
+            Object obj = Naming.lookup( registry );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "server found" );
+            }
+            ICacheServiceAdmin admin = (ICacheServiceAdmin) obj;
+            try
+            {
+                admin.shutdown();
+            }
+            catch ( Exception ex )
+            {
+                log.error( "Problem calling shutdown.", ex );
+            }
+            log.debug( "done." );
+            System.exit( 0 );
+        }
+
+        // STATS
+        if ( args.length > 0 && args[0].toLowerCase().indexOf( "-stats" ) != -1 )
+        {
+
+            log.debug( "getting cache stats" );
+
+            try
+            {
+                String sz = prop.getProperty( REMOTE_CACHE_SERVICE_NAME, REMOTE_CACHE_SERVICE_VAL ).trim();
+                String registry = RemoteUtils.getNamingURL("", port, sz);
+                log.debug( "looking up server " + registry );
+                Object obj = Naming.lookup( registry );
+                log.debug( "server found" );
+
+                log.debug( "obj = " + obj );
+                ICacheServiceAdmin admin = (ICacheServiceAdmin) obj;
+
+                try
+                {
+//                    System.out.println( admin.getStats().toString() );
+                    log.debug( admin.getStats() );
+                }
+                catch ( Exception es )
+                {
+                    log.error( es );
+                }
+
+            }
+            catch ( Exception ex )
+            {
+                log.error( "Problem getting stats.", ex );
+            }
+            log.debug( "done." );
+            System.exit( 0 );
+        }
+
+        // startup.
+        String host = prop.getProperty( "registry.host" );
+
+        if ( host == null || host.trim().equals( "" ) || host.trim().equals( "localhost" ) )
+        {
+            log.debug( "main> creating registry on the localhost" );
+            RemoteUtils.createRegistry( port );
+        }
+        log.debug( "main> starting up RemoteCacheServer" );
+        RemoteCacheServerFactory.startup( host, port, args.length > 0 ? args[0] : null );
+        log.debug( "main> done" );
+    }
+
+    /**
+     * @param serviceName the serviceName to set
+     */
+    protected static void setServiceName( String serviceName )
+    {
+        RemoteCacheServerFactory.serviceName = serviceName;
+    }
+
+    /**
+     * @return the serviceName
+     */
+    protected static String getServiceName()
+    {
+        return serviceName;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheStartupServlet.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheStartupServlet.java
new file mode 100644
index 0000000..e20b1d1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheStartupServlet.java
@@ -0,0 +1,216 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.utils.net.HostNameUtil;
+import org.apache.commons.jcs.utils.props.PropertyLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.UnknownHostException;
+import java.util.Properties;
+
+/**
+ * This servlet can be used to startup the JCS remote cache. It is easy to deploy the remote server
+ * in a tomcat base. This give you an easy way to monitor its activity.
+ * <p>
+ * <code>
+ *  <servlet>
+        <servlet-name>JCSRemoteCacheStartupServlet</servlet-name>
+        <servlet-class>
+             org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheStartupServlet
+        </servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+
+    <servlet-mapping>
+        <servlet-name>JCSRemoteCacheStartupServlet</servlet-name>
+        <url-pattern>/jcs</url-pattern>
+    </servlet-mapping>
+ * </code>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheStartupServlet
+    extends HttpServlet
+{
+    /** Don't change */
+    private static final long serialVersionUID = 1L;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheStartupServlet.class );
+
+    /** The default port to start the registry on.  */
+    private static final int DEFAULT_REGISTRY_PORT = 1101;
+
+    /** properties file name */
+    private static final String DEFAULT_PROPS_FILE_NAME = "cache";
+
+    /** properties file Suffix */
+    private static final String DEFAULT_PROPS_FILE_SUFFIX = "ccf";
+
+    /** properties file name, must set prior to calling get instance */
+    private final String propsFileName = DEFAULT_PROPS_FILE_NAME;
+
+    /** properties file name, must set prior to calling get instance */
+    private final String fullPropsFileName = DEFAULT_PROPS_FILE_NAME + "." + DEFAULT_PROPS_FILE_SUFFIX;
+
+    /**
+     * Starts the registry and then tries to bind to it.
+     * <p>
+     * Gets the port from a props file. Uses the local host name for the registry host. Tries to
+     * start the registry, ignoring failure. Starts the server.
+     * <p>
+     * @throws ServletException
+     */
+    @Override
+    public void init()
+        throws ServletException
+    {
+        super.init();
+        // TODO load from props file or get as init param or get from jndi, or
+        // all three
+        int registryPort = DEFAULT_REGISTRY_PORT;
+
+        Properties props = PropertyLoader.loadProperties( propsFileName );
+        if ( props != null )
+        {
+            String portS = props.getProperty( "registry.port", String.valueOf( DEFAULT_REGISTRY_PORT ) );
+
+            try
+            {
+                registryPort = Integer.parseInt( portS );
+            }
+            catch ( NumberFormatException e )
+            {
+                log.error( "Problem converting port to an int.", e );
+            }
+        }
+
+        // we will always use the local machine for the registry
+        String registryHost;
+        try
+        {
+            registryHost = HostNameUtil.getLocalHostAddress();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "registryHost = [" + registryHost + "]" );
+            }
+
+            if ( "localhost".equals( registryHost ) || "127.0.0.1".equals( registryHost ) )
+            {
+                log.warn( "The local address [" + registryHost
+                    + "] is INVALID.  Other machines must be able to use the address to reach this server." );
+            }
+
+            try
+            {
+                RemoteCacheServerFactory.startup( registryHost, registryPort, "/" + fullPropsFileName );
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Remote JCS Server started with properties from " + fullPropsFileName );
+                }
+            }
+            catch ( IOException e )
+            {
+                log.error( "Problem starting remote cache server.", e );
+            }
+        }
+        catch ( UnknownHostException e )
+        {
+            log.error( "Could not get local address to use for the registry!", e );
+        }
+    }
+
+    /**
+     * It just dumps the stats.
+     * <p>
+     * @param request
+     * @param response
+     * @throws ServletException
+     * @throws IOException
+     */
+    @Override
+    protected void service( HttpServletRequest request, HttpServletResponse response )
+        throws ServletException, IOException
+    {
+        String stats = "";
+
+        try
+        {
+            stats = CompositeCacheManager.getInstance().getStats();
+        }
+        catch (CacheException e)
+        {
+            throw new ServletException(e);
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( stats );
+        }
+
+        try
+        {
+            String characterEncoding = response.getCharacterEncoding();
+            if (characterEncoding == null)
+            {
+                characterEncoding = "UTF-8";
+                response.setCharacterEncoding(characterEncoding);
+            }
+            OutputStream os = response.getOutputStream();
+            os.write( stats.getBytes(characterEncoding) );
+            os.close();
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem writing response.", e );
+        }
+    }
+
+    /**
+     * shuts the cache down.
+     */
+    @Override
+    public void destroy()
+    {
+        super.destroy();
+
+        log.info( "Shutting down remote cache " );
+
+        try
+        {
+            CompositeCacheManager.getInstance().shutDown();
+        }
+        catch (CacheException e)
+        {
+            log.error("Could not retrieve cache manager instance", e);
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactory.java
new file mode 100644
index 0000000..7d1f31a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactory.java
@@ -0,0 +1,111 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.server.RMISocketFactory;
+
+/**
+ * This can be injected into the the remote cache server as follows:
+ *
+ * <pre>
+ * jcs.remotecache.customrmisocketfactory=org.apache.commons.jcs.auxiliary.remote.server.TimeoutConfigurableRMISocketFactory
+ * jcs.remotecache.customrmisocketfactory.readTimeout=5000
+ * jcs.remotecache.customrmisocketfactory.openTimeout=5000
+ * </pre>
+ */
+public class TimeoutConfigurableRMISocketFactory
+    extends RMISocketFactory
+    implements Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 1489909775271203334L;
+
+    /** The socket read timeout */
+    private int readTimeout = 5000;
+
+    /** The socket open timeout */
+    private int openTimeout = 5000;
+
+    /**
+     * @param port
+     * @return ServerSocket
+     * @throws IOException
+     */
+    @Override
+    public ServerSocket createServerSocket( int port )
+        throws IOException
+    {
+        return new ServerSocket( port );
+    }
+
+    /**
+     * @param host
+     * @param port
+     * @return Socket
+     * @throws IOException
+     */
+    @Override
+    public Socket createSocket( String host, int port )
+        throws IOException
+    {
+        Socket socket = new Socket();
+        socket.setSoTimeout( readTimeout );
+        socket.setSoLinger( false, 0 );
+        socket.connect( new InetSocketAddress( host, port ), openTimeout );
+        return socket;
+    }
+
+    /**
+     * @param readTimeout the readTimeout to set
+     */
+    public void setReadTimeout( int readTimeout )
+    {
+        this.readTimeout = readTimeout;
+    }
+
+    /**
+     * @return the readTimeout
+     */
+    public int getReadTimeout()
+    {
+        return readTimeout;
+    }
+
+    /**
+     * @param openTimeout the openTimeout to set
+     */
+    public void setOpenTimeout( int openTimeout )
+    {
+        this.openTimeout = openTimeout;
+    }
+
+    /**
+     * @return the openTimeout
+     */
+    public int getOpenTimeout()
+    {
+        return openTimeout;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServer.java
new file mode 100644
index 0000000..f0aa692
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServer.java
@@ -0,0 +1,38 @@
+package org.apache.commons.jcs.auxiliary.remote.server.behavior;
+
+import java.rmi.Remote;
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheObserver;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceAdmin;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+/*
+ * 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.
+ */
+
+/**
+ * Interface for managing Remote objects
+ *
+ * @author Thomas Vandahl
+ *
+ */
+public interface IRemoteCacheServer<K, V>
+    extends ICacheServiceNonLocal<K, V>, IRemoteCacheObserver, ICacheServiceAdmin, Remote
+{
+    // empty
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java
new file mode 100644
index 0000000..2f968dc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java
@@ -0,0 +1,118 @@
+package org.apache.commons.jcs.auxiliary.remote.server.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.ICommonRemoteCacheAttributes;
+
+/**
+ * This defines the minimal behavior for the objects that are used to configure
+ * the remote cache server.
+ */
+public interface IRemoteCacheServerAttributes
+    extends ICommonRemoteCacheAttributes
+{
+    /**
+     * Gets the localPort attribute of the IRemoteCacheAttributes object.
+     * <p>
+     * @return The localPort value
+     */
+    int getServicePort();
+
+    /**
+     * Sets the localPort attribute of the IRemoteCacheAttributes object.
+     * <p>
+     * @param p
+     *            The new localPort value
+     */
+    void setServicePort( int p );
+
+    /**
+     * Should we try to get remotely when the request does not come in from a
+     * cluster. If local L1 asks remote server R1 for element A and R1 doesn't
+     * have it, should R1 look remotely? The difference is between a local and a
+     * remote update. The local update stays local. Normal updates, removes,
+     * etc, stay local when they come from a client. If this is set to true,
+     * then they can go remote.
+     * <p>
+     * @return The localClusterConsistency value
+     */
+    boolean isAllowClusterGet();
+
+    /**
+     * Should cluster updates be propagated to the locals.
+     * <p>
+     * @param r
+     *            The new localClusterConsistency value
+     */
+    void setAllowClusterGet( boolean r );
+
+    /**
+     * Gets the ConfigFileName attribute of the IRemoteCacheAttributes object.
+     * <p>
+     * @return The clusterServers value
+     */
+    String getConfigFileName();
+
+    /**
+     * Sets the ConfigFileName attribute of the IRemoteCacheAttributes object.
+     * <p>
+     * @param s
+     *            The new clusterServers value
+     */
+    void setConfigFileName( String s );
+
+    /**
+     * Should we try to keep the registry alive
+     * <p>
+     * @param useRegistryKeepAlive the useRegistryKeepAlive to set
+     */
+    void setUseRegistryKeepAlive( boolean useRegistryKeepAlive );
+
+    /**
+     * Should we start the registry
+     * <p>
+     * @param startRegistry the startRegistry to set
+     */
+    void setStartRegistry( boolean startRegistry );
+
+    /**
+     * Should we start the registry
+     * <p>
+     * @return the startRegistry
+     */
+    boolean isStartRegistry();
+
+    /**
+     * Should we try to keep the registry alive
+     * <p>
+     * @return the useRegistryKeepAlive
+     */
+    boolean isUseRegistryKeepAlive();
+
+    /**
+     * @param registryKeepAliveDelayMillis the registryKeepAliveDelayMillis to set
+     */
+    void setRegistryKeepAliveDelayMillis( long registryKeepAliveDelayMillis );
+
+    /**
+     * @return the registryKeepAliveDelayMillis
+     */
+    long getRegistryKeepAliveDelayMillis();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/RemoteType.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/RemoteType.java
new file mode 100644
index 0000000..31c7cbf
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/RemoteType.java
@@ -0,0 +1,32 @@
+package org.apache.commons.jcs.auxiliary.remote.server.behavior;
+
+/*
+ * 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.
+ */
+
+/**
+ * Enum to describe the mode of the remote cache
+ */
+public enum RemoteType
+{
+    /** A remote cache is either a local cache or a cluster cache */
+    LOCAL,
+
+    /** A remote cache is either a local cache or a cluster cache */
+    CLUSTER
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactory.java
new file mode 100644
index 0000000..78c3941
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactory.java
@@ -0,0 +1,254 @@
+package org.apache.commons.jcs.auxiliary.remote.util;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteRequestType;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Set;
+
+/**
+ * This creates request objects. You could write your own client and use the objects from this
+ * factory.
+ */
+public class RemoteCacheRequestFactory
+{
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( RemoteCacheRequestFactory.class );
+
+    /**
+     * Creates a get Request.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createGetRequest( String cacheName, K key, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setKey( key );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.GET );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a getMatching Request.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createGetMatchingRequest( String cacheName, String pattern, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setPattern( pattern );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.GET_MATCHING );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a getMultiple Request.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createGetMultipleRequest( String cacheName, Set<K> keys, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setKeySet( keys );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.GET_MULTIPLE );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a remove Request.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createRemoveRequest( String cacheName, K key, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setKey( key );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.REMOVE );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a GetKeySet Request.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static RemoteCacheRequest<String, String> createGetKeySetRequest( String cacheName, long requesterId )
+    {
+        RemoteCacheRequest<String, String> request = new RemoteCacheRequest<String, String>();
+        request.setCacheName( cacheName );
+        request.setKey( cacheName );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.GET_KEYSET );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a removeAll Request.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createRemoveAllRequest( String cacheName, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.REMOVE_ALL );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates a dispose Request.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createDisposeRequest( String cacheName, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setCacheName( cacheName );
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.DISPOSE );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates an Update Request.
+     * <p>
+     * @param cacheElement
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createUpdateRequest( ICacheElement<K, V> cacheElement, long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        if ( cacheElement != null )
+        {
+            request.setCacheName( cacheElement.getCacheName() );
+            request.setCacheElement( cacheElement );
+            request.setKey( cacheElement.getKey() );
+        }
+        else
+        {
+            log.error( "Can't create a proper update request for a null cache element." );
+        }
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.UPDATE );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+    /**
+     * Creates an alive check Request.
+     * <p>
+     * @param requesterId
+     * @return RemoteHttpCacheRequest
+     */
+    public static <K, V> RemoteCacheRequest<K, V> createAliveCheckRequest( long requesterId )
+    {
+        RemoteCacheRequest<K, V> request = new RemoteCacheRequest<K, V>();
+        request.setRequesterId( requesterId );
+        request.setRequestType( RemoteRequestType.ALIVE_CHECK );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Created: " + request );
+        }
+
+        return request;
+    }
+
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheRequest.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheRequest.java
new file mode 100644
index 0000000..b99ef2d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheRequest.java
@@ -0,0 +1,187 @@
+package org.apache.commons.jcs.auxiliary.remote.value;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * The basic request wrapper. The different types of requests are differentiated by their types.
+ * <p>
+ * Rather than creating sub object types, I created on object that has values for all types of
+ * requests.
+ */
+public class RemoteCacheRequest<K, V>
+    implements Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -8858447417390442569L;
+
+    /** The request type specifies the type of request: get, put, remove, . . */
+    private RemoteRequestType requestType = null;
+
+    /** Used to identify the source. Same as listener id on the client side. */
+    private long requesterId = 0;
+
+    /** The name of the region */
+    private String cacheName;
+
+    /** The key, if this request has a key. */
+    private K key;
+
+    /** The keySet, if this request has a keySet. Only getMultiple requests. */
+    private Set<K> keySet;
+
+    /** The pattern, if this request uses a pattern. Only getMatching requests. */
+    private String pattern;
+
+    /** The ICacheEleemnt, if this request contains a value. Only update requests will have this. */
+    private ICacheElement<K, V> cacheElement;
+
+    /**
+     * @param requestType the requestType to set
+     */
+    public void setRequestType( RemoteRequestType requestType )
+    {
+        this.requestType = requestType;
+    }
+
+    /**
+     * @return the requestType
+     */
+    public RemoteRequestType getRequestType()
+    {
+        return requestType;
+    }
+
+    /**
+     * @param cacheName the cacheName to set
+     */
+    public void setCacheName( String cacheName )
+    {
+        this.cacheName = cacheName;
+    }
+
+    /**
+     * @return the cacheName
+     */
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+
+    /**
+     * @param key the key to set
+     */
+    public void setKey( K key )
+    {
+        this.key = key;
+    }
+
+    /**
+     * @return the key
+     */
+    public K getKey()
+    {
+        return key;
+    }
+
+    /**
+     * @param pattern the pattern to set
+     */
+    public void setPattern( String pattern )
+    {
+        this.pattern = pattern;
+    }
+
+    /**
+     * @return the pattern
+     */
+    public String getPattern()
+    {
+        return pattern;
+    }
+
+    /**
+     * @param cacheElement the cacheElement to set
+     */
+    public void setCacheElement( ICacheElement<K, V> cacheElement )
+    {
+        this.cacheElement = cacheElement;
+    }
+
+    /**
+     * @return the cacheElement
+     */
+    public ICacheElement<K, V> getCacheElement()
+    {
+        return cacheElement;
+    }
+
+    /**
+     * @param requesterId the requesterId to set
+     */
+    public void setRequesterId( long requesterId )
+    {
+        this.requesterId = requesterId;
+    }
+
+    /**
+     * @return the requesterId
+     */
+    public long getRequesterId()
+    {
+        return requesterId;
+    }
+
+    /**
+     * @param keySet the keySet to set
+     */
+    public void setKeySet( Set<K> keySet )
+    {
+        this.keySet = keySet;
+    }
+
+    /**
+     * @return the keySet
+     */
+    public Set<K> getKeySet()
+    {
+        return keySet;
+    }
+
+    /** @return string */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nRemoteHttpCacheRequest" );
+        buf.append( "\n requesterId [" + getRequesterId() + "]" );
+        buf.append( "\n requestType [" + getRequestType() + "]" );
+        buf.append( "\n cacheName [" + getCacheName() + "]" );
+        buf.append( "\n key [" + getKey() + "]" );
+        buf.append( "\n keySet [" + getKeySet() + "]" );
+        buf.append( "\n pattern [" + getPattern() + "]" );
+        buf.append( "\n cacheElement [" + getCacheElement() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheResponse.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheResponse.java
new file mode 100644
index 0000000..aaa94f3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteCacheResponse.java
@@ -0,0 +1,105 @@
+package org.apache.commons.jcs.auxiliary.remote.value;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * This is the response wrapper. The servlet wraps all different type of responses in one of these
+ * objects.
+ */
+public class RemoteCacheResponse<T>
+    implements Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -8858447417390442568L;
+
+    /** Was the event processed without error */
+    private boolean success = true;
+
+    /** Simple error messaging */
+    private String errorMessage;
+
+    /**
+     * The payload. Typically a key / ICacheElement<K, V> map. A normal get will return a map with one
+     * record.
+     */
+    private T payload;
+
+    /**
+     * @param success the success to set
+     */
+    public void setSuccess( boolean success )
+    {
+        this.success = success;
+    }
+
+    /**
+     * @return the success
+     */
+    public boolean isSuccess()
+    {
+        return success;
+    }
+
+    /**
+     * @param errorMessage the errorMessage to set
+     */
+    public void setErrorMessage( String errorMessage )
+    {
+        this.errorMessage = errorMessage;
+    }
+
+    /**
+     * @return the errorMessage
+     */
+    public String getErrorMessage()
+    {
+        return errorMessage;
+    }
+
+    /**
+     * @param payload the payload to set
+     */
+    public void setPayload( T payload )
+    {
+        this.payload = payload;
+    }
+
+    /**
+     * @return the payload
+     */
+    public T getPayload()
+    {
+        return payload;
+    }
+
+    /** @return string */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\nRemoteHttpCacheResponse" );
+        buf.append( "\n success [" + isSuccess() + "]" );
+        buf.append( "\n payload [" + getPayload() + "]" );
+        buf.append( "\n errorMessage [" + getErrorMessage() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteRequestType.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteRequestType.java
new file mode 100644
index 0000000..ea6d4e1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/value/RemoteRequestType.java
@@ -0,0 +1,53 @@
+package org.apache.commons.jcs.auxiliary.remote.value;
+
+/*
+ * 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.
+ */
+
+/**
+ * The different types of requests
+ */
+public enum RemoteRequestType
+{
+    /** Alive check request type. */
+    ALIVE_CHECK,
+
+    /** Get request type. */
+    GET,
+
+    /** Get Multiple request type. */
+    GET_MULTIPLE,
+
+    /** Get Matching request type. */
+    GET_MATCHING,
+
+    /** Update request type. */
+    UPDATE,
+
+    /** Remove request type. */
+    REMOVE,
+
+    /** Remove All request type. */
+    REMOVE_ALL,
+
+    /** Get keys request type. */
+    GET_KEYSET,
+
+    /** Dispose request type. */
+    DISPOSE,
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/AbstractCacheEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/AbstractCacheEventQueue.java
new file mode 100644
index 0000000..e421843
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/AbstractCacheEventQueue.java
@@ -0,0 +1,499 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * An abstract base class to the different implementations
+ */
+public abstract class AbstractCacheEventQueue<K, V>
+    implements ICacheEventQueue<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( AbstractCacheEventQueue.class );
+
+    /** default */
+    protected static final int DEFAULT_WAIT_TO_DIE_MILLIS = 10000;
+
+    /**
+     * time to wait for an event before snuffing the background thread if the queue is empty. make
+     * configurable later
+     */
+    private int waitToDieMillis = DEFAULT_WAIT_TO_DIE_MILLIS;
+
+    // TODO privatise the fields
+
+    /**
+     * When the events are pulled off the queue, the tell the listener to handle the specific event
+     * type. The work is done by the listener.
+     */
+    protected ICacheListener<K, V> listener;
+
+    /** Id of the listener registered with this queue */
+    protected long listenerId;
+
+    /** The cache region name, if applicable. */
+    protected String cacheName;
+
+    /** Maximum number of failures before we buy the farm. */
+    protected int maxFailure;
+
+    /** in milliseconds */
+    protected int waitBeforeRetry;
+
+    /** this is true if there is no worker thread. */
+    protected boolean destroyed = true;
+
+    /**
+     * This means that the queue is functional. If we reached the max number of failures, the queue
+     * is marked as non functional and will never work again.
+     */
+    private boolean working = true;
+
+    /**
+     * Returns the time to wait for events before killing the background thread.
+     * <p>
+     * @return int
+     */
+    public int getWaitToDieMillis()
+    {
+        return waitToDieMillis;
+    }
+
+    /**
+     * Sets the time to wait for events before killing the background thread.
+     * <p>
+     * @param wtdm the ms for the q to sit idle.
+     */
+    public void setWaitToDieMillis( int wtdm )
+    {
+        waitToDieMillis = wtdm;
+    }
+
+    /**
+     * Creates a brief string identifying the listener and the region.
+     * <p>
+     * @return String debugging info.
+     */
+    @Override
+    public String toString()
+    {
+        return "CacheEventQueue [listenerId=" + listenerId + ", cacheName=" + cacheName + "]";
+    }
+
+    /**
+     * If they queue has an active thread it is considered alive.
+     * <p>
+     * @return The alive value
+     */
+    @Override
+    public synchronized boolean isAlive()
+    {
+        return !destroyed;
+    }
+
+    /**
+     * Sets whether the queue is actively processing -- if there are working threads.
+     * <p>
+     * @param aState
+     */
+    public synchronized void setAlive( boolean aState )
+    {
+        destroyed = !aState;
+    }
+
+    /**
+     * @return The listenerId value
+     */
+    @Override
+    public long getListenerId()
+    {
+        return listenerId;
+    }
+
+    /**
+     * This adds a put event to the queue. When it is processed, the element will be put to the
+     * listener.
+     * <p>
+     * @param ce The feature to be added to the PutEvent attribute
+     * @throws IOException
+     */
+    @Override
+    public synchronized void addPutEvent( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        if ( isWorking() )
+        {
+            put( new PutEvent( ce ) );
+        }
+        else
+        {
+            if ( log.isWarnEnabled() )
+            {
+                log.warn( "Not enqueuing Put Event for [" + this + "] because it's non-functional." );
+            }
+        }
+    }
+
+    /**
+     * This adds a remove event to the queue. When processed the listener's remove method will be
+     * called for the key.
+     * <p>
+     * @param key The feature to be added to the RemoveEvent attribute
+     * @throws IOException
+     */
+    @Override
+    public synchronized void addRemoveEvent( K key )
+        throws IOException
+    {
+        if ( isWorking() )
+        {
+            put( new RemoveEvent( key ) );
+        }
+        else
+        {
+            if ( log.isWarnEnabled() )
+            {
+                log.warn( "Not enqueuing Remove Event for [" + this + "] because it's non-functional." );
+            }
+        }
+    }
+
+    /**
+     * This adds a remove all event to the queue. When it is processed, all elements will be removed
+     * from the cache.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public synchronized void addRemoveAllEvent()
+        throws IOException
+    {
+        if ( isWorking() )
+        {
+            put( new RemoveAllEvent() );
+        }
+        else
+        {
+            if ( log.isWarnEnabled() )
+            {
+                log.warn( "Not enqueuing RemoveAll Event for [" + this + "] because it's non-functional." );
+            }
+        }
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public synchronized void addDisposeEvent()
+        throws IOException
+    {
+        if ( isWorking() )
+        {
+            put( new DisposeEvent() );
+        }
+        else
+        {
+            if ( log.isWarnEnabled() )
+            {
+                log.warn( "Not enqueuing Dispose Event for [" + this + "] because it's non-functional." );
+            }
+        }
+    }
+
+    /**
+     * Adds an event to the queue.
+     * <p>
+     * @param event
+     */
+    protected abstract void put( AbstractCacheEvent event );
+
+
+    // /////////////////////////// Inner classes /////////////////////////////
+
+    /** The queue is composed of nodes. */
+    protected static class Node
+    {
+        /** Next node in the singly linked list. */
+        Node next = null;
+
+        /** The payload. */
+        AbstractCacheEventQueue<?, ?>.AbstractCacheEvent event = null;
+    }
+
+    /**
+     * Retries before declaring failure.
+     * <p>
+     * @author asmuts
+     */
+    protected abstract class AbstractCacheEvent
+        implements Runnable
+    {
+        /** Number of failures encountered processing this event. */
+        int failures = 0;
+
+        /**
+         * Main processing method for the AbstractCacheEvent object
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            try
+            {
+                doRun();
+            }
+            catch ( IOException e )
+            {
+                if ( log.isWarnEnabled() )
+                {
+                    log.warn( e );
+                }
+                if ( ++failures >= maxFailure )
+                {
+                    if ( log.isWarnEnabled() )
+                    {
+                        log.warn( "Error while running event from Queue: " + this
+                            + ". Dropping Event and marking Event Queue as non-functional." );
+                    }
+                    setWorking( false );
+                    setAlive( false );
+                    return;
+                }
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Error while running event from Queue: " + this + ". Retrying..." );
+                }
+                try
+                {
+                    Thread.sleep( waitBeforeRetry );
+                    run();
+                }
+                catch ( InterruptedException ie )
+                {
+                    if ( log.isErrorEnabled() )
+                    {
+                        log.warn( "Interrupted while sleeping for retry on event " + this + "." );
+                    }
+                    // TODO consider if this is best. maybe we should just
+                    // destroy
+                    setWorking( false );
+                    setAlive( false );
+                }
+            }
+        }
+
+        /**
+         * @throws IOException
+         */
+        protected abstract void doRun()
+            throws IOException;
+    }
+
+    /**
+     * An element should be put in the cache.
+     * <p>
+     * @author asmuts
+     */
+    protected class PutEvent
+        extends AbstractCacheEvent
+    {
+        /** The element to put to the listener */
+        private final ICacheElement<K, V> ice;
+
+        /**
+         * Constructor for the PutEvent object.
+         * <p>
+         * @param ice
+         * @throws IOException
+         */
+        PutEvent( ICacheElement<K, V> ice )
+            throws IOException
+        {
+            this.ice = ice;
+        }
+
+        /**
+         * Call put on the listener.
+         * <p>
+         * @throws IOException
+         */
+        @Override
+        protected void doRun()
+            throws IOException
+        {
+            listener.handlePut( ice );
+        }
+
+        /**
+         * For debugging.
+         * <p>
+         * @return Info on the key and value.
+         */
+        @Override
+        public String toString()
+        {
+            return new StringBuilder( "PutEvent for key: " ).append( ice.getKey() ).append( " value: " )
+                .append( ice.getVal() ).toString();
+        }
+
+    }
+
+    /**
+     * An element should be removed from the cache.
+     * <p>
+     * @author asmuts
+     */
+    protected class RemoveEvent
+        extends AbstractCacheEvent
+    {
+        /** The key to remove from the listener */
+        private final K key;
+
+        /**
+         * Constructor for the RemoveEvent object
+         * <p>
+         * @param key
+         * @throws IOException
+         */
+        RemoveEvent( K key )
+            throws IOException
+        {
+            this.key = key;
+        }
+
+        /**
+         * Call remove on the listener.
+         * <p>
+         * @throws IOException
+         */
+        @Override
+        protected void doRun()
+            throws IOException
+        {
+            listener.handleRemove( cacheName, key );
+        }
+
+        /**
+         * For debugging.
+         * <p>
+         * @return Info on the key to remove.
+         */
+        @Override
+        public String toString()
+        {
+            return new StringBuilder( "RemoveEvent for " ).append( key ).toString();
+        }
+
+    }
+
+    /**
+     * All elements should be removed from the cache when this event is processed.
+     * <p>
+     * @author asmuts
+     */
+    protected class RemoveAllEvent
+        extends AbstractCacheEvent
+    {
+        /**
+         * Call removeAll on the listener.
+         * <p>
+         * @throws IOException
+         */
+        @Override
+        protected void doRun()
+            throws IOException
+        {
+            listener.handleRemoveAll( cacheName );
+        }
+
+        /**
+         * For debugging.
+         * <p>
+         * @return The name of the event.
+         */
+        @Override
+        public String toString()
+        {
+            return "RemoveAllEvent";
+        }
+
+    }
+
+    /**
+     * The cache should be disposed when this event is processed.
+     * <p>
+     * @author asmuts
+     */
+    protected class DisposeEvent
+        extends AbstractCacheEvent
+    {
+        /**
+         * Called when gets to the end of the queue
+         * <p>
+         * @throws IOException
+         */
+        @Override
+        protected void doRun()
+            throws IOException
+        {
+            listener.handleDispose( cacheName );
+        }
+
+        /**
+         * For debugging.
+         * <p>
+         * @return The name of the event.
+         */
+        @Override
+        public String toString()
+        {
+            return "DisposeEvent";
+        }
+    }
+
+    /**
+     * @return whether the queue is functional.
+     */
+    @Override
+    public boolean isWorking()
+    {
+        return working;
+    }
+
+    /**
+     * This means that the queue is functional. If we reached the max number of failures, the queue
+     * is marked as non functional and will never work again.
+     * <p>
+     * @param b
+     */
+    public void setWorking( boolean b )
+    {
+        working = b;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheAdaptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheAdaptor.java
new file mode 100644
index 0000000..7554a31
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheAdaptor.java
@@ -0,0 +1,143 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * Used for Cache-to-Cache messaging purposes. These are used in the balking
+ * facades in the lateral and remote caches.
+ */
+public class CacheAdaptor<K, V>
+    implements ICacheListener<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( CacheAdaptor.class );
+
+    /** The cache we are adapting. */
+    private final ICache<K, V> cache;
+
+    /** The unique id of this listener. */
+    private long listenerId = 0;
+
+    /**
+     * Sets the listenerId attribute of the CacheAdaptor object
+     * <p>
+     * @param id
+     *            The new listenerId value
+     * @throws IOException
+     */
+    @Override
+    public void setListenerId( long id )
+        throws IOException
+    {
+        this.listenerId = id;
+        log.debug( "listenerId = " + id );
+    }
+
+    /**
+     * Gets the listenerId attribute of the CacheAdaptor object
+     * <p>
+     * @return The listenerId value
+     * @throws IOException
+     */
+    @Override
+    public long getListenerId()
+        throws IOException
+    {
+        return this.listenerId;
+    }
+
+    /**
+     * Constructor for the CacheAdaptor object
+     * <p>
+     * @param cache
+     */
+    public CacheAdaptor( ICache<K, V> cache )
+    {
+        this.cache = cache;
+    }
+
+    /**
+     * Puts an item into the cache.
+     * <p>
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void handlePut( ICacheElement<K, V> item )
+        throws IOException
+    {
+        try
+        {
+            cache.update( item );
+        }
+        catch ( Exception e )
+        {
+            // swallow
+        }
+    }
+
+    /**
+     * Removes an item.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void handleRemove( String cacheName, K key )
+        throws IOException
+    {
+        cache.remove( key );
+    }
+
+    /**
+     * Clears the region.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleRemoveAll( String cacheName )
+        throws IOException
+    {
+        cache.removeAll();
+    }
+
+    /**
+     * Shutdown call.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleDispose( String cacheName )
+        throws IOException
+    {
+        cache.dispose();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheConstants.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheConstants.java
new file mode 100644
index 0000000..18ff42c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheConstants.java
@@ -0,0 +1,34 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+/**
+ * Constants used throughout the JCS cache engine
+ * <p>
+ * @version $Id: CacheConstants.java 1590887 2014-04-29 07:07:32Z olamy $
+ */
+public interface CacheConstants
+{
+    /** This is the name of the config file that we will look for by default. */
+    String DEFAULT_CONFIG = "/cache.ccf";
+
+    /** Delimiter of a cache name component. This is used for hierarchical deletion */
+    String NAME_COMPONENT_DELIMITER = ":";
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElement.java
new file mode 100644
index 0000000..224b34d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElement.java
@@ -0,0 +1,160 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+/**
+ * Generic element wrapper. Often stuffed inside another.
+ */
+public class CacheElement<K, V>
+    implements ICacheElement<K, V>
+{
+    /** Don't change */
+    private static final long serialVersionUID = -6062305728297627263L;
+
+    /** The name of the cache region. This is a namespace. */
+    private final String cacheName;
+
+    /** This is the cache key by which the value can be referenced. */
+    private final K key;
+
+    /** This is the cached value, reference by the key. */
+    private final V val;
+
+    /**
+     * These attributes hold information about the element and what it is
+     * allowed to do.
+     */
+    private IElementAttributes attr;
+
+    /**
+     * Constructor for the CacheElement object
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param val
+     */
+    public CacheElement( String cacheName, K key, V val )
+    {
+        this.cacheName = cacheName;
+        this.key = key;
+        this.val = val;
+    }
+
+    /**
+     * Constructor for the CacheElement object
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param val
+     * @param attrArg
+     */
+    public CacheElement( String cacheName, K key, V val, IElementAttributes attrArg )
+    {
+        this(cacheName, key, val);
+        this.attr = attrArg;
+    }
+
+    /**
+     * Gets the cacheName attribute of the CacheElement object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return this.cacheName;
+    }
+
+    /**
+     * Gets the key attribute of the CacheElement object
+     * <p>
+     * @return The key value
+     */
+    @Override
+    public K getKey()
+    {
+        return this.key;
+    }
+
+    /**
+     * Gets the val attribute of the CacheElement object
+     * <p>
+     * @return The val value
+     */
+    @Override
+    public V getVal()
+    {
+        return this.val;
+    }
+
+    /**
+     * Sets the attributes attribute of the CacheElement object
+     * <p>
+     * @param attr
+     *            The new IElementAttributes value
+     */
+    @Override
+    public void setElementAttributes( IElementAttributes attr )
+    {
+        this.attr = attr;
+    }
+
+    /**
+     * Gets the IElementAttributes attribute of the CacheElement object
+     * <p>
+     * @return The IElementAttributes value, never null
+     */
+    @Override
+    public IElementAttributes getElementAttributes()
+    {
+        // create default attributes if they are null
+        // this shouldn't happen, but could if a corrupt
+        // object was sent over the wire.
+        if ( this.attr == null )
+        {
+            this.attr = new ElementAttributes();
+        }
+        return this.attr;
+    }
+
+    /**
+     * @return a hash of the key only
+     */
+    @Override
+    public int hashCode()
+    {
+        return key.hashCode();
+    }
+
+    /**
+     * For debugging only.
+     * <p>
+     * @return String representation
+     */
+    @Override
+    public String toString()
+    {
+        return "[CacheElement: cacheName [" + cacheName + "], key [" + key + "], val [" + val + "], attr [" + attr
+            + "]";
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElementSerialized.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElementSerialized.java
new file mode 100644
index 0000000..8a3d253
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheElementSerialized.java
@@ -0,0 +1,77 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+import java.util.Arrays;
+
+/** Either serialized value or the value should be null; */
+public class CacheElementSerialized<K, V>
+    extends CacheElement<K, V>
+    implements ICacheElementSerialized<K, V>
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -7265084818647601874L;
+
+    /** The serialized value. */
+    private final byte[] serializedValue;
+
+    /**
+     * Constructs a usable wrapper.
+     * <p>
+     * @param cacheNameArg
+     * @param keyArg
+     * @param serializedValueArg
+     * @param elementAttributesArg
+     */
+    public CacheElementSerialized( String cacheNameArg, K keyArg, byte[] serializedValueArg,
+                                   IElementAttributes elementAttributesArg )
+    {
+        super(cacheNameArg, keyArg, null, elementAttributesArg);
+        this.serializedValue = serializedValueArg;
+    }
+
+    /** @return byte[] */
+    @Override
+    public byte[] getSerializedValue()
+    {
+        return this.serializedValue;
+    }
+
+    /**
+     * For debugging only.
+     * <p>
+     * @return debugging string.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n CacheElementSerialized: " );
+        buf.append( "\n CacheName = [" + getCacheName() + "]" );
+        buf.append( "\n Key = [" + getKey() + "]" );
+        buf.append( "\n SerializedValue = " + Arrays.toString(getSerializedValue()) );
+        buf.append( "\n ElementAttributes = " + getElementAttributes() );
+        return buf.toString();
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueue.java
new file mode 100644
index 0000000..230dd09
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueue.java
@@ -0,0 +1,418 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+
+/**
+ * An event queue is used to propagate ordered cache events to one and only one target listener.
+ * <p>
+ * This is a modified version of the experimental version. It should lazy initialize the processor
+ * thread, and kill the thread if the queue goes empty for a specified period, now set to 1 minute.
+ * If something comes in after that a new processor thread should be created.
+ */
+public class CacheEventQueue<K, V>
+    extends AbstractCacheEventQueue<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( CacheEventQueue.class );
+
+    /** The type of queue -- there are pooled and single */
+    private static final QueueType queueType = QueueType.SINGLE;
+
+    /** the thread that works the queue. */
+    private Thread processorThread;
+
+    /** sync */
+    private final Object queueLock = new Object();
+
+    /** the head of the queue */
+    private Node head = new Node();
+
+    /** the end of the queue */
+    private Node tail = head;
+
+    /** Number of items in the queue */
+    private int size = 0;
+
+    /**
+     * Constructs with the specified listener and the cache name.
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     */
+    public CacheEventQueue( ICacheListener<K, V> listener, long listenerId, String cacheName )
+    {
+        this( listener, listenerId, cacheName, 10, 500 );
+    }
+
+    /**
+     * Constructor for the CacheEventQueue object
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     */
+    public CacheEventQueue( ICacheListener<K, V> listener, long listenerId, String cacheName, int maxFailure,
+                            int waitBeforeRetry )
+    {
+        initialize( listener, listenerId, cacheName, maxFailure, waitBeforeRetry, null );
+    }
+
+    /**
+     * Initializes the queue.
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     * @param threadPoolName
+     */
+    @Override
+    public void initialize( ICacheListener<K, V> listener, long listenerId, String cacheName, int maxFailure,
+                            int waitBeforeRetry, String threadPoolName )
+    {
+        if ( listener == null )
+        {
+            throw new IllegalArgumentException( "listener must not be null" );
+        }
+
+        this.listener = listener;
+        this.listenerId = listenerId;
+        this.cacheName = cacheName;
+        this.maxFailure = maxFailure <= 0 ? 3 : maxFailure;
+        this.waitBeforeRetry = waitBeforeRetry <= 0 ? 500 : waitBeforeRetry;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Constructed: " + this );
+        }
+    }
+
+    /**
+     * What type of queue is this.
+     * <p>
+     * @return queueType
+     */
+    @Override
+    public QueueType getQueueType()
+    {
+        return queueType;
+    }
+
+    /**
+     * Kill the processor thread and indicate that the queue is destroyed and no longer alive, but it
+     * can still be working.
+     */
+    public void stopProcessing()
+    {
+        synchronized (queueLock)
+        {
+            destroyed = true;
+            processorThread = null;
+        }
+    }
+
+    /**
+     * Event Q is empty.
+     * <p>
+     * Calling destroy interrupts the processor thread.
+     */
+    @Override
+    public void destroy()
+    {
+        synchronized (queueLock)
+        {
+            if ( !destroyed )
+            {
+                destroyed = true;
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Destroying queue, stats =  " + getStatistics() );
+                }
+
+                // Synchronize on queue so the thread will not wait forever,
+                // and then interrupt the QueueProcessor
+
+                if ( processorThread != null )
+                {
+                    processorThread.interrupt();
+                    processorThread = null;
+                }
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Cache event queue destroyed: " + this );
+                }
+            }
+            else
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Destroy was called after queue was destroyed.  Doing nothing.  Stats =  " + getStatistics() );
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds an event to the queue.
+     * <p>
+     * @param event
+     */
+    @Override
+    protected void put( AbstractCacheEvent event )
+    {
+        Node newNode = new Node();
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Event entering Queue for " + cacheName + ": " + event );
+        }
+
+        newNode.event = event;
+
+        synchronized ( queueLock )
+        {
+            size++;
+            tail.next = newNode;
+            tail = newNode;
+            if ( isWorking() )
+            {
+                if ( !isAlive() )
+                {
+                    destroyed = false;
+                    processorThread = new QProcessor( this );
+                    processorThread.start();
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "Cache event queue created: " + this );
+                    }
+                }
+                else
+                {
+                    queueLock.notify();
+                }
+            }
+        }
+    }
+
+    // /////////////////////////// Inner classes /////////////////////////////
+
+    /**
+     * This is the thread that works the queue.
+     * <p>
+     * @author asmuts
+     * @created January 15, 2002
+     */
+    private class QProcessor
+        extends Thread
+    {
+        /** The queue to work */
+        CacheEventQueue<K, V> queue;
+
+        /**
+         * Constructor for the QProcessor object
+         * <p>
+         * @param aQueue the event queue to take items from.
+         */
+        QProcessor( CacheEventQueue<K, V> aQueue )
+        {
+            super( "CacheEventQueue.QProcessor-" + aQueue.cacheName );
+
+            setDaemon( true );
+            queue = aQueue;
+        }
+
+        /**
+         * Main processing method for the QProcessor object.
+         * <p>
+         * Waits for a specified time (waitToDieMillis) for something to come in and if no new
+         * events come in during that period the run method can exit and the thread is dereferenced.
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            AbstractCacheEvent event = null;
+
+            while ( queue.isAlive() )
+            {
+                event = queue.take();
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Event from queue = " + event );
+                }
+
+                if ( event == null )
+                {
+                    synchronized ( queueLock )
+                    {
+                        try
+                        {
+                            queueLock.wait( queue.getWaitToDieMillis() );
+                        }
+                        catch ( InterruptedException e )
+                        {
+                            log.warn( "Interrupted while waiting for another event to come in before we die." );
+                            return;
+                        }
+                        event = queue.take();
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Event from queue after sleep = " + event );
+                        }
+                    }
+                    if ( event == null )
+                    {
+                        queue.stopProcessing();
+                    }
+                }
+
+                if ( queue.isWorking() && queue.isAlive() && event != null )
+                {
+                    event.run();
+                }
+            }
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "QProcessor exiting for " + queue );
+            }
+        }
+    }
+
+    /**
+     * Returns the next cache event from the queue or null if there are no events in the queue.
+     * <p>
+     * We have an empty node at the head and the tail. When we take an item from the queue we move
+     * the next node to the head and then clear the value from that node. This value is returned.
+     * <p>
+     * When the queue is empty the head node is the same as the tail node.
+     * <p>
+     * @return An event to process.
+     */
+    protected AbstractCacheEvent take()
+    {
+        synchronized ( queueLock )
+        {
+            // wait until there is something to read
+            if ( head == tail )
+            {
+                return null;
+            }
+
+            Node node = head.next;
+
+            @SuppressWarnings("unchecked") // No generics for public fields
+            AbstractCacheEvent value = (AbstractCacheEvent) node.event;
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "head.event = " + head.event );
+                log.debug( "node.event = " + node.event );
+            }
+
+            // Node becomes the new head (head is always empty)
+
+            node.event = null;
+            head = node;
+
+            size--;
+            return value;
+        }
+    }
+
+    /**
+     * This method returns semi-structured data on this queue.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICacheEventQueue#getStatistics()
+     * @return information on the status and history of the queue
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Cache Event Queue" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Boolean>( "Working", Boolean.valueOf(super.isWorking()) ) );
+        elems.add(new StatElement<Boolean>( "Alive", Boolean.valueOf(this.isAlive()) ) );
+        elems.add(new StatElement<Boolean>( "Empty", Boolean.valueOf(this.isEmpty()) ) );
+
+        int sz = 0;
+        synchronized ( queueLock )
+        {
+            // wait until there is something to read
+            if ( head == tail )
+            {
+                sz = 0;
+            }
+            else
+            {
+                Node n = head;
+                while ( n != null )
+                {
+                    n = n.next;
+                    sz++;
+                }
+            }
+
+            elems.add(new StatElement<Integer>( "Size", Integer.valueOf(sz) ) );
+        }
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * @return whether there are any items in the queue.
+     */
+    @Override
+    public boolean isEmpty()
+    {
+        return tail == head;
+    }
+
+    /**
+     * Returns the number of elements in the queue.
+     * <p>
+     * @return number of items in the queue.
+     */
+    @Override
+    public int size()
+    {
+        return size;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueueFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueueFactory.java
new file mode 100644
index 0000000..24b5dc2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheEventQueueFactory.java
@@ -0,0 +1,88 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class hands out event Queues. This allows us to change the implementation more easily. You
+ * can confugure the cache to use a custom type.
+ * <p>
+ * @author aaronsm
+ */
+public class CacheEventQueueFactory<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( CacheEventQueueFactory.class );
+
+    /**
+     * The most commonly used factory method.
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param threadPoolName
+     * @param poolType - SINGLE, POOLED
+     * @return ICacheEventQueue
+     */
+    public ICacheEventQueue<K, V> createCacheEventQueue( ICacheListener<K, V> listener, long listenerId, String cacheName,
+                                                   String threadPoolName, ICacheEventQueue.QueueType poolType )
+    {
+        return createCacheEventQueue( listener, listenerId, cacheName, 10, 500, threadPoolName, poolType );
+    }
+
+    /**
+     * Fully configured event queue.
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     * @param threadPoolName null is OK, if not a pooled event queue this is ignored
+     * @param poolType single or pooled
+     * @return ICacheEventQueue
+     */
+    public ICacheEventQueue<K, V> createCacheEventQueue( ICacheListener<K, V> listener, long listenerId, String cacheName,
+                                                   int maxFailure, int waitBeforeRetry, String threadPoolName,
+                                                   ICacheEventQueue.QueueType poolType )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "threadPoolName = [" + threadPoolName + "] poolType = " + poolType + " " );
+        }
+
+        ICacheEventQueue<K, V> eventQueue = null;
+        if ( poolType == null || ICacheEventQueue.QueueType.SINGLE == poolType )
+        {
+            eventQueue = new CacheEventQueue<K, V>( listener, listenerId, cacheName, maxFailure, waitBeforeRetry );
+        }
+        else if ( ICacheEventQueue.QueueType.POOLED == poolType )
+        {
+            eventQueue = new PooledCacheEventQueue<K, V>( listener, listenerId, cacheName, maxFailure, waitBeforeRetry,
+                                                    threadPoolName );
+        }
+
+        return eventQueue;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheGroup.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheGroup.java
new file mode 100644
index 0000000..0da5a6d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheGroup.java
@@ -0,0 +1,59 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+/**
+ * Holder for attributes specific to a group. The grouping functionality is on
+ * the way out.
+ */
+public class CacheGroup
+{
+    /** Element configuration. */
+    private IElementAttributes attr;
+
+    /** Constructor for the CacheGroup object */
+    public CacheGroup()
+    {
+        super();
+    }
+
+    /**
+     * Sets the attributes attribute of the CacheGroup object
+     * <p>
+     * @param attr
+     *            The new attributes value
+     */
+    public void setElementAttributes( IElementAttributes attr )
+    {
+        this.attr = attr;
+    }
+
+    /**
+     * Gets the attrributes attribute of the CacheGroup object
+     * <p>
+     * @return The attrributes value
+     */
+    public IElementAttributes getElementAttrributes()
+    {
+        return attr;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheInfo.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheInfo.java
new file mode 100644
index 0000000..4115525
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheInfo.java
@@ -0,0 +1,47 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import java.rmi.dgc.VMID;
+
+/**
+ * This is a static variable holder for the distribution auxiliaries that need something like a vmid.
+ */
+public final class CacheInfo
+{
+    /** shouldn't be instantiated */
+    private CacheInfo()
+    {
+        super();
+    }
+
+    /**
+     * Used to identify a client, so we can run multiple clients off one host.
+     * Need since there is no way to identify a client other than by host in
+     * rmi.
+     * <p>
+     * TODO: may have some trouble in failover mode if the cache keeps its old
+     * id. We may need to reset this when moving into failover.
+     */
+    protected static final VMID vmid = new VMID();
+
+    /** By default this is the hashcode of the VMID */
+    public static final long listenerId = vmid.hashCode();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheListeners.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheListeners.java
new file mode 100644
index 0000000..41f3db6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheListeners.java
@@ -0,0 +1,82 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Used to associates a set of [cache listener to cache event queue] for a
+ * cache.
+ */
+public class CacheListeners<K, V>
+{
+    /** The cache using the queue. */
+    public final ICache<K, V> cache;
+
+    /** Map ICacheListener to ICacheEventQueue */
+    public final Map<Long, ICacheEventQueue<K, V>> eventQMap =
+        new ConcurrentHashMap<Long, ICacheEventQueue<K, V>>();
+
+    /**
+     * Constructs with the given cache.
+     * <p>
+     * @param cache
+     */
+    public CacheListeners( ICache<K, V> cache )
+    {
+        if ( cache == null )
+        {
+            throw new IllegalArgumentException( "cache must not be null" );
+        }
+        this.cache = cache;
+    }
+
+    /** @return info on the listeners */
+    @Override
+    public String toString()
+    {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append( "\n CacheListeners" );
+        if ( cache != null )
+        {
+            buffer.append( "\n Region = " + cache.getCacheName() );
+        }
+        if ( eventQMap != null )
+        {
+            buffer.append( "\n Event Queue Map " );
+            buffer.append( "\n size = " + eventQMap.size() );
+            Iterator<Map.Entry<Long, ICacheEventQueue<K, V>>> it = eventQMap.entrySet().iterator();
+            while ( it.hasNext() )
+            {
+                buffer.append( "\n Entry: " + it.next() );
+            }
+        }
+        else
+        {
+            buffer.append( "\n No Listeners. " );
+        }
+        return buffer.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheStatus.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheStatus.java
new file mode 100644
index 0000000..ab79e08
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheStatus.java
@@ -0,0 +1,37 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+/**
+ * Cache statuses
+ * <p>
+ * @version $Id: CacheStatus.java 1590887 2014-04-29 07:07:32Z olamy $
+ */
+public enum CacheStatus
+{
+    /** Cache alive status. */
+    ALIVE,
+
+    /** Cache disposed status. */
+    DISPOSED,
+
+    /** Cache in error. */
+    ERROR
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheWatchRepairable.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheWatchRepairable.java
new file mode 100644
index 0000000..c5654a2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CacheWatchRepairable.java
@@ -0,0 +1,200 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.behavior.ICacheObserver;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Intercepts the requests to the underlying ICacheObserver object so that the listeners can be
+ * recorded locally for remote connection recovery purposes. (Durable subscription like those in JMS
+ * is not implemented at this stage for it can be too expensive.)
+ */
+public class CacheWatchRepairable
+    implements ICacheObserver
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( CacheWatchRepairable.class );
+
+    /** the underlying ICacheObserver. */
+    private ICacheObserver cacheWatch;
+
+    /** Map of cache regions. */
+    private final Map<String, Set<ICacheListener<?, ?>>> cacheMap =
+        new HashMap<String, Set<ICacheListener<?, ?>>>();
+
+    /**
+     * Replaces the underlying cache watch service and reattached all existing listeners to the new
+     * cache watch.
+     * <p>
+     * @param cacheWatch The new cacheWatch value
+     */
+    public void setCacheWatch( ICacheObserver cacheWatch )
+    {
+        this.cacheWatch = cacheWatch;
+        synchronized ( cacheMap )
+        {
+            for (Map.Entry<String, Set<ICacheListener<?, ?>>> entry : cacheMap.entrySet())
+            {
+                String cacheName = entry.getKey();
+                for (ICacheListener<?, ?> listener : entry.getValue())
+                {
+                    try
+                    {
+                        if ( log.isInfoEnabled() )
+                        {
+                            log.info( "Adding listener to cache watch. ICacheListener = " + listener
+                                + " | ICacheObserver = " + cacheWatch );
+                        }
+                        cacheWatch.addCacheListener( cacheName, listener );
+                    }
+                    catch ( IOException ex )
+                    {
+                        log.error( "Problem adding listener. ICacheListener = " + listener + " | ICacheObserver = "
+                            + cacheWatch, ex );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a feature to the CacheListener attribute of the CacheWatchRepairable object
+     * <p>
+     * @param cacheName The feature to be added to the CacheListener attribute
+     * @param obj The feature to be added to the CacheListener attribute
+     * @throws IOException
+     */
+    @Override
+    public <K, V> void addCacheListener( String cacheName, ICacheListener<K, V> obj )
+        throws IOException
+    {
+        // Record the added cache listener locally, regardless of whether the
+        // remote add-listener operation succeeds or fails.
+        synchronized ( cacheMap )
+        {
+            Set<ICacheListener<?, ?>> listenerSet = cacheMap.get( cacheName );
+            if ( listenerSet == null )
+            {
+                listenerSet = new HashSet<ICacheListener<?, ?>>();
+                cacheMap.put( cacheName, listenerSet );
+            }
+            listenerSet.add( obj );
+        }
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Adding listener to cache watch. ICacheListener = " + obj
+                + " | ICacheObserver = " + cacheWatch + " | cacheName = " + cacheName );
+        }
+        cacheWatch.addCacheListener( cacheName, obj );
+    }
+
+    /**
+     * Adds a feature to the CacheListener attribute of the CacheWatchRepairable object
+     * <p>
+     * @param obj The feature to be added to the CacheListener attribute
+     * @throws IOException
+     */
+    @Override
+    public <K, V> void addCacheListener( ICacheListener<K, V> obj )
+        throws IOException
+    {
+        // Record the added cache listener locally, regardless of whether the
+        // remote add-listener operation succeeds or fails.
+        synchronized ( cacheMap )
+        {
+            for (Set<ICacheListener<?, ?>> listenerSet : cacheMap.values())
+            {
+                listenerSet.add( obj );
+            }
+        }
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Adding listener to cache watch. ICacheListener = " + obj
+                + " | ICacheObserver = " + cacheWatch );
+        }
+        cacheWatch.addCacheListener( obj );
+    }
+
+    /**
+     * Tell the server to release us.
+     * <p>
+     * @param cacheName
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <K, V> void removeCacheListener( String cacheName, ICacheListener<K, V> obj )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "removeCacheListener, cacheName [" + cacheName + "]" );
+        }
+        // Record the removal locally, regardless of whether the remote
+        // remove-listener operation succeeds or fails.
+        synchronized ( cacheMap )
+        {
+            Set<ICacheListener<?, ?>> listenerSet = cacheMap.get( cacheName );
+            if ( listenerSet != null )
+            {
+                listenerSet.remove( obj );
+            }
+        }
+        cacheWatch.removeCacheListener( cacheName, obj );
+    }
+
+    /**
+     * @param obj
+     * @throws IOException
+     */
+    @Override
+    public <K, V> void removeCacheListener( ICacheListener<K, V> obj )
+        throws IOException
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "removeCacheListener, ICacheListener [" + obj + "]" );
+        }
+
+        // Record the removal locally, regardless of whether the remote
+        // remove-listener operation succeeds or fails.
+        synchronized ( cacheMap )
+        {
+            for (Set<ICacheListener<?, ?>> listenerSet : cacheMap.values())
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Before removing [" + obj + "] the listenerSet = " + listenerSet );
+                }
+                listenerSet.remove( obj );
+            }
+        }
+        cacheWatch.removeCacheListener( obj );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CompositeCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CompositeCacheAttributes.java
new file mode 100644
index 0000000..bce474e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/CompositeCacheAttributes.java
@@ -0,0 +1,447 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+
+/**
+ * The CompositeCacheAttributes defines the general cache region settings. If a region is not
+ * explicitly defined in the cache.ccf then it inherits the cache default settings.
+ * <p>
+ * If all the default attributes are not defined in the default region definition in the cache.ccf,
+ * the hard coded defaults will be used.
+ */
+public class CompositeCacheAttributes
+    implements ICompositeCacheAttributes, Cloneable
+{
+    /** Don't change */
+    private static final long serialVersionUID = 6754049978134196787L;
+
+    /** default lateral switch */
+    private static final boolean DEFAULT_USE_LATERAL = true;
+
+    /** default remote switch */
+    private static final boolean DEFAULT_USE_REMOTE = true;
+
+    /** default disk switch */
+    private static final boolean DEFAULT_USE_DISK = true;
+
+    /** default shrinker setting */
+    private static final boolean DEFAULT_USE_SHRINKER = false;
+
+    /** default max objects value */
+    private static final int DEFAULT_MAX_OBJECTS = 100;
+
+    /** default */
+    private static final int DEFAULT_MAX_MEMORY_IDLE_TIME_SECONDS = 60 * 120;
+
+    /** default interval to run the shrinker */
+    private static final int DEFAULT_SHRINKER_INTERVAL_SECONDS = 30;
+
+    /** default */
+    private static final int DEFAULT_MAX_SPOOL_PER_RUN = -1;
+
+    /** default */
+    private static final String DEFAULT_MEMORY_CACHE_NAME = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+
+    /** Default number to send to disk at a time when memory fills. */
+    private static final int DEFAULT_CHUNK_SIZE = 2;
+
+    /** allow lateral caches */
+    private boolean useLateral = DEFAULT_USE_LATERAL;
+
+    /** allow remote caches */
+    private boolean useRemote = DEFAULT_USE_REMOTE;
+
+    /** Whether we should use a disk cache if it is configured. */
+    private boolean useDisk = DEFAULT_USE_DISK;
+
+    /** Whether or not we should run the memory shrinker thread. */
+    private boolean useMemoryShrinker = DEFAULT_USE_SHRINKER;
+
+    /** The maximum objects that the memory cache will be allowed to hold. */
+    private int maxObjs = DEFAULT_MAX_OBJECTS;
+
+    /** maxMemoryIdleTimeSeconds */
+    private long maxMemoryIdleTimeSeconds = DEFAULT_MAX_MEMORY_IDLE_TIME_SECONDS;
+
+    /** shrinkerIntervalSeconds */
+    private long shrinkerIntervalSeconds = DEFAULT_SHRINKER_INTERVAL_SECONDS;
+
+    /** The maximum number the shrinker will spool to disk per run. */
+    private int maxSpoolPerRun = DEFAULT_MAX_SPOOL_PER_RUN;
+
+    /** The name of this cache region. */
+    private String cacheName;
+
+    /** The name of the memory cache implementation class. */
+    private String memoryCacheName;
+
+    /** Set via DISK_USAGE_PATTERN_NAME */
+    private DiskUsagePattern diskUsagePattern = DiskUsagePattern.SWAP;
+
+    /** How many to spool to disk at a time. */
+    private int spoolChunkSize = DEFAULT_CHUNK_SIZE;
+
+    /**
+     * Constructor for the CompositeCacheAttributes object
+     */
+    public CompositeCacheAttributes()
+    {
+        super();
+        // set this as the default so the configuration is a bit simpler
+        memoryCacheName = DEFAULT_MEMORY_CACHE_NAME;
+    }
+
+    /**
+     * Sets the maxObjects attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param maxObjs The new maxObjects value
+     */
+    @Override
+    public void setMaxObjects( int maxObjs )
+    {
+        this.maxObjs = maxObjs;
+    }
+
+    /**
+     * Gets the maxObjects attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The maxObjects value
+     */
+    @Override
+    public int getMaxObjects()
+    {
+        return this.maxObjs;
+    }
+
+    /**
+     * Sets the useDisk attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param useDisk The new useDisk value
+     */
+    @Override
+    public void setUseDisk( boolean useDisk )
+    {
+        this.useDisk = useDisk;
+    }
+
+    /**
+     * Gets the useDisk attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The useDisk value
+     */
+    @Override
+    public boolean isUseDisk()
+    {
+        return useDisk;
+    }
+
+    /**
+     * Sets the useLateral attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param b The new useLateral value
+     */
+    @Override
+    public void setUseLateral( boolean b )
+    {
+        this.useLateral = b;
+    }
+
+    /**
+     * Gets the useLateral attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The useLateral value
+     */
+    @Override
+    public boolean isUseLateral()
+    {
+        return this.useLateral;
+    }
+
+    /**
+     * Sets the useRemote attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param useRemote The new useRemote value
+     */
+    @Override
+    public void setUseRemote( boolean useRemote )
+    {
+        this.useRemote = useRemote;
+    }
+
+    /**
+     * Gets the useRemote attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The useRemote value
+     */
+    @Override
+    public boolean isUseRemote()
+    {
+        return this.useRemote;
+    }
+
+    /**
+     * Sets the cacheName attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param s The new cacheName value
+     */
+    @Override
+    public void setCacheName( String s )
+    {
+        this.cacheName = s;
+    }
+
+    /**
+     * Gets the cacheName attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return this.cacheName;
+    }
+
+    /**
+     * Sets the memoryCacheName attribute of the CompositeCacheAttributes object
+     * <p>
+     * @param s The new memoryCacheName value
+     */
+    @Override
+    public void setMemoryCacheName( String s )
+    {
+        this.memoryCacheName = s;
+    }
+
+    /**
+     * Gets the memoryCacheName attribute of the CompositeCacheAttributes object
+     * <p>
+     * @return The memoryCacheName value
+     */
+    @Override
+    public String getMemoryCacheName()
+    {
+        return this.memoryCacheName;
+    }
+
+    /**
+     * Whether the memory cache should perform background memory shrinkage.
+     * <p>
+     * @param useShrinker The new UseMemoryShrinker value
+     */
+    @Override
+    public void setUseMemoryShrinker( boolean useShrinker )
+    {
+        this.useMemoryShrinker = useShrinker;
+    }
+
+    /**
+     * Whether the memory cache should perform background memory shrinkage.
+     * <p>
+     * @return The UseMemoryShrinker value
+     */
+    @Override
+    public boolean isUseMemoryShrinker()
+    {
+        return this.useMemoryShrinker;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * <p>
+     * @param seconds The new MaxMemoryIdleTimeSeconds value
+     */
+    @Override
+    public void setMaxMemoryIdleTimeSeconds( long seconds )
+    {
+        this.maxMemoryIdleTimeSeconds = seconds;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * <p>
+     * @return The MaxMemoryIdleTimeSeconds value
+     */
+    @Override
+    public long getMaxMemoryIdleTimeSeconds()
+    {
+        return this.maxMemoryIdleTimeSeconds;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * This sets the shrinker interval.
+     * <p>
+     * @param seconds The new ShrinkerIntervalSeconds value
+     */
+    @Override
+    public void setShrinkerIntervalSeconds( long seconds )
+    {
+        this.shrinkerIntervalSeconds = seconds;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * This gets the shrinker interval.
+     * <p>
+     * @return The ShrinkerIntervalSeconds value
+     */
+    @Override
+    public long getShrinkerIntervalSeconds()
+    {
+        return this.shrinkerIntervalSeconds;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * This sets the maximum number of items to spool per run.
+     * <p>
+     * If the value is -1, then there is no limit to the number of items to be spooled.
+     * <p>
+     * @param maxSpoolPerRun The new maxSpoolPerRun value
+     */
+    @Override
+    public void setMaxSpoolPerRun( int maxSpoolPerRun )
+    {
+        this.maxSpoolPerRun = maxSpoolPerRun;
+    }
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements to reclaim space.
+     * This gets the maximum number of items to spool per run.
+     * <p>
+     * @return The maxSpoolPerRun value
+     */
+    @Override
+    public int getMaxSpoolPerRun()
+    {
+        return this.maxSpoolPerRun;
+    }
+
+    /**
+     * By default this is SWAP_ONLY.
+     * <p>
+     * @param diskUsagePattern The diskUsagePattern to set.
+     */
+    @Override
+    public void setDiskUsagePattern( DiskUsagePattern diskUsagePattern )
+    {
+        this.diskUsagePattern = diskUsagePattern;
+    }
+
+    /**
+     * Translates the name to the disk usage pattern short value.
+     * <p>
+     * The allowed values are SWAP and UPDATE.
+     * <p>
+     * @param diskUsagePatternName The diskUsagePattern to set.
+     */
+    @Override
+    public void setDiskUsagePatternName( String diskUsagePatternName )
+    {
+        if ( diskUsagePatternName != null )
+        {
+            String name = diskUsagePatternName.toUpperCase().trim();
+            if ( name.startsWith( "SWAP" ) )
+            {
+                this.setDiskUsagePattern( DiskUsagePattern.SWAP );
+            }
+            else if ( name.startsWith( "UPDATE" ) )
+            {
+                this.setDiskUsagePattern( DiskUsagePattern.UPDATE );
+            }
+        }
+    }
+
+    /**
+     * Number to send to disk at at time when memory is full.
+     * <p>
+     * @return int
+     */
+    @Override
+    public int getSpoolChunkSize()
+    {
+        return spoolChunkSize;
+    }
+
+    /**
+     * Number to send to disk at a time.
+     * <p>
+     * @param spoolChunkSize
+     */
+    @Override
+    public void setSpoolChunkSize( int spoolChunkSize )
+    {
+        this.spoolChunkSize = spoolChunkSize;
+    }
+
+    /**
+     * @return Returns the diskUsagePattern.
+     */
+    @Override
+    public DiskUsagePattern getDiskUsagePattern()
+    {
+        return diskUsagePattern;
+    }
+
+    /**
+     * Description of the Method
+     * <p>
+     * @return ICompositeCacheAttributes a copy
+     */
+    @Override
+    public ICompositeCacheAttributes copy()
+    {
+        try
+        {
+            ICompositeCacheAttributes cattr = (CompositeCacheAttributes) this.clone();
+            return cattr;
+        }
+        catch ( Exception e )
+        {
+            System.err.println( e.toString() );
+            return new CompositeCacheAttributes();
+        }
+    }
+
+    /**
+     * Dumps the core attributes.
+     * <p>
+     * @return For debugging.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder dump = new StringBuilder();
+
+        dump.append( "[ " );
+        dump.append( "useLateral = " ).append( useLateral );
+        dump.append( ", useRemote = " ).append( useRemote );
+        dump.append( ", useDisk = " ).append( useDisk );
+        dump.append( ", maxObjs = " ).append( maxObjs );
+        dump.append( ", maxSpoolPerRun = " ).append( maxSpoolPerRun );
+        dump.append( ", diskUsagePattern = " ).append( diskUsagePattern );
+        dump.append( ", spoolChunkSize = " ).append( spoolChunkSize );
+        dump.append( " ]" );
+
+        return dump.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ElementAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ElementAttributes.java
new file mode 100644
index 0000000..e0d302f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ElementAttributes.java
@@ -0,0 +1,469 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This it the element attribute descriptor class. Each element in the cache has an ElementAttribute
+ * object associated with it. An ElementAttributes object can be associated with an element in 3
+ * ways:
+ * <ol>
+ * <li>When the item is put into the cache, you can associate an element attributes object.</li>
+ * <li>If not attributes object is include when the element is put into the cache, then the default
+ * attributes for the region will be used.</li>
+ * <li>The element attributes can be reset. This effectively results in a retrieval followed by a
+ * put. Hence, this is the same as 1.</li>
+ * </ol>
+ */
+public class ElementAttributes
+    implements IElementAttributes, Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 7814990748035017441L;
+
+    /** Can this item be flushed to disk */
+    private boolean IS_SPOOL = true;
+
+    /** Is this item laterally distributable */
+    private boolean IS_LATERAL = true;
+
+    /** Can this item be sent to the remote cache */
+    private boolean IS_REMOTE = true;
+
+    /**
+     * You can turn off expiration by setting this to true. This causes the cache to bypass both max
+     * life and idle time expiration.
+     */
+    private boolean IS_ETERNAL = true;
+
+    /** Max life seconds */
+    private long maxLife = -1;
+
+    /**
+     * The maximum time an entry can be idle. Setting this to -1 causes the idle time check to be
+     * ignored.
+     */
+    private long maxIdleTime = -1;
+
+    /** The byte size of the field. Must be manually set. */
+    private int size = 0;
+
+    /** The creation time. This is used to enforce the max life. */
+    private long createTime = 0;
+
+    /** The last access time. This is used to enforce the max idel time. */
+    private long lastAccessTime = 0;
+
+    /**
+     * The list of Event handlers to use. This is transient, since the event handlers cannot usually
+     * be serialized. This means that you cannot attach a post serialization event to an item.
+     * <p>
+     * TODO we need to check that when an item is passed to a non-local cache that if the local
+     * cache had a copy with event handlers, that those handlers are used.
+     */
+    private transient ArrayList<IElementEventHandler> eventHandlers;
+
+    private long timeFactor = 1000;
+
+    /**
+     * Constructor for the IElementAttributes object
+     */
+    public ElementAttributes()
+    {
+        this.createTime = System.currentTimeMillis();
+        this.lastAccessTime = this.createTime;
+    }
+
+    /**
+     * Constructor for the IElementAttributes object
+     * <p>
+     * @param attr
+     */
+    protected ElementAttributes( ElementAttributes attr )
+    {
+        IS_ETERNAL = attr.IS_ETERNAL;
+
+        // waterfall onto disk, for pure disk set memory to 0
+        IS_SPOOL = attr.IS_SPOOL;
+
+        // lateral
+        IS_LATERAL = attr.IS_LATERAL;
+
+        // central rmi store
+        IS_REMOTE = attr.IS_REMOTE;
+
+        maxLife = attr.maxLife;
+        // time-to-live
+        maxIdleTime = attr.maxIdleTime;
+        size = attr.size;
+    }
+
+    /**
+     * Copies the attributes, including references to event handlers.
+     * <p>
+     * @return a copy of the Attributes
+     */
+    @Override
+    public IElementAttributes copy()
+    {
+        try
+        {
+            // need to make this more efficient. Just want to insure
+            // a proper copy
+            ElementAttributes attr = new ElementAttributes();
+            attr.setIdleTime( this.getIdleTime() );
+            attr.setIsEternal( this.getIsEternal() );
+            attr.setIsLateral( this.getIsLateral() );
+            attr.setIsRemote( this.getIsRemote() );
+            attr.setIsSpool( this.getIsSpool() );
+            attr.setMaxLife(this.getMaxLife());
+            attr.addElementEventHandlers( this.eventHandlers );
+            return attr;
+        }
+        catch ( Exception e )
+        {
+            return new ElementAttributes();
+        }
+    }
+
+    /**
+     * Sets the maxLife attribute of the IAttributes object.
+     * <p>
+     * @param mls The new MaxLifeSeconds value
+     */
+    @Override
+    public void setMaxLife(long mls)
+    {
+        this.maxLife = mls;
+    }
+
+    /**
+     * Sets the maxLife attribute of the IAttributes object. How many seconds it can live after
+     * creation.
+     * <p>
+     * If this is exceeded the element will not be returned, instead it will be removed. It will be
+     * removed on retrieval, or removed actively if the memory shrinker is turned on.
+     * @return The MaxLifeSeconds value
+     */
+    @Override
+    public long getMaxLife()
+    {
+        return this.maxLife;
+    }
+
+    /**
+     * Sets the idleTime attribute of the IAttributes object. This is the maximum time the item can
+     * be idle in the cache, that is not accessed.
+     * <p>
+     * If this is exceeded the element will not be returned, instead it will be removed. It will be
+     * removed on retrieval, or removed actively if the memory shrinker is turned on.
+     * @param idle The new idleTime value
+     */
+    @Override
+    public void setIdleTime( long idle )
+    {
+        this.maxIdleTime = idle;
+    }
+
+    /**
+     * Size in bytes. This is not used except in the admin pages. It will be -1 by default.
+     * <p>
+     * @param size The new size value
+     */
+    @Override
+    public void setSize( int size )
+    {
+        this.size = size;
+    }
+
+    /**
+     * Gets the size attribute of the IAttributes object
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return size;
+    }
+
+    /**
+     * Gets the createTime attribute of the IAttributes object.
+     * <p>
+     * This should be the current time in milliseconds returned by the sysutem call when the element
+     * is put in the cache.
+     * <p>
+     * Putting an item in the cache overrides any existing items.
+     * @return The createTime value
+     */
+    @Override
+    public long getCreateTime()
+    {
+        return createTime;
+    }
+
+    /**
+     * Sets the createTime attribute of the IElementAttributes object
+     */
+    public void setCreateTime()
+    {
+        createTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Gets the idleTime attribute of the IAttributes object.
+     * <p>
+     * @return The idleTime value
+     */
+    @Override
+    public long getIdleTime()
+    {
+        return this.maxIdleTime;
+    }
+
+    /**
+     * Gets the time left to live of the IAttributes object.
+     * <p>
+     * This is the (max life + create time) - current time.
+     * @return The TimeToLiveSeconds value
+     */
+    @Override
+    public long getTimeToLiveSeconds()
+    {
+        final long now = System.currentTimeMillis();
+        final long timeFactorForMilliseconds = getTimeFactorForMilliseconds();
+        return ( this.getCreateTime() + this.getMaxLife() * timeFactorForMilliseconds - now ) / 1000;
+    }
+
+    /**
+     * Gets the LastAccess attribute of the IAttributes object.
+     * <p>
+     * @return The LastAccess value.
+     */
+    @Override
+    public long getLastAccessTime()
+    {
+        return this.lastAccessTime;
+    }
+
+    /**
+     * Sets the LastAccessTime as now of the IElementAttributes object
+     */
+    @Override
+    public void setLastAccessTimeNow()
+    {
+        this.lastAccessTime = System.currentTimeMillis();
+    }
+
+    /**
+     * only for use from test code
+     */
+    public void setLastAccessTime(long time)
+    {
+        this.lastAccessTime = time;
+    }
+
+    /**
+     * Can this item be spooled to disk
+     * <p>
+     * By default this is true.
+     * @return The spoolable value
+     */
+    @Override
+    public boolean getIsSpool()
+    {
+        return this.IS_SPOOL;
+    }
+
+    /**
+     * Sets the isSpool attribute of the IElementAttributes object
+     * <p>
+     * By default this is true.
+     * @param val The new isSpool value
+     */
+    @Override
+    public void setIsSpool( boolean val )
+    {
+        this.IS_SPOOL = val;
+    }
+
+    /**
+     * Is this item laterally distributable. Can it be sent to auxiliaries of type lateral.
+     * <p>
+     * By default this is true.
+     * @return The isLateral value
+     */
+    @Override
+    public boolean getIsLateral()
+    {
+        return this.IS_LATERAL;
+    }
+
+    /**
+     * Sets the isLateral attribute of the IElementAttributes object
+     * <p>
+     * By default this is true.
+     * @param val The new isLateral value
+     */
+    @Override
+    public void setIsLateral( boolean val )
+    {
+        this.IS_LATERAL = val;
+    }
+
+    /**
+     * Can this item be sent to the remote cache
+     * @return true if the item can be sent to a remote auxiliary
+     */
+    @Override
+    public boolean getIsRemote()
+    {
+        return this.IS_REMOTE;
+    }
+
+    /**
+     * Sets the isRemote attribute of the ElementAttributes object
+     * @param val The new isRemote value
+     */
+    @Override
+    public void setIsRemote( boolean val )
+    {
+        this.IS_REMOTE = val;
+    }
+
+    /**
+     * You can turn off expiration by setting this to true. The max life value will be ignored.
+     * <p>
+     * @return true if the item cannot expire.
+     */
+    @Override
+    public boolean getIsEternal()
+    {
+        return this.IS_ETERNAL;
+    }
+
+    /**
+     * Sets the isEternal attribute of the ElementAttributes object. True means that the item should
+     * never expire. If can still be removed if it is the least recently used, and you are using the
+     * LRUMemory cache. it just will not be filtered for expiration by the cache hub.
+     * <p>
+     * @param val The new isEternal value
+     */
+    @Override
+    public void setIsEternal( boolean val )
+    {
+        this.IS_ETERNAL = val;
+    }
+
+    /**
+     * Adds a ElementEventHandler. Handler's can be registered for multiple events. A registered
+     * handler will be called at every recognized event.
+     * <p>
+     * The alternative would be to register handlers for each event. Or maybe The handler interface
+     * should have a method to return whether it cares about certain events.
+     * <p>
+     * @param eventHandler The ElementEventHandler to be added to the list.
+     */
+    @Override
+    public void addElementEventHandler( IElementEventHandler eventHandler )
+    {
+        // lazy here, no concurrency problems expected
+        if ( this.eventHandlers == null )
+        {
+            this.eventHandlers = new ArrayList<IElementEventHandler>();
+        }
+        this.eventHandlers.add( eventHandler );
+    }
+
+    /**
+     * Sets the eventHandlers of the IElementAttributes object.
+     * <p>
+     * This add the references to the local list. Subsequent changes in the caller's list will not
+     * be reflected.
+     * <p>
+     * @param eventHandlers List of IElementEventHandler objects
+     */
+    @Override
+    public void addElementEventHandlers( List<IElementEventHandler> eventHandlers )
+    {
+        if ( eventHandlers == null )
+        {
+            return;
+        }
+
+        for (IElementEventHandler handler : eventHandlers)
+        {
+            addElementEventHandler(handler);
+        }
+    }
+
+    @Override
+    public long getTimeFactorForMilliseconds()
+    {
+        return timeFactor;
+    }
+
+    @Override
+    public void setTimeFactorForMilliseconds(long factor)
+    {
+        this.timeFactor = factor;
+    }
+
+    /**
+     * Gets the elementEventHandlers. Returns null if none exist. Makes checking easy.
+     * <p>
+     * @return The elementEventHandlers List of IElementEventHandler objects
+     */
+    @Override
+    public ArrayList<IElementEventHandler> getElementEventHandlers()
+    {
+        return this.eventHandlers;
+    }
+
+    /**
+     * For logging and debugging the element IElementAttributes.
+     * <p>
+     * @return String info about the values.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder dump = new StringBuilder();
+
+        dump.append( "[ IS_LATERAL = " ).append( IS_LATERAL );
+        dump.append( ", IS_SPOOL = " ).append( IS_SPOOL );
+        dump.append( ", IS_REMOTE = " ).append( IS_REMOTE );
+        dump.append( ", IS_ETERNAL = " ).append( IS_ETERNAL );
+        dump.append( ", MaxLifeSeconds = " ).append( this.getMaxLife() );
+        dump.append( ", IdleTime = " ).append( this.getIdleTime() );
+        dump.append( ", CreateTime = " ).append( this.getCreateTime() );
+        dump.append( ", LastAccessTime = " ).append( this.getLastAccessTime() );
+        dump.append( ", getTimeToLiveSeconds() = " ).append( String.valueOf( getTimeToLiveSeconds() ) );
+        dump.append( ", createTime = " ).append( String.valueOf( createTime ) ).append( " ]" );
+
+        return dump.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/PooledCacheEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/PooledCacheEventQueue.java
new file mode 100644
index 0000000..56cc973
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/PooledCacheEventQueue.java
@@ -0,0 +1,228 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.jcs.utils.threadpool.ThreadPoolManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * An event queue is used to propagate ordered cache events to one and only one target listener.
+ * <p>
+ * This is a modified version of the experimental version. It uses a PooledExecutor and a
+ * BoundedBuffer to queue up events and execute them as threads become available.
+ * <p>
+ * The PooledExecutor is static, because presumably these processes will be IO bound, so throwing
+ * more than a few threads at them will serve no purpose other than to saturate the IO interface. In
+ * light of this, having one thread per region seems unnecessary. This may prove to be false.
+ */
+public class PooledCacheEventQueue<K, V>
+    extends AbstractCacheEventQueue<K, V>
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( PooledCacheEventQueue.class );
+
+    /** The type of event queue */
+    private static final QueueType queueType = QueueType.POOLED;
+
+    /** The Thread Pool to execute events with. */
+    private ThreadPoolExecutor pool = null;
+
+    /**
+     * Constructor for the CacheEventQueue object
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     * @param threadPoolName
+     */
+    public PooledCacheEventQueue( ICacheListener<K, V> listener, long listenerId, String cacheName, int maxFailure,
+                                  int waitBeforeRetry, String threadPoolName )
+    {
+        initialize( listener, listenerId, cacheName, maxFailure, waitBeforeRetry, threadPoolName );
+    }
+
+    /**
+     * Initializes the queue.
+     * <p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     * @param threadPoolName
+     */
+    @Override
+    public void initialize( ICacheListener<K, V> listener, long listenerId, String cacheName, int maxFailure,
+                            int waitBeforeRetry, String threadPoolName )
+    {
+        if ( listener == null )
+        {
+            throw new IllegalArgumentException( "listener must not be null" );
+        }
+
+        this.listener = listener;
+        this.listenerId = listenerId;
+        this.cacheName = cacheName;
+        this.maxFailure = maxFailure <= 0 ? 3 : maxFailure;
+        this.waitBeforeRetry = waitBeforeRetry <= 0 ? 500 : waitBeforeRetry;
+
+        // this will share the same pool with other event queues by default.
+        pool = ThreadPoolManager.getInstance().getPool(
+                (threadPoolName == null) ? "cache_event_queue" : threadPoolName );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Initialized: " + this );
+        }
+    }
+
+    /**
+     * @return the queue type
+     */
+    @Override
+    public QueueType getQueueType()
+    {
+        return queueType;
+    }
+
+    /**
+     * Event Q is empty.
+     */
+    public synchronized void stopProcessing()
+    {
+        destroyed = true;
+    }
+
+    /**
+     * Destroy the queue. Interrupt all threads.
+     */
+    @Override
+    public synchronized void destroy()
+    {
+        if ( !destroyed )
+        {
+            destroyed = true;
+            pool.shutdownNow();
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Cache event queue destroyed: " + this );
+            }
+        }
+    }
+
+    /**
+     * Adds an event to the queue.
+     * <p>
+     * @param event
+     */
+    @Override
+    protected void put( AbstractCacheEvent event )
+    {
+        pool.execute( event );
+    }
+
+    /**
+     * @return Statistics info
+     */
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * @return IStats
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Pooled Cache Event Queue" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Boolean>( "Working", Boolean.valueOf(super.isWorking()) ) );
+        elems.add(new StatElement<Boolean>( "Alive", Boolean.valueOf(this.isAlive()) ) );
+        elems.add(new StatElement<Boolean>( "Empty", Boolean.valueOf(this.isEmpty()) ) );
+
+        if ( pool.getQueue() != null )
+        {
+            BlockingQueue<Runnable> bb = pool.getQueue();
+            elems.add(new StatElement<Integer>( "Queue Size", Integer.valueOf(bb.size()) ) );
+            elems.add(new StatElement<Integer>( "Queue Capacity", Integer.valueOf(bb.remainingCapacity()) ) );
+        }
+
+        elems.add(new StatElement<Integer>( "Pool Size", Integer.valueOf(pool.getPoolSize()) ) );
+        elems.add(new StatElement<Integer>( "Maximum Pool Size", Integer.valueOf(pool.getMaximumPoolSize()) ) );
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * If the Queue is using a bounded channel we can determine the size. If it is zero or we can't
+     * determine the size, we return true.
+     * <p>
+     * @return whether or not there are items in the queue
+     */
+    @Override
+    public boolean isEmpty()
+    {
+        if ( pool.getQueue() == null )
+        {
+            return true;
+        }
+        else
+        {
+            return pool.getQueue().size() == 0;
+        }
+    }
+
+    /**
+     * Returns the number of elements in the queue. If the queue cannot determine the size
+     * accurately it will return 1.
+     * <p>
+     * @return number of items in the queue.
+     */
+    @Override
+    public int size()
+    {
+        if ( pool.getQueue() == null )
+        {
+            return 0;
+        }
+        else
+        {
+            return pool.getQueue().size();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheService.java
new file mode 100644
index 0000000..dfa47fe
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheService.java
@@ -0,0 +1,156 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheService;
+import org.apache.commons.jcs.engine.behavior.IZombie;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Zombie adapter for any cache service. Balks at every call.
+ */
+public class ZombieCacheService<K, V>
+    implements ICacheService<K, V>, IZombie
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( ZombieCacheService.class );
+
+    /**
+     * @param item
+     */
+    public void put( ICacheElement<K, V> item )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Zombie put for item " + item );
+        }
+        // zombies have no inner life
+    }
+
+    /**
+     * Does nothing.
+     * <p>
+     * @param item
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+    {
+        // zombies have no inner life
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     * @return null. zombies have no internal data
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+    {
+        return null;
+    }
+
+    /**
+     * Returns an empty map. Zombies have no internal data.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return Collections.EMPTY_MAP
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+    {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Returns an empty map. Zombies have no internal data.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return Collections.EMPTY_MAP
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+    {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * Logs the get to debug, but always balks.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param container
+     * @return null always
+     */
+    public Serializable get( String cacheName, K key, boolean container )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Zombie get for key [" + key + "] cacheName [" + cacheName + "] container [" + container + "]" );
+        }
+        // zombies have no inner life
+        return null;
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     */
+    @Override
+    public void remove( String cacheName, K key )
+    {
+        // zombies have no inner life
+    }
+
+    /**
+     * @param cacheName
+     */
+    @Override
+    public void removeAll( String cacheName )
+    {
+        // zombies have no inner life
+    }
+
+    /**
+     * @param cacheName
+     */
+    @Override
+    public void dispose( String cacheName )
+    {
+        // zombies have no inner life
+    }
+
+    /**
+     * Frees all caches.
+     */
+    @Override
+    public void release()
+    {
+        // zombies have no inner life
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocal.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocal.java
new file mode 100644
index 0000000..857fb08
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocal.java
@@ -0,0 +1,313 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.utils.struct.BoundedQueue;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Zombie adapter for the non local cache services. It just balks if there is no queue configured.
+ * <p>
+ * If a queue is configured, then events will be added to the queue. The idea is that when proper
+ * operation is restored, the non local cache will walk the queue. The queue must be bounded so it
+ * does not eat memory.
+ * <p>
+ * This originated in the remote cache.
+ */
+public class ZombieCacheServiceNonLocal<K, V>
+    extends ZombieCacheService<K, V>
+    implements ICacheServiceNonLocal<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( ZombieCacheServiceNonLocal.class );
+
+    /** How big can the queue grow. */
+    private int maxQueueSize = 0;
+
+    /** The queue */
+    private final BoundedQueue<ZombieEvent> queue;
+
+    /**
+     * Default.
+     */
+    public ZombieCacheServiceNonLocal()
+    {
+        queue = new BoundedQueue<ZombieEvent>( 0 );
+    }
+
+    /**
+     * Sets the maximum number of items that will be allowed on the queue.
+     * <p>
+     * @param maxQueueSize
+     */
+    public ZombieCacheServiceNonLocal( int maxQueueSize )
+    {
+        this.maxQueueSize = maxQueueSize;
+        queue = new BoundedQueue<ZombieEvent>( maxQueueSize );
+    }
+
+    /**
+     * Gets the number of items on the queue.
+     * <p>
+     * @return size of the queue.
+     */
+    public int getQueueSize()
+    {
+        return queue.size();
+    }
+
+    /**
+     * Adds an update event to the queue if the maxSize is greater than 0;
+     * <p>
+     * @param item ICacheElement
+     * @param listenerId - identifies the caller.
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long listenerId )
+    {
+        if ( maxQueueSize > 0 )
+        {
+            PutEvent<K, V> event = new PutEvent<K, V>( item, listenerId );
+            queue.add( event );
+        }
+        // Zombies have no inner life
+    }
+
+    /**
+     * Adds a removeAll event to the queue if the maxSize is greater than 0;
+     * <p>
+     * @param cacheName - region name
+     * @param key - item key
+     * @param listenerId - identifies the caller.
+     */
+    @Override
+    public void remove( String cacheName, K key, long listenerId )
+    {
+        if ( maxQueueSize > 0 )
+        {
+            RemoveEvent<K> event = new RemoveEvent<K>( cacheName, key, listenerId );
+            queue.add( event );
+        }
+        // Zombies have no inner life
+    }
+
+    /**
+     * Adds a removeAll event to the queue if the maxSize is greater than 0;
+     * <p>
+     * @param cacheName - name of the region
+     * @param listenerId - identifies the caller.
+     */
+    @Override
+    public void removeAll( String cacheName, long listenerId )
+    {
+        if ( maxQueueSize > 0 )
+        {
+            RemoveAllEvent event = new RemoveAllEvent( cacheName, listenerId );
+            queue.add( event );
+        }
+        // Zombies have no inner life
+    }
+
+    /**
+     * Does nothing. Gets are synchronous and cannot be added to a queue.
+     * <p>
+     * @param cacheName - region name
+     * @param key - item key
+     * @param requesterId - identifies the caller.
+     * @return null
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException
+    {
+        // Zombies have no inner life
+        return null;
+    }
+
+    /**
+     * Does nothing.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return empty map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        return Collections.emptyMap();
+    }
+
+    /**
+     * @param cacheName - region name
+     * @param keys - item key
+     * @param requesterId - identity of the caller
+     * @return an empty map. zombies have no internal data
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+    {
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * Does nothing.
+     * <p>
+     * @param cacheName - region name
+     * @return empty set
+     */
+    @Override
+    public Set<K> getKeySet( String cacheName )
+    {
+        return Collections.emptySet();
+    }
+
+    /**
+     * Walk the queue, calling the service for each queue operation.
+     * <p>
+     * @param service
+     * @throws Exception
+     */
+    public synchronized void propagateEvents( ICacheServiceNonLocal<K, V> service )
+        throws Exception
+    {
+        int cnt = 0;
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Propagating events to the new ICacheServiceNonLocal." );
+        }
+        ElapsedTimer timer = new ElapsedTimer();
+        while ( !queue.isEmpty() )
+        {
+            cnt++;
+
+            // for each item, call the appropriate service method
+            ZombieEvent event = queue.take();
+
+            if ( event instanceof PutEvent )
+            {
+                @SuppressWarnings("unchecked") // Type checked by instanceof
+                PutEvent<K, V> putEvent = (PutEvent<K, V>) event;
+                service.update( putEvent.element, event.requesterId );
+            }
+            else if ( event instanceof RemoveEvent )
+            {
+                @SuppressWarnings("unchecked") // Type checked by instanceof
+                RemoveEvent<K> removeEvent = (RemoveEvent<K>) event;
+                service.remove( event.cacheName, removeEvent.key, event.requesterId );
+            }
+            else if ( event instanceof RemoveAllEvent )
+            {
+                service.removeAll( event.cacheName, event.requesterId );
+            }
+        }
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Propagated " + cnt + " events to the new ICacheServiceNonLocal in "
+                + timer.getElapsedTimeString() );
+        }
+    }
+
+    /**
+     * Base of the other events.
+     */
+    protected static abstract class ZombieEvent
+    {
+        /** The name of the region. */
+        String cacheName;
+
+        /** The id of the requester */
+        long requesterId;
+    }
+
+    /**
+     * A basic put event.
+     */
+    private static class PutEvent<K, V>
+        extends ZombieEvent
+    {
+        /** The element to put */
+        ICacheElement<K, V> element;
+
+        /**
+         * Set the element
+         * @param element
+         * @param requesterId
+         */
+        public PutEvent( ICacheElement<K, V> element, long requesterId )
+        {
+            this.requesterId = requesterId;
+            this.element = element;
+        }
+    }
+
+    /**
+     * A basic Remove event.
+     */
+    private static class RemoveEvent<K>
+        extends ZombieEvent
+    {
+        /** The key to remove */
+        K key;
+
+        /**
+         * Set the element
+         * @param cacheName
+         * @param key
+         * @param requesterId
+         */
+        public RemoveEvent( String cacheName, K key, long requesterId )
+        {
+            this.cacheName = cacheName;
+            this.requesterId = requesterId;
+            this.key = key;
+        }
+    }
+
+    /**
+     * A basic RemoveAll event.
+     */
+    private static class RemoveAllEvent
+        extends ZombieEvent
+    {
+        /**
+         * @param cacheName
+         * @param requesterId
+         */
+        public RemoveAllEvent( String cacheName, long requesterId )
+        {
+            this.cacheName = cacheName;
+            this.requesterId = requesterId;
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheWatch.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheWatch.java
new file mode 100644
index 0000000..8a17e57
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/ZombieCacheWatch.java
@@ -0,0 +1,73 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+import org.apache.commons.jcs.engine.behavior.ICacheObserver;
+import org.apache.commons.jcs.engine.behavior.IZombie;
+
+/**
+ * Zombie Observer.
+ */
+public class ZombieCacheWatch
+    implements ICacheObserver, IZombie
+{
+    /**
+     * Adds a feature to the CacheListener attribute of the ZombieCacheWatch object
+     * <p>
+     * @param cacheName The feature to be added to the CacheListener attribute
+     * @param obj The feature to be added to the CacheListener attribute
+     */
+    @Override
+    public <K, V> void addCacheListener( String cacheName, ICacheListener<K, V> obj )
+    {
+        // empty
+    }
+
+    /**
+     * Adds a feature to the CacheListener attribute of the ZombieCacheWatch object
+     * <p>
+     * @param obj The feature to be added to the CacheListener attribute
+     */
+    @Override
+    public <K, V> void addCacheListener( ICacheListener<K, V> obj )
+    {
+        // empty
+    }
+
+    /**
+     * @param cacheName
+     * @param obj
+     */
+    @Override
+    public <K, V> void removeCacheListener( String cacheName, ICacheListener<K, V> obj )
+    {
+        // empty
+    }
+
+    /**
+     * @param obj
+     */
+    @Override
+    public <K, V> void removeCacheListener( ICacheListener<K, V> obj )
+    {
+        // empty
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICache.java
new file mode 100644
index 0000000..3398336
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICache.java
@@ -0,0 +1,141 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is the top level interface for all cache like structures. It defines the methods used
+ * internally by JCS to access, modify, and instrument such structures.
+ * <p>
+ * This allows for a suite of reusable components for accessing such structures, for example
+ * asynchronous access via an event queue.
+ */
+public interface ICache<K, V>
+    extends ICacheType
+{
+    /**
+     * Puts an item to the cache.
+     * <p>
+     * @param element
+     * @throws IOException
+     */
+    void update( ICacheElement<K, V> element )
+        throws IOException;
+
+    /**
+     * Gets an item from the cache.
+     * <p>
+     * @param key
+     * @return a cache element, or null if there is no data in cache for this key
+     * @throws IOException
+     */
+    ICacheElement<K, V> get( K key )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no data in cache for any of these keys
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+        throws IOException;
+
+    /**
+     * Gets items from the cache matching the given pattern.  Items from memory will replace those from remote sources.
+     * <p>
+     * This only works with string keys.  It's too expensive to do a toString on every key.
+     * <p>
+     * Auxiliaries will do their best to handle simple expressions.  For instance, the JDBC disk cache will convert * to % and . to _
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no data matching the pattern.
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMatching(String pattern)
+        throws IOException;
+
+    /**
+     * Removes an item from the cache.
+     * <p>
+     * @param key
+     * @return false if there was an error in removal
+     * @throws IOException
+     */
+    boolean remove( K key )
+        throws IOException;
+
+    /**
+     * Removes all cached items from the cache.
+     * <p>
+     * @throws IOException
+     */
+    void removeAll()
+        throws IOException;
+
+    /**
+     * Prepares for shutdown.
+     * @throws IOException
+     */
+    void dispose()
+        throws IOException;
+
+    /**
+     * Returns the current cache size in number of elements.
+     * <p>
+     * @return number of elements
+     */
+    int getSize();
+
+    /**
+     * Returns the cache status.
+     * <p>
+     * @return Alive or Error
+     */
+    CacheStatus getStatus();
+
+    /**
+     * Returns the cache stats.
+     * <p>
+     * @return String of important historical information.
+     */
+    String getStats();
+
+    /**
+     * Returns the cache name.
+     * <p>
+     * @return usually the region name.
+     */
+    String getCacheName();
+
+    /**
+     * Sets the key matcher used by get matching.
+     * <p>
+     * @param keyMatcher
+     */
+    void setKeyMatcher( IKeyMatcher<K> keyMatcher );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElement.java
new file mode 100644
index 0000000..3ce48b0
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElement.java
@@ -0,0 +1,74 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Every item is the cache is wrapped in an ICacheElement. This contains
+ * information about the element: the region name, the key, the value, and the
+ * element attributes.
+ * <p>
+ * The element attributes have lots of useful information about each element,
+ * such as when they were created, how long they have to live, and if they are
+ * allowed to be spooled, etc.
+ *
+ */
+public interface ICacheElement<K, V>
+    extends Serializable
+{
+
+    /**
+     * Gets the cacheName attribute of the ICacheElement<K, V> object. The cacheName
+     * is also known as the region name.
+     *
+     * @return The cacheName value
+     */
+    String getCacheName();
+
+    /**
+     * Gets the key attribute of the ICacheElement<K, V> object
+     *
+     * @return The key value
+     */
+    K getKey();
+
+    /**
+     * Gets the val attribute of the ICacheElement<K, V> object
+     *
+     * @return The val value
+     */
+    V getVal();
+
+    /**
+     * Gets the attributes attribute of the ICacheElement<K, V> object
+     *
+     * @return The attributes value
+     */
+    IElementAttributes getElementAttributes();
+
+    /**
+     * Sets the attributes attribute of the ICacheElement<K, V> object
+     *
+     * @param attr
+     *            The new attributes value
+     */
+    void setElementAttributes( IElementAttributes attr );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElementSerialized.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElementSerialized.java
new file mode 100644
index 0000000..d4da08d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheElementSerialized.java
@@ -0,0 +1,41 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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 interface defines the behavior of the serialized element wrapper.
+ * <p>
+ * The value is stored as a byte array. This should allow for a variety of serialization mechanisms.
+ * <p>
+ * This currently extends ICacheElement<K, V> for backward compatibility.
+ *<p>
+ * @author Aaron Smuts
+ */
+public interface ICacheElementSerialized<K, V>
+    extends ICacheElement<K, V>
+{
+    /**
+     * Gets the value attribute of the ICacheElementSerialized object. This is the value the client
+     * cached serialized by some mechanism.
+     *<p>
+     * @return The serialized value
+     */
+    byte[] getSerializedValue();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheEventQueue.java
new file mode 100644
index 0000000..618bdc1
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheEventQueue.java
@@ -0,0 +1,147 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+
+/**
+ * Interface for a cache event queue. An event queue is used to propagate
+ * ordered cache events to one and only one target listener.
+ */
+public interface ICacheEventQueue<K, V>
+{
+    enum QueueType
+    {
+        /** Does not use a thread pool. */
+        SINGLE,
+
+        /** Uses a thread pool. */
+        POOLED
+    }
+
+    /**
+     * Initializes the queue.
+     * <,p>
+     * @param listener
+     * @param listenerId
+     * @param cacheName
+     * @param maxFailure
+     * @param waitBeforeRetry
+     * @param threadPoolName
+     */
+    void initialize( ICacheListener<K, V> listener, long listenerId, String cacheName, int maxFailure,
+                            int waitBeforeRetry, String threadPoolName );
+
+    /**
+     * Return the type of event queue we are using, either single or pooled.
+     * <p>
+     * @return the queue type: single or pooled
+     */
+    QueueType getQueueType();
+
+    /**
+     * Adds a feature to the PutEvent attribute of the ICacheEventQueue object
+     * <p>
+     * @param ce
+     *            The feature to be added to the PutEvent attribute
+     * @throws IOException
+     */
+    void addPutEvent( ICacheElement<K, V> ce )
+        throws IOException;
+
+    /**
+     * Adds a feature to the RemoveEvent attribute of the ICacheEventQueue
+     * object
+     * <p>
+     * @param key
+     *            The feature to be added to the RemoveEvent attribute
+     * @throws IOException
+     */
+    void addRemoveEvent( K key )
+        throws IOException;
+
+    /**
+     * Adds a feature to the RemoveAllEvent attribute of the ICacheEventQueue
+     * object
+     * <p>
+     * @throws IOException
+     */
+    void addRemoveAllEvent()
+        throws IOException;
+
+    /**
+     * Adds a feature to the DisposeEvent attribute of the ICacheEventQueue
+     * object
+     * <p>
+     * @throws IOException
+     */
+    void addDisposeEvent()
+        throws IOException;
+
+    /**
+     * Gets the listenerId attribute of the ICacheEventQueue object
+     *
+     * @return The listenerId value
+     */
+    long getListenerId();
+
+    /** Description of the Method */
+    void destroy();
+
+    /**
+     * Gets the alive attribute of the ICacheEventQueue object. Alive just
+     * indicates that there are active threads. This is less important that if
+     * the queue is working.
+     * <p>
+     * @return The alive value
+     */
+    boolean isAlive();
+
+    /**
+     * A Queue is working unless it has reached its max failure count.
+     * <p>
+     * @return boolean
+     */
+    boolean isWorking();
+
+    /**
+     * Returns the number of elements in the queue.  If the queue cannot
+     * determine the size accurately it will return 1.
+     * <p>
+     * @return number of items in the queue.
+     */
+    int size();
+
+    /**
+     * Are there elements in the queue.
+     * <p>
+     * @return true if there are stil elements.
+     */
+    boolean isEmpty();
+
+    /**
+     * Returns the historical and statistical data for an event queue cache.
+     * <p>
+     * @return IStats
+     */
+    IStats getStatistics();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheListener.java
new file mode 100644
index 0000000..5e15913
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheListener.java
@@ -0,0 +1,86 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Used to receive a cache event notification.
+ * <p>
+ * Note: objects which implement this interface are local listeners to cache changes, whereas
+ * objects which implement IRmiCacheListener are remote listeners to cache changes.
+ */
+public interface ICacheListener<K, V>
+{
+    /**
+     * Notifies the subscribers for a cache entry update.
+     * <p>
+     * @param item
+     * @throws IOException
+     */
+    void handlePut( ICacheElement<K, V> item )
+        throws IOException;
+
+    /**
+     * Notifies the subscribers for a cache entry removal.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    void handleRemove( String cacheName, K key )
+        throws IOException;
+
+    /**
+     * Notifies the subscribers for a cache remove-all.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    void handleRemoveAll( String cacheName )
+        throws IOException;
+
+    /**
+     * Notifies the subscribers for freeing up the named cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    void handleDispose( String cacheName )
+        throws IOException;
+
+    /**
+     * sets unique identifier of listener home
+     * <p>
+     * @param id The new listenerId value
+     * @throws IOException
+     */
+    void setListenerId( long id )
+        throws IOException;
+
+    /**
+     * Gets the listenerId attribute of the ICacheListener object
+     * <p>
+     * @return The listenerId value
+     * @throws IOException
+     */
+    long getListenerId()
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheManager.java
new file mode 100644
index 0000000..59bd073
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheManager.java
@@ -0,0 +1,42 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+
+/**
+ * Interface implemented by a specific cache.
+ *
+ */
+public interface ICacheManager
+    extends ICacheType
+{
+
+    /**
+     * methods to get a cache region from a manager
+     * @param cacheName
+     *
+     * @return The cache value
+     */
+    <K extends Serializable, V extends Serializable> ICache<K, V> getCache( String cacheName );
+
+}
+// end interface
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheObserver.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheObserver.java
new file mode 100644
index 0000000..11c9a05
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheObserver.java
@@ -0,0 +1,78 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Used to register interest in receiving cache changes. <br>
+ * <br>
+ * Note: server which implements this interface provides a local cache event
+ * notification service, whereas server which implements IRmiCacheWatch provides
+ * a remote cache event notification service.
+ *
+ */
+public interface ICacheObserver
+{
+    /**
+     * Subscribes to the specified cache.
+     *
+     * @param cacheName
+     *            the specified cache.
+     * @param obj
+     *            object to notify for cache changes.
+     * @throws IOException
+     */
+    <K, V> void addCacheListener( String cacheName, ICacheListener<K, V> obj )
+        throws IOException;
+
+    //, CacheNotFoundException;
+
+    /**
+     * Subscribes to all caches.
+     *
+     * @param obj
+     *            object to notify for all cache changes.
+     * @throws IOException
+     */
+    <K, V> void addCacheListener( ICacheListener<K, V> obj )
+        throws IOException;
+
+    /**
+     * Unsubscribes from the specified cache.
+     * @param cacheName
+     *
+     * @param obj
+     *            existing subscriber.
+     * @throws IOException
+     */
+    <K, V> void removeCacheListener( String cacheName, ICacheListener<K, V> obj )
+        throws IOException;
+
+    /**
+     * Unsubscribes from all caches.
+     *
+     * @param obj
+     *            existing subscriber.
+     * @throws IOException
+     */
+    <K, V> void removeCacheListener( ICacheListener<K, V> obj )
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheRestore.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheRestore.java
new file mode 100644
index 0000000..2e7efa3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheRestore.java
@@ -0,0 +1,37 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+/**
+ * TODO: Description of the Interface
+ *
+ */
+public interface ICacheRestore
+{
+    /**
+     * Tries to fix the cache, returns true if successful.
+     *
+     * @return true if fixed
+     */
+    boolean canFix();
+
+    /** Fix the cache */
+    void fix();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheService.java
new file mode 100644
index 0000000..f1f59c8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheService.java
@@ -0,0 +1,117 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.ObjectExistsException;
+import org.apache.commons.jcs.access.exception.ObjectNotFoundException;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used to retrieve and update the cache.
+ * <p>
+ * Note: server which implements this interface provides a local cache service, whereas server which
+ * implements IRmiCacheService provides a remote cache service.
+ */
+public interface ICacheService<K, V>
+{
+    /**
+     * Puts a cache item to the cache.
+     * <p>
+     * @param item
+     * @throws ObjectExistsException
+     * @throws IOException
+     */
+    void update( ICacheElement<K, V> item )
+        throws ObjectExistsException, IOException;
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @return the ICacheElement<K, V> or null if not found
+     * @throws ObjectNotFoundException
+     * @throws IOException
+     */
+    ICacheElement<K, V> get( String cacheName, K key )
+        throws ObjectNotFoundException, IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws ObjectNotFoundException
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+        throws ObjectNotFoundException, IOException;
+
+    /**
+     * Gets multiple items from the cache matching the pattern.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException;
+
+    /**
+     * Removes the given key from the specified cache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    void remove( String cacheName, K key )
+        throws IOException;
+
+    /**
+     * Remove all keys from the specified cache.
+     * @param cacheName
+     * @throws IOException
+     */
+    void removeAll( String cacheName )
+        throws IOException;
+
+    /**
+     * Frees the specified cache.
+     * <p>
+     * @param cacheName
+     * @throws IOException
+     */
+    void dispose( String cacheName )
+        throws IOException;
+
+    /**
+     * Frees all caches.
+     * @throws IOException
+     */
+    void release()
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceAdmin.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceAdmin.java
new file mode 100644
index 0000000..856ff51
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceAdmin.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Description of the Interface
+ *
+ */
+public interface ICacheServiceAdmin
+{
+
+    /**
+     * Gets the stats attribute of the ICacheServiceAdmin object
+     *
+     * @return The stats value
+     * @throws IOException
+     */
+    String getStats()
+        throws IOException;
+
+    /** Description of the Method
+     * @throws IOException*/
+    void shutdown()
+        throws IOException;
+
+    /** Description of the Method
+     * @param host
+     * @param port
+     * @throws IOException*/
+    void shutdown( String host, int port )
+        throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceNonLocal.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceNonLocal.java
new file mode 100644
index 0000000..a150b3b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheServiceNonLocal.java
@@ -0,0 +1,118 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.rmi.Remote;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used to retrieve and update non local caches, such as the remote and lateral caches. Unlike
+ * ICacheService, the methods here have a requester id. This allows us to avoid propagating events
+ * to ourself.
+ * <p>
+ * TODO consider not extending ICacheService
+ */
+public interface ICacheServiceNonLocal<K, V>
+    extends Remote, ICacheService<K, V>
+{
+    /**
+     * Puts a cache item to the cache.
+     * <p>
+     * @param item
+     * @param requesterId
+     * @throws IOException
+     */
+    void update( ICacheElement<K, V> item, long requesterId )
+        throws IOException;
+
+    /**
+     * Removes the given key from the specified cache.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @throws IOException
+     */
+    void remove( String cacheName, K key, long requesterId )
+        throws IOException;
+
+    /**
+     * Remove all keys from the specified cache.
+     * <p>
+     * @param cacheName
+     * @param requesterId
+     * @throws IOException
+     */
+    void removeAll( String cacheName, long requesterId )
+        throws IOException;
+
+    /**
+     * Returns a cache bean from the specified cache; or null if the key does not exist.
+     * <p>
+     * Adding the requester id, allows the cache to determine the source of the get.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return ICacheElement
+     * @throws IOException
+     */
+    ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache matching the pattern.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache matching the pattern.
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException;
+
+    /**
+     * Get a set of the keys for all elements in the cache.
+     * <p>
+     * @param cacheName the name of the cache
+     * @return a set of the key type
+     * TODO This should probably be done in chunks with a range passed in. This
+     *       will be a problem if someone puts a 1,000,000 or so items in a
+     *       region.
+     */
+    Set<K> getKeySet( String cacheName ) throws IOException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheType.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheType.java
new file mode 100644
index 0000000..3828f3a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICacheType.java
@@ -0,0 +1,49 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+/**
+ * Interface implemented by a specific cache.
+ *
+ */
+public interface ICacheType
+{
+    enum CacheType {
+        /** Composite/ memory cache type, central hub. */
+        CACHE_HUB,
+
+        /** Disk cache type. */
+        DISK_CACHE,
+
+        /** Lateral cache type. */
+        LATERAL_CACHE,
+
+        /** Remote cache type. */
+        REMOTE_CACHE
+    }
+
+    /**
+     * Returns the cache type.
+     * <p>
+     * @return The cacheType value
+     */
+    CacheType getCacheType();
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheAttributes.java
new file mode 100644
index 0000000..49e45cc
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheAttributes.java
@@ -0,0 +1,246 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * This defines the minimal behavior for the Cache Configuration settings.
+ */
+public interface ICompositeCacheAttributes
+    extends Serializable
+{
+    enum DiskUsagePattern
+    {
+        /** Items will only go to disk when the memory limit is reached. This is the default. */
+        SWAP,
+
+        /**
+         * Items will go to disk on a normal put. If The disk usage pattern is UPDATE, the swap will be
+         * disabled.
+         */
+        UPDATE
+    }
+
+    /**
+     * SetMaxObjects is used to set the attribute to determine the maximum
+     * number of objects allowed in the memory cache. If the max number of
+     * objects or the cache size is set, the default for the one not set is
+     * ignored. If both are set, both are used to determine the capacity of the
+     * cache, i.e., object will be removed from the cache if either limit is
+     * reached. TODO: move to MemoryCache config file.
+     * <p>
+     * @param size
+     *            The new maxObjects value
+     */
+    void setMaxObjects( int size );
+
+    /**
+     * Gets the maxObjects attribute of the ICompositeCacheAttributes object
+     * <p>
+     * @return The maxObjects value
+     */
+    int getMaxObjects();
+
+    /**
+     * Sets the useDisk attribute of the ICompositeCacheAttributes object
+     * <p>
+     * @param useDisk
+     *            The new useDisk value
+     */
+    void setUseDisk( boolean useDisk );
+
+    /**
+     * Gets the useDisk attribute of the ICompositeCacheAttributes object
+     * <p>
+     * @return The useDisk value
+     */
+    boolean isUseDisk();
+
+    /**
+     * set whether the cache should use a lateral cache
+     * <p>
+     * @param d
+     *            The new useLateral value
+     */
+    void setUseLateral( boolean d );
+
+    /**
+     * Gets the useLateral attribute of the ICompositeCacheAttributes object
+     * <p>
+     * @return The useLateral value
+     */
+    boolean isUseLateral();
+
+    /**
+     * Sets whether the cache is remote enabled
+     * <p>
+     * @param isRemote
+     *            The new useRemote value
+     */
+    void setUseRemote( boolean isRemote );
+
+    /**
+     * returns whether the cache is remote enabled
+     * <p>
+     * @return The useRemote value
+     */
+    boolean isUseRemote();
+
+    /**
+     * Sets the name of the cache, referenced by the appropriate manager.
+     * <p>
+     * @param s
+     *            The new cacheName value
+     */
+    void setCacheName( String s );
+
+    /**
+     * Gets the cacheName attribute of the ICompositeCacheAttributes object
+     * <p>
+     * @return The cacheName value
+     */
+    String getCacheName();
+
+    /**
+     * Sets the name of the MemoryCache, referenced by the appropriate manager.
+     * TODO: create a separate memory cache attribute class.
+     * <p>
+     * @param s
+     *            The new memoryCacheName value
+     */
+    void setMemoryCacheName( String s );
+
+    /**
+     * Gets the memoryCacheName attribute of the ICompositeCacheAttributes
+     * object
+     * <p>
+     * @return The memoryCacheName value
+     */
+    String getMemoryCacheName();
+
+    /**
+     * Whether the memory cache should perform background memory shrinkage.
+     * <p>
+     * @param useShrinker
+     *            The new UseMemoryShrinker value
+     */
+    void setUseMemoryShrinker( boolean useShrinker );
+
+    /**
+     * Whether the memory cache should perform background memory shrinkage.
+     * <p>
+     * @return The UseMemoryShrinker value
+     */
+    boolean isUseMemoryShrinker();
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space.
+     * <p>
+     * @param seconds
+     *            The new MaxMemoryIdleTimeSeconds value
+     */
+    void setMaxMemoryIdleTimeSeconds( long seconds );
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space.
+     * <p>
+     * @return The MaxMemoryIdleTimeSeconds value
+     */
+    long getMaxMemoryIdleTimeSeconds();
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space. This sets the shrinker interval.
+     * <p>
+     * @param seconds
+     *            The new ShrinkerIntervalSeconds value
+     */
+    void setShrinkerIntervalSeconds( long seconds );
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space. This gets the shrinker interval.
+     * <p>
+     * @return The ShrinkerIntervalSeconds value
+     */
+    long getShrinkerIntervalSeconds();
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space. This sets the maximum number of items to spool per run.
+     * <p>
+     * @param maxSpoolPerRun
+     *            The new maxSpoolPerRun value
+     */
+    void setMaxSpoolPerRun( int maxSpoolPerRun );
+
+    /**
+     * If UseMemoryShrinker is true the memory cache should auto-expire elements
+     * to reclaim space. This gets the maximum number of items to spool per run.
+     * <p>
+     * @return The maxSpoolPerRun value
+     */
+    int getMaxSpoolPerRun();
+
+    /**
+     * Clones the attributes.
+     * <p>
+     * @return a new object with the same settings.
+     */
+    ICompositeCacheAttributes copy();
+
+    /**
+     * By default this is SWAP_ONLY.
+     * <p>
+     * @param diskUsagePattern The diskUsagePattern to set.
+     */
+    void setDiskUsagePattern( DiskUsagePattern diskUsagePattern );
+
+    /**
+     * Translates the name to the disk usage pattern short value.
+     * <p>
+     * The allowed values are SWAP and UPDATE.
+     * <p>
+     * @param diskUsagePatternName The diskUsagePattern to set.
+     */
+    void setDiskUsagePatternName( String diskUsagePatternName );
+
+    /**
+     * @return Returns the diskUsagePattern.
+     */
+    DiskUsagePattern getDiskUsagePattern();
+
+    /**
+     * Number to send to disk at at time when memory is full.
+     * <p>
+     * @return int
+     */
+    int getSpoolChunkSize();
+
+    /**
+     * Number to send to disk at a time.
+     * <p>
+     * @param spoolChunkSize
+     */
+    void setSpoolChunkSize( int spoolChunkSize );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheManager.java
new file mode 100644
index 0000000..d7a500b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/ICompositeCacheManager.java
@@ -0,0 +1,54 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.CompositeCache;
+
+import java.util.Properties;
+
+/**
+ * I need the interface so I can plug in mock managers for testing.
+ *
+ * @author Aaron Smuts
+ */
+public interface ICompositeCacheManager extends IShutdownObservable
+{
+    /**
+     * Gets the cache attribute of the CacheHub object
+     *
+     * @param cacheName
+     * @return CompositeCache
+     */
+    <K, V> CompositeCache<K, V>  getCache( String cacheName );
+
+    /**
+     * This is exposed so other manager can get access to the props.
+     * <p>
+     * @return the configurationProperties
+     */
+    Properties getConfigurationProperties();
+
+    /**
+     * Gets stats for debugging.
+     * <p>
+     * @return String
+     */
+    String getStats();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementAttributes.java
new file mode 100644
index 0000000..1786b1d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementAttributes.java
@@ -0,0 +1,203 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Interface for cache element attributes classes. Every item is the cache is associated with an
+ * element attributes object. It is used to track the life of the object as well as to restrict its
+ * behavior. By default, elements get a clone of the region's attributes.
+ */
+public interface IElementAttributes
+{
+    /**
+     * Sets the maxLife attribute of the IAttributes object.
+     * <p>
+     * @param mls The new MaxLifeSeconds value
+     */
+    void setMaxLife(long mls);
+
+    /**
+     * Sets the maxLife attribute of the IAttributes object. How many seconds it can live after
+     * creation.
+     * <p>
+     * If this is exceeded the element will not be returned, instead it will be removed. It will be
+     * removed on retrieval, or removed actively if the memory shrinker is turned on.
+     * @return The MaxLifeSeconds value
+     */
+    long getMaxLife();
+
+    /**
+     * Sets the idleTime attribute of the IAttributes object. This is the maximum time the item can
+     * be idle in the cache, that is not accessed.
+     * <p>
+     * If this is exceeded the element will not be returned, instead it will be removed. It will be
+     * removed on retrieval, or removed actively if the memory shrinker is turned on.
+     * @param idle The new idleTime value
+     */
+    void setIdleTime( long idle );
+
+    /**
+     * Size in bytes. This is not used except in the admin pages. It will be -1 by default.
+     * <p>
+     * @param size The new size value
+     */
+    void setSize( int size );
+
+    /**
+     * Gets the size attribute of the IAttributes object
+     * <p>
+     * @return The size value
+     */
+    int getSize();
+
+    /**
+     * Gets the createTime attribute of the IAttributes object.
+     * <p>
+     * This should be the current time in milliseconds returned by the sysutem call when the element
+     * is put in the cache.
+     * <p>
+     * Putting an item in the cache overrides any existing items.
+     * @return The createTime value
+     */
+    long getCreateTime();
+
+    /**
+     * Gets the LastAccess attribute of the IAttributes object.
+     * <p>
+     * @return The LastAccess value.
+     */
+    long getLastAccessTime();
+
+    /**
+     * Sets the LastAccessTime as now of the IElementAttributes object
+     */
+    void setLastAccessTimeNow();
+
+    /**
+     * Gets the idleTime attribute of the IAttributes object
+     * @return The idleTime value
+     */
+    long getIdleTime();
+
+    /**
+     * Gets the time left to live of the IAttributes object.
+     * <p>
+     * This is the (max life + create time) - current time.
+     * @return The TimeToLiveSeconds value
+     */
+    long getTimeToLiveSeconds();
+
+    /**
+     * Returns a copy of the object.
+     * @return IElementAttributes
+     */
+    IElementAttributes copy();
+
+    /**
+     * Can this item be spooled to disk
+     * <p>
+     * By default this is true.
+     * @return The spoolable value
+     */
+    boolean getIsSpool();
+
+    /**
+     * Sets the isSpool attribute of the IElementAttributes object
+     * <p>
+     * By default this is true.
+     * @param val The new isSpool value
+     */
+    void setIsSpool( boolean val );
+
+    /**
+     * Is this item laterally distributable. Can it be sent to auxiliaries of type lateral.
+     * <p>
+     * By default this is true.
+     * @return The isLateral value
+     */
+    boolean getIsLateral();
+
+    /**
+     * Sets the isLateral attribute of the IElementAttributes object
+     * <p>
+     * By default this is true.
+     * @param val The new isLateral value
+     */
+    void setIsLateral( boolean val );
+
+    /**
+     * Can this item be sent to the remote cache.
+     * <p>
+     * By default this is true.
+     * @return The isRemote value
+     */
+    boolean getIsRemote();
+
+    /**
+     * Sets the isRemote attribute of the IElementAttributes object.
+     * <p>
+     * By default this is true.
+     * @param val The new isRemote value
+     */
+    void setIsRemote( boolean val );
+
+    /**
+     * This turns off expiration if it is true.
+     * @return The IsEternal value
+     */
+    boolean getIsEternal();
+
+    /**
+     * Sets the isEternal attribute of the IElementAttributes object
+     * @param val The new isEternal value
+     */
+    void setIsEternal( boolean val );
+
+    /**
+     * Adds a ElementEventHandler. Handler's can be registered for multiple events. A registered
+     * handler will be called at every recognized event.
+     * @param eventHandler The feature to be added to the ElementEventHandler
+     */
+    void addElementEventHandler( IElementEventHandler eventHandler );
+
+    /**
+     * Gets the elementEventHandlers.
+     * <p>
+     * Event handlers are transient. The only events defined are in memory events. All handlers are
+     * lost if the item goes to disk.
+     * @return The elementEventHandlers value, null if there are none
+     */
+    ArrayList<IElementEventHandler> getElementEventHandlers();
+
+    /**
+     * Sets the eventHandlers of the IElementAttributes object
+     * @param eventHandlers value
+     */
+    void addElementEventHandlers( List<IElementEventHandler> eventHandlers );
+
+    long getTimeFactorForMilliseconds();
+
+    void setTimeFactorForMilliseconds(long factor);
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementSerializer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementSerializer.java
new file mode 100644
index 0000000..547cc1e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IElementSerializer.java
@@ -0,0 +1,50 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Defines the behavior for cache element serializers. This layer of abstraction allows us to plug
+ * in different serialization mechanisms, such as a compressing standard serializer.
+ * <p>
+ * @author Aaron Smuts
+ */
+public interface IElementSerializer
+{
+    /**
+     * Turns an object into a byte array.
+     * @param obj
+     * @return byte[]
+     * @throws IOException
+     */
+    <T> byte[] serialize( T obj )
+        throws IOException;
+
+    /**
+     * Turns a byte array into an object.
+     * @param bytes
+     * @return Object
+     * @throws IOException
+     * @throws ClassNotFoundException thrown if we don't know the object.
+     */
+    <T> T deSerialize( byte[] bytes, ClassLoader loader )
+        throws IOException, ClassNotFoundException;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IProvideScheduler.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IProvideScheduler.java
new file mode 100644
index 0000000..29ad9d2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IProvideScheduler.java
@@ -0,0 +1,38 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.util.concurrent.ScheduledExecutorService;
+
+
+/**
+ * Marker interface for providers of the central ScheduledExecutorService
+ * <p>
+ * @author Thomas Vandahl
+ *
+ */
+public interface IProvideScheduler
+{
+    /**
+     * Get an instance of a central ScheduledExecutorService
+     * @return the central scheduler
+     */
+    ScheduledExecutorService getScheduledExecutorService();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IRequireScheduler.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IRequireScheduler.java
new file mode 100644
index 0000000..09ea8a7
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IRequireScheduler.java
@@ -0,0 +1,39 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+import java.util.concurrent.ScheduledExecutorService;
+
+
+/**
+ * Marker interface to allow the injection of a central ScheduledExecutorService
+ * for all modules requiring scheduled background operations.
+ * <p>
+ * @author Thomas Vandahl
+ *
+ */
+public interface IRequireScheduler
+{
+    /**
+     * Inject an instance of a central ScheduledExecutorService
+     * @param scheduledExecutor
+     */
+    void setScheduledExecutorService( ScheduledExecutorService scheduledExecutor );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObservable.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObservable.java
new file mode 100644
index 0000000..c428037
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObservable.java
@@ -0,0 +1,55 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+/**
+ * ShutdownObservers can observe ShutdownObservable objects.
+ * The CacheManager is the primary observable that this is intended for.
+ * <p>
+ * Most shutdown operations will occur outside this framework for now.  The initial
+ * goal is to allow background threads that are not reachable through any reference
+ * that the cache manager maintains to be killed on shutdown.
+ * <p>
+ * Perhaps the composite cache itself should be the observable object.
+ * It doesn't make much of a difference.  There are some problems with
+ * region by region shutdown.  Some auxiliaries are local.  They will
+ * need to track when every region has shutdown before doing things like
+ * closing the socket with a lateral.
+ * <p>
+ * @author Aaron Smuts
+ *
+ */
+public interface IShutdownObservable
+{
+
+    /**
+     * Registers an observer with the observable object.
+     * @param observer
+     */
+    void registerShutdownObserver( IShutdownObserver observer );
+
+    /**
+     * Deregisters the observer with the observable.
+     *
+     * @param observer
+     */
+    void deregisterShutdownObserver( IShutdownObserver observer );
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObserver.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObserver.java
new file mode 100644
index 0000000..22b4052
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IShutdownObserver.java
@@ -0,0 +1,41 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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 interface is required of all shutdown observers.  These observers
+ * can observer ShutdownObservable objects.  The CacheManager is the primary
+ * observable that this is intended for.
+ * <p>
+ * Most shutdown operations will occur outside this framework for now.  The initial
+ * goal is to allow background threads that are not reachable through any reference
+ * that the cache manager maintains to be killed on shutdown.
+ *
+ * @author Aaron Smuts
+ *
+ */
+public interface IShutdownObserver
+{
+    /**
+     * Tells the observer that the observable has received a shutdown command.
+     *
+     */
+    void shutdown();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IZombie.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IZombie.java
new file mode 100644
index 0000000..2c726c6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/IZombie.java
@@ -0,0 +1,30 @@
+package org.apache.commons.jcs.engine.behavior;
+
+/*
+ * 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.
+ */
+
+/**
+ * Interface to mark an object as zombie for error recovery purposes.
+ *
+ */
+public interface IZombie
+{
+    // Zombies have no inner life.
+    // No qaulia found.
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/package.html
new file mode 100644
index 0000000..9cb3956
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/behavior/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+     Interfaces used by the core and the auxiliary caches.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCache.java
new file mode 100644
index 0000000..7a19089
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCache.java
@@ -0,0 +1,1848 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.ObjectNotFoundException;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes.DiskUsagePattern;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
+import org.apache.commons.jcs.engine.control.event.ElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.ElementEventType;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventQueue;
+import org.apache.commons.jcs.engine.control.group.GroupId;
+import org.apache.commons.jcs.engine.match.KeyMatcherPatternImpl;
+import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
+import org.apache.commons.jcs.engine.memory.behavior.IMemoryCache;
+import org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache;
+import org.apache.commons.jcs.engine.memory.shrinking.ShrinkerThread;
+import org.apache.commons.jcs.engine.stats.CacheStats;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This is the primary hub for a single cache/region. It controls the flow of items through the
+ * cache. The auxiliary and memory caches are plugged in here.
+ * <p>
+ * This is the core of a JCS region. Hence, this simple class is the core of JCS.
+ */
+public class CompositeCache<K, V>
+    implements ICache<K, V>, IRequireScheduler
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( CompositeCache.class );
+
+    /**
+     * EventQueue for handling element events. Lazy initialized. One for each region. To be more efficient, the manager
+     * should pass a shared queue in.
+     */
+    private IElementEventQueue elementEventQ;
+
+    /** Auxiliary caches. */
+    @SuppressWarnings("unchecked") // OK because this is an empty array
+    private AuxiliaryCache<K, V>[] auxCaches = new AuxiliaryCache[0];
+
+    /** is this alive? */
+    private boolean alive = true;
+
+    /** Region Elemental Attributes, default. */
+    private IElementAttributes attr;
+
+    /** Cache Attributes, for hub and memory auxiliary. */
+    private ICompositeCacheAttributes cacheAttr;
+
+    /** How many times update was called. */
+    private int updateCount;
+
+    /** How many times remove was called. */
+    private int removeCount;
+
+    /** Memory cache hit count */
+    private int hitCountRam;
+
+    /** Auxiliary cache hit count (number of times found in ANY auxiliary) */
+    private int hitCountAux;
+
+    /** Count of misses where element was not found. */
+    private int missCountNotFound = 0;
+
+    /** Count of misses where element was expired. */
+    private int missCountExpired = 0;
+
+    /**
+     * The cache hub can only have one memory cache. This could be made more flexible in the future,
+     * but they are tied closely together. More than one doesn't make much sense.
+     */
+    private IMemoryCache<K, V> memCache;
+
+    /** Key matcher used by the getMatching API */
+    private IKeyMatcher<K> keyMatcher = new KeyMatcherPatternImpl<K>();
+
+    /**
+     * Constructor for the Cache object
+     * <p>
+     * @param cattr The cache attribute
+     * @param attr The default element attributes
+     */
+    public CompositeCache( ICompositeCacheAttributes cattr, IElementAttributes attr )
+    {
+        this.attr = attr;
+        this.cacheAttr = cattr;
+
+        createMemoryCache( cattr );
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Constructed cache with name [" + cacheAttr.getCacheName() + "] and cache attributes " + cattr );
+        }
+    }
+
+    /**
+     * Injector for Element event queue
+     *
+     * @param queue
+     */
+    public void setElementEventQueue( IElementEventQueue queue )
+    {
+        this.elementEventQ = queue;
+    }
+
+    /**
+     * @see org.apache.commons.jcs.engine.behavior.IRequireScheduler#setScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
+     */
+    @Override
+    public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutor)
+    {
+        if ( cacheAttr.isUseMemoryShrinker() )
+        {
+            scheduledExecutor.scheduleAtFixedRate(
+                    new ShrinkerThread<K, V>(this), 0, cacheAttr.getShrinkerIntervalSeconds(),
+                    TimeUnit.SECONDS);
+        }
+    }
+
+    /**
+     * This sets the list of auxiliary caches for this region.
+     * <p>
+     * @param auxCaches
+     */
+    public void setAuxCaches( AuxiliaryCache<K, V>[] auxCaches )
+    {
+        this.auxCaches = auxCaches;
+    }
+
+    /**
+     * Get the list of auxiliary caches for this region.
+     * <p>
+     * @return an array of auxiliary caches, may be empty, never null
+     */
+    public AuxiliaryCache<K, V>[] getAuxCaches()
+    {
+        return this.auxCaches;
+    }
+
+    /**
+     * Standard update method.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        update( ce, false );
+    }
+
+    /**
+     * Standard update method.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    public void localUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        update( ce, true );
+    }
+
+    /**
+     * Put an item into the cache. If it is localOnly, then do no notify remote or lateral
+     * auxiliaries.
+     * <p>
+     * @param cacheElement the ICacheElement<K, V>
+     * @param localOnly Whether the operation should be restricted to local auxiliaries.
+     * @throws IOException
+     */
+    protected void update( ICacheElement<K, V> cacheElement, boolean localOnly )
+        throws IOException
+    {
+
+        if ( cacheElement.getKey() instanceof String
+            && cacheElement.getKey().toString().endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+        {
+            throw new IllegalArgumentException( "key must not end with " + CacheConstants.NAME_COMPONENT_DELIMITER
+                + " for a put operation" );
+        }
+        else if ( cacheElement.getKey() instanceof GroupId )
+        {
+            throw new IllegalArgumentException( "key cannot be a GroupId " + " for a put operation" );
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Updating memory cache " + cacheElement.getKey() );
+        }
+
+        synchronized ( this )
+        {
+            updateCount++;
+
+            memCache.update( cacheElement );
+
+            updateAuxiliaries( cacheElement, localOnly );
+
+            cacheElement.getElementAttributes().setLastAccessTimeNow();
+        }
+    }
+
+    /**
+     * This method is responsible for updating the auxiliaries if they are present. If it is local
+     * only, any lateral and remote auxiliaries will not be updated.
+     * <p>
+     * Before updating an auxiliary it checks to see if the element attributes permit the operation.
+     * <p>
+     * Disk auxiliaries are only updated if the disk cache is not merely used as a swap. If the disk
+     * cache is merely a swap, then items will only go to disk when they overflow from memory.
+     * <p>
+     * This is called by update( cacheElement, localOnly ) after it updates the memory cache.
+     * <p>
+     * This is protected to make it testable.
+     * <p>
+     * @param cacheElement
+     * @param localOnly
+     * @throws IOException
+     */
+    protected void updateAuxiliaries( ICacheElement<K, V> cacheElement, boolean localOnly )
+        throws IOException
+    {
+        // UPDATE AUXILLIARY CACHES
+        // There are 3 types of auxiliary caches: remote, lateral, and disk
+        // more can be added if future auxiliary caches don't fit the model
+        // You could run a database cache as either a remote or a local disk.
+        // The types would describe the purpose.
+
+        if ( log.isDebugEnabled() )
+        {
+            if ( auxCaches.length > 0 )
+            {
+                log.debug( "Updating auxiliary caches" );
+            }
+            else
+            {
+                log.debug( "No auxiliary cache to update" );
+            }
+        }
+
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            ICache<K, V> aux = auxCaches[i];
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Auxilliary cache type: " + aux.getCacheType() );
+            }
+
+            if ( aux == null )
+            {
+                continue;
+            }
+
+            // SEND TO REMOTE STORE
+            if ( aux.getCacheType() == CacheType.REMOTE_CACHE )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "ce.getElementAttributes().getIsRemote() = "
+                        + cacheElement.getElementAttributes().getIsRemote() );
+                }
+
+                if ( cacheElement.getElementAttributes().getIsRemote() && !localOnly )
+                {
+                    try
+                    {
+                        // need to make sure the group cache understands that
+                        // the key is a group attribute on update
+                        aux.update( cacheElement );
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Updated remote store for " + cacheElement.getKey() + cacheElement );
+                        }
+                    }
+                    catch ( IOException ex )
+                    {
+                        log.error( "Failure in updateExclude", ex );
+                    }
+                }
+                // SEND LATERALLY
+            }
+            else if ( aux.getCacheType() == CacheType.LATERAL_CACHE )
+            {
+                // lateral can't do the checking since it is dependent on the
+                // cache region restrictions
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "lateralcache in aux list: cattr " + cacheAttr.isUseLateral() );
+                }
+                if ( cacheAttr.isUseLateral() && cacheElement.getElementAttributes().getIsLateral() && !localOnly )
+                {
+                    // DISTRIBUTE LATERALLY
+                    // Currently always multicast even if the value is
+                    // unchanged, to cause the cache item to move to the front.
+                    aux.update( cacheElement );
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "updated lateral cache for " + cacheElement.getKey() );
+                    }
+                }
+            }
+            // update disk if the usage pattern permits
+            else if ( aux.getCacheType() == CacheType.DISK_CACHE )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "diskcache in aux list: cattr " + cacheAttr.isUseDisk() );
+                }
+                if ( cacheAttr.isUseDisk()
+                    && cacheAttr.getDiskUsagePattern() == DiskUsagePattern.UPDATE
+                    && cacheElement.getElementAttributes().getIsSpool() )
+                {
+                    aux.update( cacheElement );
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "updated disk cache for " + cacheElement.getKey() );
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Writes the specified element to any disk auxiliaries. Might want to rename this "overflow" in
+     * case the hub wants to do something else.
+     * <p>
+     * If JCS is not configured to use the disk as a swap, that is if the the
+     * CompositeCacheAttribute diskUsagePattern is not SWAP_ONLY, then the item will not be spooled.
+     * <p>
+     * @param ce The CacheElement
+     */
+    public void spoolToDisk( ICacheElement<K, V> ce )
+    {
+        // if the item is not spoolable, return
+        if ( !ce.getElementAttributes().getIsSpool() )
+        {
+            // there is an event defined for this.
+            handleElementEvent( ce, ElementEventType.SPOOLED_NOT_ALLOWED );
+            return;
+        }
+
+        boolean diskAvailable = false;
+
+        // SPOOL TO DISK.
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            ICache<K, V> aux = auxCaches[i];
+
+            if ( aux != null && aux.getCacheType() == CacheType.DISK_CACHE )
+            {
+                diskAvailable = true;
+
+                if ( cacheAttr.getDiskUsagePattern() == DiskUsagePattern.SWAP )
+                {
+                    // write the last items to disk.2
+                    try
+                    {
+                        handleElementEvent( ce, ElementEventType.SPOOLED_DISK_AVAILABLE );
+                        aux.update( ce );
+                    }
+                    catch ( IOException ex )
+                    {
+                        // impossible case.
+                        log.error( "Problem spooling item to disk cache.", ex );
+                        throw new IllegalStateException( ex.getMessage() );
+                    }
+                    catch ( Exception oee )
+                    {
+                        // swallow
+                    }
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "spoolToDisk done for: " + ce.getKey() + " on disk cache[" + i + "]" );
+                    }
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "DiskCache avaialbe, but JCS is not configured to use the DiskCache as a swap." );
+                    }
+                }
+            }
+        }
+
+        if ( !diskAvailable )
+        {
+            try
+            {
+                handleElementEvent( ce, ElementEventType.SPOOLED_DISK_NOT_AVAILABLE );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Trouble handling the ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE  element event", e );
+            }
+        }
+    }
+
+    /**
+     * Gets an item from the cache.
+     * <p>
+     * @param key
+     * @return element from the cache, or null if not present
+     * @see org.apache.commons.jcs.engine.behavior.ICache#get(Object)
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+    {
+        return get( key, false );
+    }
+
+    /**
+     * Do not try to go remote or laterally for this get.
+     * <p>
+     * @param key
+     * @return ICacheElement
+     */
+    public ICacheElement<K, V> localGet( K key )
+    {
+        return get( key, true );
+    }
+
+    /**
+     * Look in memory, then disk, remote, or laterally for this item. The order is dependent on the
+     * order in the cache.ccf file.
+     * <p>
+     * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
+     * remote or lateral if such an auxiliary is configured for this region.
+     * <p>
+     * @param key
+     * @param localOnly
+     * @return ICacheElement
+     */
+    protected ICacheElement<K, V> get( K key, boolean localOnly )
+    {
+        ICacheElement<K, V> element = null;
+
+        boolean found = false;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "get: key = " + key + ", localOnly = " + localOnly );
+        }
+
+        synchronized (this)
+        {
+            try
+            {
+                // First look in memory cache
+                element = memCache.get( key );
+
+                if ( element != null )
+                {
+                    // Found in memory cache
+                    if ( isExpired( element ) )
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( cacheAttr.getCacheName() + " - Memory cache hit, but element expired" );
+                        }
+
+                        missCountExpired++;
+
+                        remove( key );
+
+                        element = null;
+                    }
+                    else
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( cacheAttr.getCacheName() + " - Memory cache hit" );
+                        }
+
+                        // Update counters
+                        hitCountRam++;
+                    }
+
+                    found = true;
+                }
+                else
+                {
+                    // Item not found in memory. If local invocation look in aux
+                    // caches, even if not local look in disk auxiliaries
+
+                    for ( int i = 0; i < auxCaches.length; i++ )
+                    {
+                        AuxiliaryCache<K, V> aux = auxCaches[i];
+
+                        if ( aux != null )
+                        {
+                            CacheType cacheType = aux.getCacheType();
+
+                            if ( !localOnly || cacheType == CacheType.DISK_CACHE )
+                            {
+                                if ( log.isDebugEnabled() )
+                                {
+                                    log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
+                                        + cacheType );
+                                }
+
+                                try
+                                {
+                                    element = aux.get( key );
+                                }
+                                catch ( IOException e )
+                                {
+                                    log.error( "Error getting from aux", e );
+                                }
+                            }
+
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "Got CacheElement: " + element );
+                            }
+
+                            // Item found in one of the auxiliary caches.
+                            if ( element != null )
+                            {
+                                if ( isExpired( element ) )
+                                {
+                                    if ( log.isDebugEnabled() )
+                                    {
+                                        log.debug( cacheAttr.getCacheName() + " - Aux cache[" + i + "] hit, but element expired." );
+                                    }
+
+                                    missCountExpired++;
+
+                                    // This will tell the remotes to remove the item
+                                    // based on the element's expiration policy. The elements attributes
+                                    // associated with the item when it created govern its behavior
+                                    // everywhere.
+                                    remove( key );
+
+                                    element = null;
+                                }
+                                else
+                                {
+                                    if ( log.isDebugEnabled() )
+                                    {
+                                        log.debug( cacheAttr.getCacheName() + " - Aux cache[" + i + "] hit" );
+                                    }
+
+                                    // Update counters
+                                    hitCountAux++;
+                                    copyAuxiliaryRetrievedItemToMemory( element );
+                                }
+
+                                found = true;
+
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem encountered getting element.", e );
+            }
+        }
+
+        if ( !found )
+        {
+            missCountNotFound++;
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( cacheAttr.getCacheName() + " - Miss" );
+            }
+        }
+
+        if (element != null)
+        {
+            element.getElementAttributes().setLastAccessTimeNow();
+        }
+
+        return element;
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
+    {
+        return getMultiple( keys, false );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys. Do not try to go remote or
+     * laterally for this data.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    public Map<K, ICacheElement<K, V>> localGetMultiple( Set<K> keys )
+    {
+        return getMultiple( keys, true );
+    }
+
+    /**
+     * Look in memory, then disk, remote, or laterally for these items. The order is dependent on
+     * the order in the cache.ccf file. Keep looking in each cache location until either the element
+     * is found, or the method runs out of places to look.
+     * <p>
+     * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
+     * remote or lateral if such an auxiliary is configured for this region.
+     * <p>
+     * @param keys
+     * @param localOnly
+     * @return ICacheElement
+     */
+    protected Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys, boolean localOnly )
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "get: key = " + keys + ", localOnly = " + localOnly );
+        }
+
+        try
+        {
+            // First look in memory cache
+            elements.putAll( getMultipleFromMemory( keys ) );
+
+            // If fewer than all items were found in memory, then keep looking.
+            if ( elements.size() != keys.size() )
+            {
+                Set<K> remainingKeys = pruneKeysFound( keys, elements );
+                elements.putAll( getMultipleFromAuxiliaryCaches( remainingKeys, localOnly ) );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem encountered getting elements.", e );
+        }
+
+        // if we didn't find all the elements, increment the miss count by the number of elements not found
+        if ( elements.size() != keys.size() )
+        {
+            missCountNotFound += keys.size() - elements.size();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( cacheAttr.getCacheName() + " - " + ( keys.size() - elements.size() ) + " Misses" );
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Gets items for the keys in the set. Returns a map: key -> result.
+     * <p>
+     * @param keys
+     * @return the elements found in the memory cache
+     * @throws IOException
+     */
+    private Map<K, ICacheElement<K, V>> getMultipleFromMemory( Set<K> keys )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elementsFromMemory = memCache.getMultiple( keys );
+
+        Iterator<ICacheElement<K, V>> elementFromMemoryIterator = new HashMap<K, ICacheElement<K, V>>( elementsFromMemory ).values().iterator();
+
+        while ( elementFromMemoryIterator.hasNext() )
+        {
+            ICacheElement<K, V> element = elementFromMemoryIterator.next();
+
+            if ( element != null )
+            {
+                // Found in memory cache
+                if ( isExpired( element ) )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( cacheAttr.getCacheName() + " - Memory cache hit, but element expired" );
+                    }
+
+                    missCountExpired++;
+
+                    remove( element.getKey() );
+                    elementsFromMemory.remove( element.getKey() );
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( cacheAttr.getCacheName() + " - Memory cache hit" );
+                    }
+
+                    // Update counters
+                    hitCountRam++;
+                }
+            }
+        }
+        return elementsFromMemory;
+    }
+
+    /**
+     * If local invocation look in aux caches, even if not local look in disk auxiliaries.
+     * <p>
+     * @param keys
+     * @param localOnly
+     * @return the elements found in the auxiliary caches
+     * @throws IOException
+     */
+    private Map<K, ICacheElement<K, V>> getMultipleFromAuxiliaryCaches( Set<K> keys, boolean localOnly )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+        Set<K> remainingKeys = new HashSet<K>( keys );
+
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            AuxiliaryCache<K, V> aux = auxCaches[i];
+
+            if ( aux != null )
+            {
+                Map<K, ICacheElement<K, V>> elementsFromAuxiliary =
+                    new HashMap<K, ICacheElement<K, V>>();
+
+                CacheType cacheType = aux.getCacheType();
+
+                if ( !localOnly || cacheType == CacheType.DISK_CACHE )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
+                            + cacheType );
+                    }
+
+                    try
+                    {
+                        elementsFromAuxiliary.putAll( aux.getMultiple( remainingKeys ) );
+                    }
+                    catch ( IOException e )
+                    {
+                        log.error( "Error getting from aux", e );
+                    }
+                }
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Got CacheElements: " + elementsFromAuxiliary );
+                }
+
+                processRetrievedElements( i, elementsFromAuxiliary );
+
+                elements.putAll( elementsFromAuxiliary );
+
+                if ( elements.size() == keys.size() )
+                {
+                    break;
+                }
+                else
+                {
+                    remainingKeys = pruneKeysFound( keys, elements );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Build a map of all the matching elements in all of the auxiliaries and memory.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any matching keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
+    {
+        return getMatching( pattern, false );
+    }
+
+    /**
+     * Build a map of all the matching elements in all of the auxiliaries and memory. Do not try to
+     * go remote or laterally for this data.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any matching keys
+     */
+    public Map<K, ICacheElement<K, V>> localGetMatching( String pattern )
+    {
+        return getMatching( pattern, true );
+    }
+
+    /**
+     * Build a map of all the matching elements in all of the auxiliaries and memory. Items in
+     * memory will replace from the auxiliaries in the returned map. The auxiliaries are accessed in
+     * opposite order. It's assumed that those closer to home are better.
+     * <p>
+     * Do not try to go remote or laterally for this get if it is localOnly. Otherwise try to go
+     * remote or lateral if such an auxiliary is configured for this region.
+     * <p>
+     * @param pattern
+     * @param localOnly
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any matching keys
+     */
+    protected Map<K, ICacheElement<K, V>> getMatching( String pattern, boolean localOnly )
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "get: pattern [" + pattern + "], localOnly = " + localOnly );
+        }
+
+        try
+        {
+            // First look in auxiliaries
+            elements.putAll( getMatchingFromAuxiliaryCaches( pattern, localOnly ) );
+
+            // then look in memory, override aux with newer memory items.
+            elements.putAll( getMatchingFromMemory( pattern ) );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem encountered getting elements.", e );
+        }
+
+        return elements;
+    }
+
+    /**
+     * Gets the key array from the memcache. Builds a set of matches. Calls getMultiple with the
+     * set. Returns a map: key -> result.
+     * <p>
+     * @param pattern
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any matching keys
+     * @throws IOException
+     */
+    protected Map<K, ICacheElement<K, V>> getMatchingFromMemory( String pattern )
+        throws IOException
+    {
+        // find matches in key array
+        // this avoids locking the memory cache, but it uses more memory
+        Set<K> keyArray = memCache.getKeySet();
+
+        Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray( pattern, keyArray );
+
+        // call get multiple
+        return getMultipleFromMemory( matchingKeys );
+    }
+
+    /**
+     * If local invocation look in aux caches, even if not local look in disk auxiliaries.
+     * <p>
+     * Moves in reverse order of definition. This will allow you to override those that are from the
+     * remote with those on disk.
+     * <p>
+     * @param pattern
+     * @param localOnly
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any matching keys
+     * @throws IOException
+     */
+    private Map<K, ICacheElement<K, V>> getMatchingFromAuxiliaryCaches( String pattern, boolean localOnly )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        for ( int i = auxCaches.length - 1; i >= 0; i-- )
+        {
+            AuxiliaryCache<K, V> aux = auxCaches[i];
+
+            if ( aux != null )
+            {
+                Map<K, ICacheElement<K, V>> elementsFromAuxiliary =
+                    new HashMap<K, ICacheElement<K, V>>();
+
+                CacheType cacheType = aux.getCacheType();
+
+                if ( !localOnly || cacheType == CacheType.DISK_CACHE )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Attempting to get from aux [" + aux.getCacheName() + "] which is of type: "
+                            + cacheType );
+                    }
+
+                    try
+                    {
+                        elementsFromAuxiliary.putAll( aux.getMatching( pattern ) );
+                    }
+                    catch ( IOException e )
+                    {
+                        log.error( "Error getting from aux", e );
+                    }
+
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Got CacheElements: " + elementsFromAuxiliary );
+                    }
+
+                    processRetrievedElements( i, elementsFromAuxiliary );
+
+                    elements.putAll( elementsFromAuxiliary );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Remove expired elements retrieved from an auxiliary. Update memory with good items.
+     * <p>
+     * @param i - the aux index
+     * @param elementsFromAuxiliary
+     * @throws IOException
+     */
+    private void processRetrievedElements( int i, Map<K, ICacheElement<K, V>> elementsFromAuxiliary )
+        throws IOException
+    {
+        Iterator<ICacheElement<K, V>> elementFromAuxiliaryIterator = new HashMap<K, ICacheElement<K, V>>( elementsFromAuxiliary ).values().iterator();
+
+        while ( elementFromAuxiliaryIterator.hasNext() )
+        {
+            ICacheElement<K, V> element = elementFromAuxiliaryIterator.next();
+
+            // Item found in one of the auxiliary caches.
+            if ( element != null )
+            {
+                if ( isExpired( element ) )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( cacheAttr.getCacheName() + " - Aux cache[" + i + "] hit, but element expired." );
+                    }
+
+                    missCountExpired++;
+
+                    // This will tell the remote caches to remove the item
+                    // based on the element's expiration policy. The elements attributes
+                    // associated with the item when it created govern its behavior
+                    // everywhere.
+                    remove( element.getKey() );
+                    elementsFromAuxiliary.remove( element.getKey() );
+                }
+                else
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( cacheAttr.getCacheName() + " - Aux cache[" + i + "] hit" );
+                    }
+
+                    // Update counters
+                    hitCountAux++;
+                    copyAuxiliaryRetrievedItemToMemory( element );
+                }
+            }
+        }
+    }
+
+    /**
+     * Copies the item to memory if the memory size is greater than 0. Only spool if the memory
+     * cache size is greater than 0, else the item will immediately get put into purgatory.
+     * <p>
+     * @param element
+     * @throws IOException
+     */
+    private void copyAuxiliaryRetrievedItemToMemory( ICacheElement<K, V> element )
+        throws IOException
+    {
+        if ( memCache.getCacheAttributes().getMaxObjects() > 0 )
+        {
+            memCache.update( element );
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Skipping memory update since no items are allowed in memory" );
+            }
+        }
+    }
+
+    /**
+     * Returns a set of keys that were not found.
+     * <p>
+     * @param keys
+     * @param foundElements
+     * @return the original set of cache keys, minus any cache keys present in the map keys of the
+     *         foundElements map
+     */
+    private Set<K> pruneKeysFound( Set<K> keys, Map<K, ICacheElement<K, V>> foundElements )
+    {
+        Set<K> remainingKeys = new HashSet<K>( keys );
+
+        for (K key : foundElements.keySet())
+        {
+            remainingKeys.remove( key );
+        }
+
+        return remainingKeys;
+    }
+
+    /**
+     * Get a set of the keys for all elements in the cache
+     * <p>
+     * @return A set of the key type
+     */
+    public Set<K> getKeySet()
+    {
+        return getKeySet(false);
+    }
+
+    /**
+     * Get a set of the keys for all elements in the cache
+     * <p>
+     * @param localOnly true if only memory keys are requested
+     *
+     * @return A set of the key type
+     */
+    public Set<K> getKeySet(boolean localOnly)
+    {
+        HashSet<K> allKeys = new HashSet<K>();
+
+        allKeys.addAll( memCache.getKeySet() );
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            AuxiliaryCache<K, V> aux = auxCaches[i];
+            if ( aux != null )
+            {
+                if(!localOnly || aux.getCacheType() == CacheType.DISK_CACHE)
+                {
+                    try
+                    {
+                        allKeys.addAll( aux.getKeySet() );
+                    }
+                    catch ( IOException e )
+                    {
+                        // ignore
+                    }
+                }
+            }
+        }
+        return allKeys;
+    }
+
+    /**
+     * Removes an item from the cache.
+     * <p>
+     * @param key
+     * @return true is it was removed
+     * @see org.apache.commons.jcs.engine.behavior.ICache#remove(Object)
+     */
+    @Override
+    public boolean remove( K key )
+    {
+        return remove( key, false );
+    }
+
+    /**
+     * Do not propagate removeall laterally or remotely.
+     * <p>
+     * @param key
+     * @return true if the item was already in the cache.
+     */
+    public boolean localRemove( K key )
+    {
+        return remove( key, true );
+    }
+
+    /**
+     * fromRemote: If a remove call was made on a cache with both, then the remote should have been
+     * called. If it wasn't then the remote is down. we'll assume it is down for all. If it did come
+     * from the remote then the cache is remotely configured and lateral removal is unnecessary. If
+     * it came laterally then lateral removal is unnecessary. Does this assume that there is only
+     * one lateral and remote for the cache? Not really, the initial removal should take care of the
+     * problem if the source cache was similarly configured. Otherwise the remote cache, if it had
+     * no laterals, would remove all the elements from remotely configured caches, but if those
+     * caches had some other weird laterals that were not remotely configured, only laterally
+     * propagated then they would go out of synch. The same could happen for multiple remotes. If
+     * this looks necessary we will need to build in an identifier to specify the source of a
+     * removal.
+     * <p>
+     * @param key
+     * @param localOnly
+     * @return true if the item was in the cache, else false
+     */
+    protected synchronized boolean remove( K key, boolean localOnly )
+    {
+        // not thread safe, but just for debugging and testing.
+        removeCount++;
+
+        boolean removed = false;
+
+        try
+        {
+            removed = memCache.remove( key );
+        }
+        catch ( IOException e )
+        {
+            log.error( e );
+        }
+
+        // Removes from all auxiliary caches.
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            ICache<K, V> aux = auxCaches[i];
+
+            if ( aux == null )
+            {
+                continue;
+            }
+
+            CacheType cacheType = aux.getCacheType();
+
+            // for now let laterals call remote remove but not vice versa
+
+            if ( localOnly && ( cacheType == CacheType.REMOTE_CACHE || cacheType == CacheType.LATERAL_CACHE ) )
+            {
+                continue;
+            }
+            try
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Removing " + key + " from cacheType" + cacheType );
+                }
+
+                boolean b = aux.remove( key );
+
+                // Don't take the remote removal into account.
+                if ( !removed && cacheType != CacheType.REMOTE_CACHE )
+                {
+                    removed = b;
+                }
+            }
+            catch ( IOException ex )
+            {
+                log.error( "Failure removing from aux", ex );
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Clears the region. This command will be sent to all auxiliaries. Some auxiliaries, such as
+     * the JDBC disk cache, can be configured to not honor removeAll requests.
+     * <p>
+     * @see org.apache.commons.jcs.engine.behavior.ICache#removeAll()
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        removeAll( false );
+    }
+
+    /**
+     * Will not pass the remove message remotely.
+     * <p>
+     * @throws IOException
+     */
+    public void localRemoveAll()
+        throws IOException
+    {
+        removeAll( true );
+    }
+
+    /**
+     * Removes all cached items.
+     * <p>
+     * @param localOnly must pass in false to get remote and lateral aux's updated. This prevents
+     *            looping.
+     * @throws IOException
+     */
+    protected synchronized void removeAll( boolean localOnly )
+        throws IOException
+    {
+        try
+        {
+            memCache.removeAll();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Removed All keys from the memory cache." );
+            }
+        }
+        catch ( IOException ex )
+        {
+            log.error( "Trouble updating memory cache.", ex );
+        }
+
+        // Removes from all auxiliary disk caches.
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            ICache<K, V> aux = auxCaches[i];
+
+            if ( aux != null && ( aux.getCacheType() == CacheType.DISK_CACHE || !localOnly ) )
+            {
+                try
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Removing All keys from cacheType" + aux.getCacheType() );
+                    }
+
+                    aux.removeAll();
+                }
+                catch ( IOException ex )
+                {
+                    log.error( "Failure removing all from aux", ex );
+                }
+            }
+        }
+    }
+
+    /**
+     * Flushes all cache items from memory to auxiliary caches and close the auxiliary caches.
+     */
+    @Override
+    public void dispose()
+    {
+        dispose( false );
+    }
+
+    /**
+     * Invoked only by CacheManager. This method disposes of the auxiliaries one by one. For the
+     * disk cache, the items in memory are freed, meaning that they will be sent through the
+     * overflow channel to disk. After the auxiliaries are disposed, the memory cache is disposed.
+     * <p>
+     * @param fromRemote
+     */
+    public synchronized void dispose( boolean fromRemote )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "In DISPOSE, [" + this.cacheAttr.getCacheName() + "] fromRemote [" + fromRemote + "]" );
+        }
+
+        // If already disposed, return immediately
+        if ( !alive )
+        {
+            return;
+        }
+        alive = false;
+
+        // Now, shut down the event queue
+        if (elementEventQ != null)
+        {
+            elementEventQ.dispose();
+            elementEventQ = null;
+        }
+
+        // Dispose of each auxiliary cache, Remote auxiliaries will be
+        // skipped if 'fromRemote' is true.
+        for ( int i = 0; i < auxCaches.length; i++ )
+        {
+            try
+            {
+                ICache<K, V> aux = auxCaches[i];
+
+                // Skip this auxiliary if:
+                // - The auxiliary is null
+                // - The auxiliary is not alive
+                // - The auxiliary is remote and the invocation was remote
+
+                if ( aux == null || aux.getStatus() != CacheStatus.ALIVE
+                    || ( fromRemote && aux.getCacheType() == CacheType.REMOTE_CACHE ) )
+                {
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "In DISPOSE, [" + this.cacheAttr.getCacheName() + "] SKIPPING auxiliary [" + aux + "] fromRemote ["
+                            + fromRemote + "]" );
+                    }
+                    continue;
+                }
+
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "In DISPOSE, [" + this.cacheAttr.getCacheName() + "] auxiliary [" + aux + "]" );
+                }
+
+                // IT USED TO BE THE CASE THAT (If the auxiliary is not a lateral, or the cache
+                // attributes
+                // have 'getUseLateral' set, all the elements currently in
+                // memory are written to the lateral before disposing)
+                // I changed this. It was excessive. Only the disk cache needs the items, since only
+                // the disk cache
+                // is in a situation to not get items on a put.
+
+                if ( aux.getCacheType() == CacheType.DISK_CACHE )
+                {
+                    int numToFree = memCache.getSize();
+                    memCache.freeElements( numToFree );
+
+                    if ( log.isInfoEnabled() )
+                    {
+                        log.info( "In DISPOSE, [" + this.cacheAttr.getCacheName() + "] put " + numToFree + " into auxiliary " + aux );
+                    }
+                }
+
+                // Dispose of the auxiliary
+                aux.dispose();
+            }
+            catch ( IOException ex )
+            {
+                log.error( "Failure disposing of aux.", ex );
+            }
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "In DISPOSE, [" + this.cacheAttr.getCacheName() + "] disposing of memory cache." );
+        }
+        try
+        {
+            memCache.dispose();
+        }
+        catch ( IOException ex )
+        {
+            log.error( "Failure disposing of memCache", ex );
+        }
+    }
+
+    /**
+     * Calling save cause the entire contents of the memory cache to be flushed to all auxiliaries.
+     * Though this put is extremely fast, this could bog the cache and should be avoided. The
+     * dispose method should call a version of this. Good for testing.
+     */
+    public void save()
+    {
+        synchronized ( this )
+        {
+            if ( !alive )
+            {
+                return;
+            }
+            alive = false;
+
+            for ( int i = 0; i < auxCaches.length; i++ )
+            {
+                try
+                {
+                    ICache<K, V> aux = auxCaches[i];
+
+                    if ( aux.getStatus() == CacheStatus.ALIVE )
+                    {
+                        for (K key : memCache.getKeySet())
+                        {
+                            ICacheElement<K, V> ce = memCache.get(key);
+
+                            if (ce != null)
+                            {
+                                aux.update( ce );
+                            }
+                        }
+                    }
+                }
+                catch ( IOException ex )
+                {
+                    log.error( "Failure saving aux caches.", ex );
+                }
+            }
+        }
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Called save for [" + cacheAttr.getCacheName() + "]" );
+        }
+    }
+
+    /**
+     * Gets the size attribute of the Cache object. This return the number of elements, not the byte
+     * size.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return memCache.getSize();
+    }
+
+    /**
+     * Gets the cacheType attribute of the Cache object.
+     * <p>
+     * @return The cacheType value
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.CACHE_HUB;
+    }
+
+    /**
+     * Gets the status attribute of the Cache object.
+     * <p>
+     * @return The status value
+     */
+    @Override
+    public synchronized CacheStatus getStatus()
+    {
+        return alive ? CacheStatus.ALIVE : CacheStatus.DISPOSED;
+    }
+
+    /**
+     * Gets stats for debugging.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        return getStatistics().toString();
+    }
+
+    /**
+     * This returns data gathered for this region and all the auxiliaries it currently uses.
+     * <p>
+     * @return Statistics and Info on the Region.
+     */
+    public ICacheStats getStatistics()
+    {
+        ICacheStats stats = new CacheStats();
+        stats.setRegionName( this.getCacheName() );
+
+        // store the composite cache stats first
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Integer>( "HitCountRam", Integer.valueOf(getHitCountRam()) ) );
+        elems.add(new StatElement<Integer>( "HitCountAux", Integer.valueOf(getHitCountAux()) ) );
+
+        stats.setStatElements( elems );
+
+        // memory + aux, memory is not considered an auxiliary internally
+        int total = auxCaches.length + 1;
+        ArrayList<IStats> auxStats = new ArrayList<IStats>(total);
+
+        auxStats.add(getMemoryCache().getStatistics());
+
+        for ( AuxiliaryCache<K, V> aux : auxCaches )
+        {
+            auxStats.add(aux.getStatistics());
+        }
+
+        // store the auxiliary stats
+        stats.setAuxiliaryCacheStats( auxStats );
+
+        return stats;
+    }
+
+    /**
+     * Gets the cacheName attribute of the Cache object. This is also known as the region name.
+     * <p>
+     * @return The cacheName value
+     */
+    @Override
+    public String getCacheName()
+    {
+        return cacheAttr.getCacheName();
+    }
+
+    /**
+     * Gets the default element attribute of the Cache object This returns a copy. It does not
+     * return a reference to the attributes.
+     * <p>
+     * @return The attributes value
+     */
+    public IElementAttributes getElementAttributes()
+    {
+        if ( attr != null )
+        {
+            return attr.copy();
+        }
+        return null;
+    }
+
+    /**
+     * Sets the default element attribute of the Cache object.
+     * <p>
+     * @param attr
+     */
+    public void setElementAttributes( IElementAttributes attr )
+    {
+        this.attr = attr;
+    }
+
+    /**
+     * Gets the ICompositeCacheAttributes attribute of the Cache object.
+     * <p>
+     * @return The ICompositeCacheAttributes value
+     */
+    public ICompositeCacheAttributes getCacheAttributes()
+    {
+        return this.cacheAttr;
+    }
+
+    /**
+     * Sets the ICompositeCacheAttributes attribute of the Cache object.
+     * <p>
+     * @param cattr The new ICompositeCacheAttributes value
+     */
+    public void setCacheAttributes( ICompositeCacheAttributes cattr )
+    {
+        this.cacheAttr = cattr;
+        // need a better way to do this, what if it is in error
+        this.memCache.initialize( this );
+    }
+
+    /**
+     * Gets the elementAttributes attribute of the Cache object.
+     * <p>
+     * @param key
+     * @return The elementAttributes value
+     * @throws CacheException
+     * @throws IOException
+     */
+    public IElementAttributes getElementAttributes( K key )
+        throws CacheException, IOException
+    {
+        ICacheElement<K, V> ce = get( key );
+        if ( ce == null )
+        {
+            throw new ObjectNotFoundException( "key " + key + " is not found" );
+        }
+        return ce.getElementAttributes();
+    }
+
+    /**
+     * Determine if the element is expired based on the values of the element attributes
+     *
+     * @param element the element
+     *
+     * @return true if the element is expired
+     */
+    public boolean isExpired( ICacheElement<K, V> element)
+    {
+        return isExpired(element, System.currentTimeMillis(),
+                ElementEventType.EXCEEDED_MAXLIFE_ONREQUEST,
+                ElementEventType.EXCEEDED_IDLETIME_ONREQUEST );
+    }
+
+    /**
+     * Check if the element is expired based on the values of the element attributes
+     *
+     * @param element the element
+     * @param timestamp the timestamp to compare to
+     * @param eventMaxlife the event to fire in case the max life time is exceeded
+     * @param eventIdle the event to fire in case the idle time is exceeded
+     *
+     * @return true if the element is expired
+     */
+    public boolean isExpired(ICacheElement<K, V> element, long timestamp,
+            ElementEventType eventMaxlife, ElementEventType eventIdle)
+    {
+        try
+        {
+            IElementAttributes attributes = element.getElementAttributes();
+
+            if ( !attributes.getIsEternal() )
+            {
+                // Remove if maxLifeSeconds exceeded
+
+                long maxLifeSeconds = attributes.getMaxLife();
+                long createTime = attributes.getCreateTime();
+
+                final long timeFactorForMilliseconds = attributes.getTimeFactorForMilliseconds();
+
+                if ( maxLifeSeconds != -1 && ( timestamp - createTime ) > ( maxLifeSeconds * timeFactorForMilliseconds) )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Exceeded maxLife: " + element.getKey() );
+                    }
+
+                    handleElementEvent( element, eventMaxlife );
+
+                    return true;
+                }
+                long idleTime = attributes.getIdleTime();
+                long lastAccessTime = attributes.getLastAccessTime();
+
+                // Remove if maxIdleTime exceeded
+                // If you have a 0 size memory cache, then the last access will
+                // not get updated.
+                // you will need to set the idle time to -1.
+
+                if ( ( idleTime != -1 ) && ( timestamp - lastAccessTime ) > idleTime * timeFactorForMilliseconds )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.info( "Exceeded maxIdle: " + element.getKey() );
+                    }
+
+                    handleElementEvent( element, eventIdle );
+
+                    return true;
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Error determining expiration period, expiring", e );
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * If there are event handlers for the item, then create an event and queue it up.
+     * <p>
+     * This does not call handle directly; instead the handler and the event are put into a queue.
+     * This prevents the event handling from blocking normal cache operations.
+     * <p>
+     * @param element the item
+     * @param eventType the event type
+     */
+    public void handleElementEvent( ICacheElement<K, V> element, ElementEventType eventType )
+    {
+        ArrayList<IElementEventHandler> eventHandlers = element.getElementAttributes().getElementEventHandlers();
+        if ( eventHandlers != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Element Handlers are registered.  Create event type " + eventType );
+            }
+            if ( elementEventQ == null )
+            {
+                log.warn("No element event queue available for cache " + getCacheName());
+                return;
+            }
+            IElementEvent event = new ElementEvent( element, eventType );
+            for (IElementEventHandler hand : eventHandlers)
+            {
+                try
+                {
+                   elementEventQ.addElementEvent( hand, event );
+                }
+                catch ( IOException e )
+                {
+                    log.error( "Trouble adding element event to queue", e );
+                }
+            }
+        }
+    }
+
+    /**
+     * Create the MemoryCache based on the config parameters.
+     * TODO: consider making this an auxiliary, despite its close tie to the CacheHub.
+     * TODO: might want to create a memory cache config file separate from that of the hub -- ICompositeCacheAttributes
+     * <p>
+     * @param cattr
+     */
+    private void createMemoryCache( ICompositeCacheAttributes cattr )
+    {
+        if ( memCache == null )
+        {
+            try
+            {
+                Class<?> c = Class.forName( cattr.getMemoryCacheName() );
+                @SuppressWarnings("unchecked") // Need cast
+                IMemoryCache<K, V> newInstance = (IMemoryCache<K, V>) c.newInstance();
+                memCache = newInstance;
+                memCache.initialize( this );
+            }
+            catch ( Exception e )
+            {
+                log.warn( "Failed to init mem cache, using: LRUMemoryCache", e );
+
+                this.memCache = new LRUMemoryCache<K, V>();
+                this.memCache.initialize( this );
+            }
+        }
+        else
+        {
+            log.warn( "Refusing to create memory cache -- already exists." );
+        }
+    }
+
+    /**
+     * Access to the memory cache for instrumentation.
+     * <p>
+     * @return the MemoryCache implementation
+     */
+    public IMemoryCache<K, V> getMemoryCache()
+    {
+        return memCache;
+    }
+
+    /**
+     * Number of times a requested item was found in the memory cache.
+     * <p>
+     * @return number of hits in memory
+     */
+    public int getHitCountRam()
+    {
+        return hitCountRam;
+    }
+
+    /**
+     * Number of times a requested item was found in and auxiliary cache.
+     * @return number of auxiliary hits.
+     */
+    public int getHitCountAux()
+    {
+        return hitCountAux;
+    }
+
+    /**
+     * Number of times a requested element was not found.
+     * @return number of misses.
+     */
+    public int getMissCountNotFound()
+    {
+        return missCountNotFound;
+    }
+
+    /**
+     * Number of times a requested element was found but was expired.
+     * @return number of found but expired gets.
+     */
+    public int getMissCountExpired()
+    {
+        return missCountExpired;
+    }
+
+    /**
+     * Sets the key matcher used by get matching.
+     * <p>
+     * @param keyMatcher
+     */
+    @Override
+    public void setKeyMatcher( IKeyMatcher<K> keyMatcher )
+    {
+        if ( keyMatcher != null )
+        {
+            this.keyMatcher = keyMatcher;
+        }
+    }
+
+    /**
+     * Returns the key matcher used by get matching.
+     * <p>
+     * @return keyMatcher
+     */
+    public IKeyMatcher<K> getKeyMatcher()
+    {
+        return this.keyMatcher;
+    }
+
+    /**
+     * @param updateCount The updateCount to set.
+     */
+    public synchronized void setUpdateCount( int updateCount )
+    {
+        this.updateCount = updateCount;
+    }
+
+    /**
+     * @return Returns the updateCount.
+     */
+    public synchronized int getUpdateCount()
+    {
+        return updateCount;
+    }
+
+    /**
+     * @param removeCount The removeCount to set.
+     */
+    public synchronized void setRemoveCount( int removeCount )
+    {
+        this.removeCount = removeCount;
+    }
+
+    /**
+     * @return Returns the removeCount.
+     */
+    public synchronized int getRemoveCount()
+    {
+        return removeCount;
+    }
+
+    /**
+     * This returns the stats.
+     * <p>
+     * @return getStats()
+     */
+    @Override
+    public String toString()
+    {
+        return getStats();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheConfigurator.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheConfigurator.java
new file mode 100644
index 0000000..a9456a6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheConfigurator.java
@@ -0,0 +1,648 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.match.KeyMatcherPatternImpl;
+import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
+import org.apache.commons.jcs.utils.config.OptionConverter;
+import org.apache.commons.jcs.utils.config.PropertySetter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * This class configures JCS based on a properties object.
+ * <p>
+ * This class is based on the log4j class org.apache.log4j.PropertyConfigurator which was made by:
+ * "Luke Blanshard" <Luke at quiq.com>"Mark DONSZELMANN" <Mark.Donszelmann at cern.ch>"Anders Kristensen"
+ * <akristensen at dynamicsoft.com>
+ */
+public class CompositeCacheConfigurator
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( CompositeCacheConfigurator.class );
+
+    /** default region prefix */
+    static final String DEFAULT_REGION = "jcs.default";
+
+    /** normal region prefix */
+    static final String REGION_PREFIX = "jcs.region.";
+
+    /** system region prefix. might not be used */
+    static final String SYSTEM_REGION_PREFIX = "jcs.system.";
+
+    /** auxiliary prefix */
+    static final String AUXILIARY_PREFIX = "jcs.auxiliary.";
+
+    /** .attributes */
+    static final String ATTRIBUTE_PREFIX = ".attributes";
+
+    /** .cacheattributes */
+    static final String CACHE_ATTRIBUTE_PREFIX = ".cacheattributes";
+
+    /** .elementattributes */
+    static final String ELEMENT_ATTRIBUTE_PREFIX = ".elementattributes";
+
+    /**
+     * jcs.auxiliary.NAME.keymatcher=CLASSNAME
+     * <p>
+     * jcs.auxiliary.NAME.keymatcher.attributes.CUSTOMPROPERTY=VALUE
+     */
+    public static final String KEY_MATCHER_PREFIX = ".keymatcher";
+
+    /** Can't operate on the interface. */
+    private final CompositeCacheManager compositeCacheManager;
+
+    /**
+     * Constructor for the CompositeCacheConfigurator object
+     *<p>
+     * @param ccMgr
+     */
+    public CompositeCacheConfigurator( CompositeCacheManager ccMgr )
+    {
+        this.compositeCacheManager = ccMgr;
+    }
+
+    /**
+     * Configure cached for file name.
+     * <p>
+     * This is only used for testing. The manager handles the translation of a file into a
+     * properties object.
+     * <p>
+     * @param configFileName
+     */
+    protected void doConfigure( String configFileName )
+    {
+        Properties props = new Properties();
+        FileInputStream istream = null;
+
+        try
+        {
+            istream = new FileInputStream( configFileName );
+            props.load( istream );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Could not read configuration file, ignored: " + configFileName, e );
+            return;
+        }
+        finally
+        {
+            if (istream != null)
+            {
+                try
+                {
+                    istream.close();
+                }
+                catch (IOException e)
+                {
+                    log.error( "Could not close configuration file " + configFileName, e );
+                }
+            }
+        }
+
+        // If we reach here, then the config file is alright.
+        doConfigure( props );
+    }
+
+    /**
+     * Configure cache for properties object.
+     * <p>
+     * This method proceeds in several steps:
+     * <ul>
+     * <li>Store props for use by non configured caches.
+     * <li>Set default value list
+     * <li>Set default cache attr
+     * <li>Set default element attr
+     * <li>Setup system caches to be used
+     * <li>Setup preconfigured caches
+     * </ul>
+     * @param properties
+     */
+    public void doConfigure( Properties properties )
+    {
+        long start = System.currentTimeMillis();
+
+        // store props for use by non configured caches
+        compositeCacheManager.setConfigurationProperties( properties );
+
+        // set default value list
+        setDefaultAuxValues( properties );
+
+        // set default cache attr
+        setDefaultCompositeCacheAttributes( properties );
+
+        // set default element attr
+        setDefaultElementAttributes( properties );
+
+        // set up system caches to be used by non system caches
+        // need to make sure there is no circularity of reference
+        parseSystemRegions( properties );
+
+        // setup preconfigured caches
+        parseRegions( properties );
+
+        long end = System.currentTimeMillis();
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Finished configuration in " + ( end - start ) + " ms." );
+        }
+
+    }
+
+    /**
+     * Set the default aux list for new caches.
+     * <p>
+     * @param props
+     */
+    protected void setDefaultAuxValues( Properties props )
+    {
+        String value = OptionConverter.findAndSubst( DEFAULT_REGION, props );
+        compositeCacheManager.setDefaultAuxValues(value);
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Setting default auxiliaries to " + value );
+        }
+    }
+
+    /**
+     * Set the default CompositeCacheAttributes for new caches.
+     *<p>
+     * @param props
+     */
+    protected void setDefaultCompositeCacheAttributes( Properties props )
+    {
+        ICompositeCacheAttributes icca = parseCompositeCacheAttributes( props, "",
+                                                                        CompositeCacheConfigurator.DEFAULT_REGION );
+        compositeCacheManager.setDefaultCacheAttributes( icca );
+
+        log.info( "setting defaultCompositeCacheAttributes to " + icca );
+    }
+
+    /**
+     * Set the default ElementAttributes for new caches.
+     *<p>
+     * @param props
+     */
+    protected void setDefaultElementAttributes( Properties props )
+    {
+        IElementAttributes iea = parseElementAttributes( props, "", CompositeCacheConfigurator.DEFAULT_REGION );
+        compositeCacheManager.setDefaultElementAttributes( iea );
+
+        log.info( "setting defaultElementAttributes to " + iea );
+    }
+
+    /**
+     * Create caches used internally. System status gives them creation priority.
+     *<p>
+     * @param props
+     */
+    protected <K, V> void parseSystemRegions( Properties props )
+    {
+        Enumeration<?> en = props.propertyNames();
+        while ( en.hasMoreElements() )
+        {
+            String key = (String) en.nextElement();
+            if ( key.startsWith( SYSTEM_REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 )
+            {
+                String regionName = key.substring( SYSTEM_REGION_PREFIX.length() );
+                String value = OptionConverter.findAndSubst( key, props );
+                ICache<K, V> cache;
+                synchronized ( regionName )
+                {
+                    cache = parseRegion( props, regionName, value, null, SYSTEM_REGION_PREFIX );
+                }
+
+                compositeCacheManager.addCache( regionName, cache );
+            }
+        }
+    }
+
+    /**
+     * Parse region elements.
+     *<p>
+     * @param props
+     */
+    protected <K, V> void parseRegions( Properties props )
+    {
+        List<String> regionNames = new ArrayList<String>();
+
+        Enumeration<?> en = props.propertyNames();
+        while ( en.hasMoreElements() )
+        {
+            String key = (String) en.nextElement();
+            if ( key.startsWith( REGION_PREFIX ) && key.indexOf( "attributes" ) == -1 )
+            {
+                String regionName = key.substring( REGION_PREFIX.length() );
+
+                regionNames.add( regionName );
+
+                String auxiliaryList = OptionConverter.findAndSubst( key, props );
+                ICache<K, V> cache;
+                synchronized ( regionName )
+                {
+                    cache = parseRegion( props, regionName, auxiliaryList );
+                }
+                compositeCacheManager.addCache( regionName, cache );
+            }
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Parsed regions " + regionNames );
+        }
+    }
+
+    /**
+     * Create cache region.
+     *<p>
+     * @param props
+     * @param regName
+     * @param value
+     * @return CompositeCache
+     */
+    protected <K, V> CompositeCache<K, V> parseRegion(
+            Properties props, String regName, String value )
+    {
+        return parseRegion( props, regName, value, null, REGION_PREFIX );
+    }
+
+    /**
+     * Get all the properties for a region and configure its cache.
+     * <p>
+     * This method tells the other parse method the name of the region prefix.
+     *<p>
+     * @param props
+     * @param regName
+     * @param value
+     * @param cca
+     * @return CompositeCache
+     */
+    protected <K, V> CompositeCache<K, V> parseRegion(
+            Properties props, String regName, String value, ICompositeCacheAttributes cca )
+    {
+        return parseRegion( props, regName, value, cca, REGION_PREFIX );
+    }
+
+    /**
+     * Get all the properties for a region and configure its cache.
+     *<p>
+     * @param props
+     * @param regName
+     * @param value
+     * @param cca
+     * @param regionPrefix
+     * @return CompositeCache
+     */
+    protected <K, V> CompositeCache<K, V> parseRegion(
+            Properties props, String regName, String value,
+            ICompositeCacheAttributes cca, String regionPrefix )
+    {
+        // First, create or get the cache and element attributes, and create
+        // the cache.
+        IElementAttributes ea = parseElementAttributes( props, regName, regionPrefix );
+
+        CompositeCache<K, V> cache = ( cca == null )
+            ? new CompositeCache<K, V>( parseCompositeCacheAttributes( props, regName, regionPrefix ), ea )
+            : new CompositeCache<K, V>( cca, ea );
+
+        // Inject scheduler service
+        cache.setScheduledExecutorService(compositeCacheManager.getScheduledExecutorService());
+
+        // Inject element event queue
+        cache.setElementEventQueue(compositeCacheManager.getElementEventQueue());
+
+        if (cache.getMemoryCache() instanceof IRequireScheduler)
+        {
+            ((IRequireScheduler)cache.getMemoryCache()).setScheduledExecutorService(
+                    compositeCacheManager.getScheduledExecutorService());
+        }
+
+        if (value != null)
+        {
+            // Next, create the auxiliaries for the new cache
+            List<AuxiliaryCache<K, V>> auxList = new ArrayList<AuxiliaryCache<K, V>>();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Parsing region name '" + regName + "', value '" + value + "'" );
+            }
+
+            // We must skip over ',' but not white space
+            StringTokenizer st = new StringTokenizer( value, "," );
+
+            // If value is not in the form ", appender.." or "", then we should set
+            // the priority of the category.
+
+            if ( !( value.startsWith( "," ) || value.equals( "" ) ) )
+            {
+                // just to be on the safe side...
+                if ( !st.hasMoreTokens() )
+                {
+                    return null;
+                }
+            }
+
+            AuxiliaryCache<K, V> auxCache;
+            String auxName;
+            while ( st.hasMoreTokens() )
+            {
+                auxName = st.nextToken().trim();
+                if ( auxName == null || auxName.equals( "," ) )
+                {
+                    continue;
+                }
+                log.debug( "Parsing auxiliary named \"" + auxName + "\"." );
+
+                auxCache = parseAuxiliary( props, auxName, regName );
+
+                if ( auxCache != null )
+                {
+                    if (auxCache instanceof IRequireScheduler)
+                    {
+                        ((IRequireScheduler)auxCache).setScheduledExecutorService(
+                                compositeCacheManager.getScheduledExecutorService());
+                    }
+
+                    auxList.add( auxCache );
+                }
+            }
+
+            // Associate the auxiliaries with the cache
+            @SuppressWarnings("unchecked") // No generic arrays in java
+            AuxiliaryCache<K, V>[] auxArray = auxList.toArray( new AuxiliaryCache[0] );
+            cache.setAuxCaches( auxArray );
+        }
+
+        // Return the new cache
+        return cache;
+    }
+
+    /**
+     * Get an ICompositeCacheAttributes for the listed region.
+     *<p>
+     * @param props
+     * @param regName
+     * @return ICompositeCacheAttributes
+     */
+    protected ICompositeCacheAttributes parseCompositeCacheAttributes( Properties props, String regName )
+    {
+        return parseCompositeCacheAttributes( props, regName, REGION_PREFIX );
+    }
+
+    /**
+     * Get the main attributes for a region.
+     *<p>
+     * @param props
+     * @param regName
+     * @param regionPrefix
+     * @return ICompositeCacheAttributes
+     */
+    protected ICompositeCacheAttributes parseCompositeCacheAttributes( Properties props, String regName,
+                                                                       String regionPrefix )
+    {
+        ICompositeCacheAttributes ccAttr;
+
+        String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX;
+
+        // auxFactory was not previously initialized.
+        // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX;
+        ccAttr = OptionConverter.instantiateByKey( props, attrName, null );
+
+        if ( ccAttr == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "No special CompositeCacheAttributes class defined for key [" + attrName
+                    + "], using default class." );
+            }
+
+            ICompositeCacheAttributes ccAttr2 = compositeCacheManager.getDefaultCacheAttributes();
+            ccAttr = ccAttr2.copy();
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Parsing options for '" + attrName + "'" );
+        }
+
+        PropertySetter.setProperties( ccAttr, props, attrName + "." );
+        ccAttr.setCacheName( regName );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "End of parsing for \"" + attrName + "\"." );
+        }
+
+        // GET CACHE FROM FACTORY WITH ATTRIBUTES
+        ccAttr.setCacheName( regName );
+        return ccAttr;
+    }
+
+    /**
+     * Create the element attributes from the properties object for a cache region.
+     *<p>
+     * @param props
+     * @param regName
+     * @param regionPrefix
+     * @return IElementAttributes
+     */
+    protected IElementAttributes parseElementAttributes( Properties props, String regName, String regionPrefix )
+    {
+        IElementAttributes eAttr;
+
+        String attrName = regionPrefix + regName + CompositeCacheConfigurator.ELEMENT_ATTRIBUTE_PREFIX;
+
+        // auxFactory was not previously initialized.
+        // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX;
+        eAttr = OptionConverter.instantiateByKey( props, attrName, null );
+        if ( eAttr == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "No special ElementAttribute class defined for key [" + attrName + "], using default class." );
+            }
+
+            IElementAttributes eAttr2 = compositeCacheManager.getDefaultElementAttributes();
+            eAttr = eAttr2.copy();
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Parsing options for '" + attrName + "'" );
+        }
+
+        PropertySetter.setProperties( eAttr, props, attrName + "." );
+        // eAttr.setCacheName( regName );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "End of parsing for \"" + attrName + "\"." );
+        }
+
+        // GET CACHE FROM FACTORY WITH ATTRIBUTES
+        // eAttr.setCacheName( regName );
+        return eAttr;
+    }
+
+    /**
+     * Get an aux cache for the listed aux for a region.
+     *<p>
+     * @param props the configuration properties
+     * @param auxName the name of the auxiliary cache
+     * @param regName the name of the region.
+     * @return AuxiliaryCache
+     */
+    protected <K, V> AuxiliaryCache<K, V> parseAuxiliary( Properties props, String auxName, String regName )
+    {
+        AuxiliaryCache<K, V> auxCache;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "parseAuxiliary " + auxName );
+        }
+
+        // GET FACTORY
+        AuxiliaryCacheFactory auxFac = compositeCacheManager.registryFacGet( auxName );
+        if ( auxFac == null )
+        {
+            // auxFactory was not previously initialized.
+            String prefix = AUXILIARY_PREFIX + auxName;
+            auxFac = OptionConverter.instantiateByKey( props, prefix, null );
+            if ( auxFac == null )
+            {
+                log.error( "Could not instantiate auxFactory named \"" + auxName + "\"." );
+                return null;
+            }
+
+            auxFac.setName( auxName );
+
+            compositeCacheManager.registryFacPut( auxFac );
+        }
+
+        // GET ATTRIBUTES
+        AuxiliaryCacheAttributes auxAttr = compositeCacheManager.registryAttrGet( auxName );
+        String attrName = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX;
+        if ( auxAttr == null )
+        {
+            // auxFactory was not previously initialized.
+            String prefix = AUXILIARY_PREFIX + auxName + ATTRIBUTE_PREFIX;
+            auxAttr = OptionConverter.instantiateByKey( props, prefix, null );
+            if ( auxAttr == null )
+            {
+                log.error( "Could not instantiate auxAttr named '" + attrName + "'" );
+                return null;
+            }
+            auxAttr.setName( auxName );
+            compositeCacheManager.registryAttrPut( auxAttr );
+        }
+
+        auxAttr = auxAttr.copy();
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Parsing options for '" + attrName + "'" );
+        }
+
+        PropertySetter.setProperties( auxAttr, props, attrName + "." );
+        auxAttr.setCacheName( regName );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "End of parsing for '" + attrName + "'" );
+        }
+
+        // GET CACHE FROM FACTORY WITH ATTRIBUTES
+        auxAttr.setCacheName( regName );
+
+        String auxPrefix = AUXILIARY_PREFIX + auxName;
+
+        // CONFIGURE THE EVENT LOGGER
+        ICacheEventLogger cacheEventLogger = AuxiliaryCacheConfigurator.parseCacheEventLogger( props, auxPrefix );
+
+        // CONFIGURE THE ELEMENT SERIALIZER
+        IElementSerializer elementSerializer = AuxiliaryCacheConfigurator.parseElementSerializer( props, auxPrefix );
+
+        // CONFIGURE THE KEYMATCHER
+        //IKeyMatcher keyMatcher = parseKeyMatcher( props, auxPrefix );
+        // TODO add to factory interface
+
+        // Consider putting the compositeCache back in the factory interface
+        // since the manager may not know about it at this point.
+        // need to make sure the manager already has the cache
+        // before the auxiliary is created.
+        auxCache = auxFac.createCache( auxAttr, compositeCacheManager, cacheEventLogger, elementSerializer );
+
+        return auxCache;
+    }
+
+    /**
+     * Creates a custom key matcher if one is defined.  Else, it uses the default.
+     * <p>
+     * @param props
+     * @param auxPrefix - ex. AUXILIARY_PREFIX + auxName
+     * @return IKeyMatcher
+     */
+    public static <K> IKeyMatcher<K> parseKeyMatcher( Properties props, String auxPrefix )
+    {
+
+        // auxFactory was not previously initialized.
+        String keyMatcherClassName = auxPrefix + KEY_MATCHER_PREFIX;
+        IKeyMatcher<K> keyMatcher = OptionConverter.instantiateByKey( props, keyMatcherClassName, null );
+        if ( keyMatcher != null )
+        {
+            String attributePrefix = auxPrefix + KEY_MATCHER_PREFIX + ATTRIBUTE_PREFIX;
+            PropertySetter.setProperties( keyMatcher, props, attributePrefix + "." );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Using custom key matcher [" + keyMatcher + "] for auxiliary [" + auxPrefix
+                    + "]" );
+            }
+        }
+        else
+        {
+            // use the default standard serializer
+            keyMatcher = new KeyMatcherPatternImpl<K>();
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Using standard key matcher [" + keyMatcher + "] for auxiliary [" + auxPrefix + "]" );
+            }
+        }
+        return keyMatcher;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheManager.java
new file mode 100644
index 0000000..919bf73
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/CompositeCacheManager.java
@@ -0,0 +1,978 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.admin.JCSAdminBean;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheType.CacheType;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IProvideScheduler;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.engine.control.event.ElementEventQueue;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventQueue;
+import org.apache.commons.jcs.engine.stats.CacheStats;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.jcs.utils.threadpool.ThreadPoolManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * Manages a composite cache. This provides access to caches and is the primary way to shutdown the
+ * caching system as a whole.
+ * <p>
+ * The composite cache manager is responsible for creating / configuring cache regions. It serves as
+ * a factory for the ComositeCache class. The CompositeCache is the core of JCS, the hub for various
+ * auxiliaries.
+ */
+public class CompositeCacheManager
+    implements IRemoteCacheConstants, ICompositeCacheManager, IProvideScheduler
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( CompositeCacheManager.class );
+
+    /** JMX object name */
+    public static final String JMX_OBJECT_NAME = "org.apache.commons.jcs:type=JCSAdminBean";
+
+    /** Caches managed by this cache manager */
+    private final Map<String, ICache<?, ?>> caches =
+        new ConcurrentHashMap<String, ICache<?, ?>>();
+
+    /** Number of clients accessing this cache manager */
+    private int clients;
+
+    /** Default cache attributes for this cache manager */
+    private ICompositeCacheAttributes defaultCacheAttr = new CompositeCacheAttributes();
+
+    /** Default element attributes for this cache manager */
+    private IElementAttributes defaultElementAttr = new ElementAttributes();
+
+    /** Used to keep track of configured auxiliaries */
+    private final Map<String, AuxiliaryCacheFactory> auxiliaryFactoryRegistry =
+        new ConcurrentHashMap<String, AuxiliaryCacheFactory>( 11 );
+
+    /** Used to keep track of attributes for auxiliaries. */
+    private final Map<String, AuxiliaryCacheAttributes> auxiliaryAttributeRegistry =
+        new ConcurrentHashMap<String, AuxiliaryCacheAttributes>( 11 );
+
+    /** Properties with which this manager was configured. This is exposed for other managers. */
+    private Properties configurationProperties;
+
+    /** The default auxiliary caches to be used if not preconfigured */
+    private String defaultAuxValues;
+
+    /** The Singleton Instance */
+    private static CompositeCacheManager instance;
+
+    /** The prefix of relevant system properties */
+    private static final String SYSTEM_PROPERTY_KEY_PREFIX = "jcs";
+
+    /** Should we use system property substitutions. */
+    private static final boolean DEFAULT_USE_SYSTEM_PROPERTIES = true;
+
+    /** Once configured, you can force a reconfiguration of sorts. */
+    private static final boolean DEFAULT_FORCE_RECONFIGURATION = false;
+
+    /** Those waiting for notification of a shutdown. */
+    private final LinkedHashSet<IShutdownObserver> shutdownObservers = new LinkedHashSet<IShutdownObserver>();
+
+    /** The central background scheduler. */
+    private ScheduledExecutorService scheduledExecutor;
+
+    /** The central event queue. */
+    private IElementEventQueue elementEventQueue;
+
+    /** Shutdown hook thread instance */
+    private ShutdownHook shutdownHook;
+
+    /** Indicates whether the instance has been initialized. */
+    private boolean isInitialized = false;
+
+    /** Indicates whether configure has been called. */
+    private boolean isConfigured = false;
+
+    /** Indicates whether JMX bean has been registered. */
+    private boolean isJMXRegistered = false;
+
+    private String jmxName = JMX_OBJECT_NAME;
+
+    /**
+     * Gets the CacheHub instance. For backward compatibility, if this creates the instance it will
+     * attempt to configure it with the default configuration. If you want to configure from your
+     * own source, use {@link #getUnconfiguredInstance}and then call {@link #configure}
+     * <p>
+     * @return CompositeCacheManager
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    public static synchronized CompositeCacheManager getInstance() throws CacheException
+    {
+        return getInstance( CacheConstants.DEFAULT_CONFIG );
+    }
+
+    /**
+     * Initializes the cache manager using the props file for the given name.
+     * <p>
+     * @param propsFilename
+     * @return CompositeCacheManager configured from the give propsFileName
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    public static synchronized CompositeCacheManager getInstance( String propsFilename ) throws CacheException
+    {
+        if ( instance == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Instance is null, creating with config [" + propsFilename + "]" );
+            }
+
+            instance = createInstance();
+        }
+
+        if (!instance.isInitialized())
+        {
+            instance.initialize();
+        }
+
+        if (!instance.isConfigured())
+        {
+            instance.configure( propsFilename );
+        }
+
+        instance.incrementClients();
+
+        return instance;
+    }
+
+    /**
+     * Get a CacheHub instance which is not configured. If an instance already exists, it will be
+     * returned.
+     *<p>
+     * @return CompositeCacheManager
+     */
+    public static synchronized CompositeCacheManager getUnconfiguredInstance()
+    {
+        if ( instance == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Instance is null, returning unconfigured instance" );
+            }
+
+            instance = createInstance();
+        }
+
+        if (!instance.isInitialized())
+        {
+            instance.initialize();
+        }
+
+        instance.incrementClients();
+
+        return instance;
+    }
+
+    /**
+     * Simple factory method, must override in subclasses so getInstance creates / returns the
+     * correct object.
+     * <p>
+     * @return CompositeCacheManager
+     */
+    protected static CompositeCacheManager createInstance()
+    {
+        return new CompositeCacheManager();
+    }
+
+    /**
+     * Default constructor
+     */
+    protected CompositeCacheManager()
+    {
+        // empty
+    }
+
+    /** Creates a shutdown hook and starts the scheduler service */
+    protected void initialize()
+    {
+        if (!isInitialized)
+        {
+            this.shutdownHook = new ShutdownHook();
+            try
+            {
+                Runtime.getRuntime().addShutdownHook( shutdownHook );
+            }
+            catch ( AccessControlException e )
+            {
+                log.error( "Could not register shutdown hook.", e );
+            }
+
+            this.scheduledExecutor = Executors.newScheduledThreadPool(4,
+                    new DaemonThreadFactory("JCS-Scheduler-", Thread.MIN_PRIORITY));
+
+            // Register JMX bean
+            if (!isJMXRegistered && jmxName != null)
+            {
+                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+                JCSAdminBean adminBean = new JCSAdminBean(this);
+                try
+                {
+                    ObjectName jmxObjectName = new ObjectName(jmxName);
+                    mbs.registerMBean(adminBean, jmxObjectName);
+                    isJMXRegistered = true;
+                }
+                catch (Exception e)
+                {
+                    log.warn( "Could not register JMX bean.", e );
+                }
+            }
+
+            this.elementEventQueue = new ElementEventQueue();
+
+            isInitialized = true;
+        }
+    }
+
+    /**
+     * Get the element event queue
+     *
+     * @return the elementEventQueue
+     */
+    public IElementEventQueue getElementEventQueue()
+    {
+        return elementEventQueue;
+    }
+
+    /**
+     * Get the scheduler service
+     *
+     * @return the scheduledExecutor
+     */
+    @Override
+    public ScheduledExecutorService getScheduledExecutorService()
+    {
+        return scheduledExecutor;
+    }
+
+    /**
+     * Configure with default properties file
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    public void configure() throws CacheException
+    {
+        configure( CacheConstants.DEFAULT_CONFIG );
+    }
+
+    /**
+     * Configure from specific properties file.
+     * <p>
+     * @param propFile Path <u>within classpath </u> to load configuration from
+     * @throws CacheException if the configuration cannot be loaded
+     */
+    public void configure( String propFile ) throws CacheException
+    {
+        log.info( "Creating cache manager from config file: " + propFile );
+
+        Properties props = new Properties();
+
+        InputStream is = getClass().getResourceAsStream( propFile );
+
+        if ( is != null )
+        {
+            try
+            {
+                props.load( is );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "File [" + propFile + "] contained " + props.size() + " properties" );
+                }
+            }
+            catch ( IOException ex )
+            {
+                throw new CacheException("Failed to load properties for name [" + propFile + "]", ex);
+            }
+            finally
+            {
+                try
+                {
+                    is.close();
+                }
+                catch ( IOException ignore )
+                {
+                    // Ignored
+                }
+            }
+        }
+        else
+        {
+            throw new CacheException( "Failed to read configuration file [" + propFile + "]" );
+        }
+
+        configure( props );
+    }
+
+    /**
+     * Configure from properties object.
+     * <p>
+     * This method will call configure, instructing it to use system properties as a default.
+     * @param props
+     */
+    public void configure( Properties props )
+    {
+        configure( props, DEFAULT_USE_SYSTEM_PROPERTIES );
+    }
+
+    /**
+     * Configure from properties object, overriding with values from the system properties if
+     * instructed.
+     * <p>
+     * You can override a specific value by passing in a system property:
+     * <p>
+     * For example, you could override this value in the cache.ccf file by starting up your program
+     * with the argument: -Djcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+     * <p>
+     * @param props
+     * @param useSystemProperties -- if true, values starting with jcs will be put into the props
+     *            file prior to configuring the cache.
+     */
+    public void configure( Properties props, boolean useSystemProperties )
+    {
+        configure( props, useSystemProperties, DEFAULT_FORCE_RECONFIGURATION );
+    }
+
+    /**
+     * Configure from properties object, overriding with values from the system properties if
+     * instructed.
+     * <p>
+     * You can override a specific value by passing in a system property:
+     * <p>
+     * For example, you could override this value in the cache.ccf file by starting up your program
+     * with the argument: -Djcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+     * <p>
+     * @param props
+     * @param useSystemProperties -- if true, values starting with jcs will be put into the props
+     *            file prior to configuring the cache.
+     * @param forceReconfiguration - if the manager is already configured, we will try again. This
+     *            may not work properly.
+     */
+    public synchronized void configure( Properties props, boolean useSystemProperties, boolean forceReconfiguration )
+    {
+        if ( props == null )
+        {
+            log.error( "No properties found.  Please configure the cache correctly." );
+            return;
+        }
+
+        if ( isConfigured )
+        {
+            if ( !forceReconfiguration )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Configure called after the manager has been configured.  "
+                        + "Force reconfiguration is false.  Doing nothing" );
+                }
+                return;
+            }
+            else
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Configure called after the manager has been configured.  "
+                        + "Force reconfiguration is true.  Reconfiguring as best we can." );
+                }
+            }
+        }
+        if ( useSystemProperties )
+        {
+            overrideWithSystemProperties( props );
+        }
+        doConfigure( props );
+    }
+
+    /**
+     * Any property values will be replaced with system property values that match the key.
+     * <p>
+     * TODO move to a utility.
+     * <p>
+     * @param props
+     */
+    private static void overrideWithSystemProperties( Properties props )
+    {
+        // override any setting with values from the system properties.
+        Properties sysProps = System.getProperties();
+        Set<Object> keys = sysProps.keySet();
+        Iterator<Object> keyIt = keys.iterator();
+        while ( keyIt.hasNext() )
+        {
+            String key = (String) keyIt.next();
+            if ( key.startsWith( SYSTEM_PROPERTY_KEY_PREFIX ) )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Using system property [[" + key + "] [" + sysProps.getProperty( key ) + "]]" );
+                }
+                props.put( key, sysProps.getProperty( key ) );
+            }
+        }
+    }
+
+    /**
+     * Configure the cache using the supplied properties.
+     * <p>
+     * @param props assumed not null
+     */
+    private void doConfigure( Properties props )
+    {
+        // We will expose this for managers that need raw properties.
+        this.configurationProperties = props;
+
+        // set the props value and then configure the ThreadPoolManager
+        ThreadPoolManager.setProps( props );
+        ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "ThreadPoolManager = " + poolMgr );
+        }
+
+        // configure the cache
+        CompositeCacheConfigurator configurator = new CompositeCacheConfigurator( this );
+
+        configurator.doConfigure( props );
+
+        isConfigured = true;
+    }
+
+    /**
+     * Gets the defaultCacheAttributes attribute of the CacheHub object
+     * <p>
+     * @return The defaultCacheAttributes value
+     */
+    public ICompositeCacheAttributes getDefaultCacheAttributes()
+    {
+        return this.defaultCacheAttr.copy();
+    }
+
+    /**
+     * Sets the defaultCacheAttributes attribute of the CacheHub object
+     * <p>
+     * @param icca The new defaultCacheAttributes value
+     */
+    public void setDefaultCacheAttributes( ICompositeCacheAttributes icca )
+    {
+        this.defaultCacheAttr = icca;
+    }
+
+    /**
+     * Sets the defaultElementAttributes attribute of the CacheHub object
+     * <p>
+     * @param iea The new defaultElementAttributes value
+     */
+    public void setDefaultElementAttributes( IElementAttributes iea )
+    {
+        this.defaultElementAttr = iea;
+    }
+
+    /**
+     * Gets the defaultElementAttributes attribute of the CacheHub object
+     * <p>
+     * @return The defaultElementAttributes value
+     */
+    public IElementAttributes getDefaultElementAttributes()
+    {
+        return this.defaultElementAttr.copy();
+    }
+
+    /**
+     * Gets the cache attribute of the CacheHub object
+     * <p>
+     * @param cacheName
+     * @return CompositeCache -- the cache region controller
+     */
+    @Override
+    public <K, V> CompositeCache<K, V>  getCache( String cacheName )
+    {
+        return getCache( cacheName, this.defaultCacheAttr.copy() );
+    }
+
+    /**
+     * Gets the cache attribute of the CacheHub object
+     * <p>
+     * @param cacheName
+     * @param cattr
+     * @return CompositeCache
+     */
+    public <K, V> CompositeCache<K, V> getCache( String cacheName, ICompositeCacheAttributes cattr )
+    {
+        cattr.setCacheName( cacheName );
+        return getCache( cattr, this.defaultElementAttr );
+    }
+
+    /**
+     * Gets the cache attribute of the CacheHub object
+     * <p>
+     * @param cacheName
+     * @param cattr
+     * @param attr
+     * @return CompositeCache
+     */
+    public <K, V> CompositeCache<K, V>  getCache( String cacheName, ICompositeCacheAttributes cattr, IElementAttributes attr )
+    {
+        cattr.setCacheName( cacheName );
+        return getCache( cattr, attr );
+    }
+
+    /**
+     * Gets the cache attribute of the CacheHub object
+     * <p>
+     * @param cattr
+     * @return CompositeCache
+     */
+    public <K, V> CompositeCache<K, V>  getCache( ICompositeCacheAttributes cattr )
+    {
+        return getCache( cattr, this.defaultElementAttr );
+    }
+
+    /**
+     * If the cache has already been created, then the CacheAttributes and the element Attributes
+     * will be ignored. Currently there is no overriding the CacheAttributes once it is set up. You
+     * can change the default ElementAttributes for a region later.
+     * <p>
+     * Overriding the default elemental attributes will require changing the way the attributes are
+     * assigned to elements. Get cache creates a cache with defaults if none are specified. We might
+     * want to create separate method for creating/getting. . .
+     * <p>
+     * @param cattr
+     * @param attr
+     * @return CompositeCache
+     */
+    @SuppressWarnings("unchecked") // Need to cast because of common map for all caches
+    public <K, V> CompositeCache<K, V>  getCache( ICompositeCacheAttributes cattr, IElementAttributes attr )
+    {
+        CompositeCache<K, V> cache;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "attr = " + attr );
+        }
+
+        synchronized ( caches )
+        {
+            cache = (CompositeCache<K, V>) caches.get( cattr.getCacheName() );
+            if ( cache == null )
+            {
+                cattr.setCacheName( cattr.getCacheName() );
+
+                CompositeCacheConfigurator configurator = new CompositeCacheConfigurator( this );
+
+                cache = configurator.parseRegion( this.getConfigurationProperties(), cattr.getCacheName(),
+                                                  this.defaultAuxValues, cattr );
+
+                caches.put( cattr.getCacheName(), cache );
+            }
+        }
+
+        return cache;
+    }
+
+    /**
+     * @param name
+     */
+    public void freeCache( String name )
+    {
+        freeCache( name, false );
+    }
+
+    /**
+     * @param name
+     * @param fromRemote
+     */
+    public void freeCache( String name, boolean fromRemote )
+    {
+        CompositeCache<?, ?> cache = (CompositeCache<?, ?>) caches.remove( name );
+
+        if ( cache != null )
+        {
+            cache.dispose( fromRemote );
+        }
+    }
+
+    /**
+     * Calls freeCache on all regions
+     */
+    public void shutDown()
+    {
+        synchronized (CompositeCacheManager.class)
+        {
+            // shutdown element event queue
+            this.elementEventQueue.dispose();
+
+            // shutdown all scheduled jobs
+            this.scheduledExecutor.shutdownNow();
+
+            // notify any observers
+            synchronized ( shutdownObservers )
+            {
+                // We don't need to worry about locking the set.
+                // since this is a shutdown command, nor do we need
+                // to queue these up.
+                for (IShutdownObserver observer : shutdownObservers)
+                {
+                    observer.shutdown();
+                }
+            }
+
+            // Unregister JMX bean
+            if (isJMXRegistered)
+            {
+                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+                try
+                {
+                    ObjectName jmxObjectName = new ObjectName(jmxName);
+                    mbs.unregisterMBean(jmxObjectName);
+                }
+                catch (Exception e)
+                {
+                    log.warn( "Could not unregister JMX bean.", e );
+                }
+
+                isJMXRegistered = false;
+            }
+
+            // do the traditional shutdown of the regions.
+            for (String name : getCacheNames())
+            {
+                freeCache( name );
+            }
+
+            if (shutdownHook != null)
+            {
+                try
+                {
+                    Runtime.getRuntime().removeShutdownHook(shutdownHook);
+                }
+                catch (IllegalStateException e)
+                {
+                    // May fail if the JVM is already shutting down
+                }
+
+                this.shutdownHook = null;
+            }
+
+            isConfigured = false;
+            isInitialized = false;
+        }
+    }
+
+    /** */
+    private void incrementClients()
+    {
+        clients++;
+    }
+
+    /** */
+    public void release()
+    {
+        release( false );
+    }
+
+    /**
+     * @param fromRemote
+     */
+    private void release( boolean fromRemote )
+    {
+        synchronized ( CompositeCacheManager.class )
+        {
+            // Wait until called by the last client
+            if ( --clients > 0 )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Release called, but " + clients + " remain" );
+                    return;
+                }
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Last client called release. There are " + caches.size() + " caches which will be disposed" );
+            }
+
+            for (ICache<?, ?> c : caches.values() )
+            {
+                CompositeCache<?, ?> cache = (CompositeCache<?, ?>) c;
+
+                if ( cache != null )
+                {
+                    cache.dispose( fromRemote );
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns a list of the current cache names.
+     * @return String[]
+     */
+    public String[] getCacheNames()
+    {
+        return caches.keySet().toArray(new String[caches.size()]);
+    }
+
+    /**
+     * @return ICacheType.CACHE_HUB
+     */
+    public CacheType getCacheType()
+    {
+        return CacheType.CACHE_HUB;
+    }
+
+    /**
+     * @return ICompositeCacheAttributes
+     */
+    public ICompositeCacheAttributes getDefaultCattr()
+    {
+        return this.defaultCacheAttr;
+    }
+
+    /**
+     * @param auxFac
+     */
+    void registryFacPut( AuxiliaryCacheFactory auxFac )
+    {
+        auxiliaryFactoryRegistry.put( auxFac.getName(), auxFac );
+    }
+
+    /**
+     * @param name
+     * @return AuxiliaryCacheFactory
+     */
+    AuxiliaryCacheFactory registryFacGet( String name )
+    {
+        return auxiliaryFactoryRegistry.get( name );
+    }
+
+    /**
+     * @param auxAttr
+     */
+    void registryAttrPut( AuxiliaryCacheAttributes auxAttr )
+    {
+        auxiliaryAttributeRegistry.put( auxAttr.getName(), auxAttr );
+    }
+
+    /**
+     * @param name
+     * @return AuxiliaryCacheAttributes
+     */
+    AuxiliaryCacheAttributes registryAttrGet( String name )
+    {
+        return auxiliaryAttributeRegistry.get( name );
+    }
+
+    /**
+     * Add a cache to the map of registered caches
+     *
+     * @param cacheName the region name
+     * @param cache the cache instance
+     */
+    void addCache(String cacheName, ICache<?, ?> cache)
+    {
+        caches.put(cacheName, cache);
+    }
+
+    /**
+     * @param defaultAuxValues the defaultAuxValues to set
+     */
+    void setDefaultAuxValues(String defaultAuxValues)
+    {
+        this.defaultAuxValues = defaultAuxValues;
+    }
+
+    /**
+     * Gets stats for debugging. This calls gets statistics and then puts all the results in a
+     * string. This returns data for all regions.
+     * <p>
+     * @return String
+     */
+    @Override
+    public String getStats()
+    {
+        ICacheStats[] stats = getStatistics();
+        if ( stats == null )
+        {
+            return "NONE";
+        }
+
+        // force the array elements into a string.
+        StringBuilder buf = new StringBuilder();
+        int statsLen = stats.length;
+        for ( int i = 0; i < statsLen; i++ )
+        {
+            buf.append( "\n---------------------------\n" );
+            buf.append( stats[i] );
+        }
+        return buf.toString();
+    }
+
+    /**
+     * This returns data gathered for all regions and all the auxiliaries they currently uses.
+     * <p>
+     * @return ICacheStats[]
+     */
+    public ICacheStats[] getStatistics()
+    {
+        ArrayList<ICacheStats> cacheStats = new ArrayList<ICacheStats>();
+        for (ICache<?, ?> c :  caches.values())
+        {
+            CompositeCache<?, ?> cache = (CompositeCache<?, ?>) c;
+            if ( cache != null )
+            {
+                cacheStats.add( cache.getStatistics() );
+            }
+        }
+        ICacheStats[] stats = cacheStats.toArray( new CacheStats[0] );
+        return stats;
+    }
+
+    /**
+     * Perhaps the composite cache itself should be the observable object. It doesn't make much of a
+     * difference. There are some problems with region by region shutdown. Some auxiliaries are
+     * global. They will need to track when every region has shutdown before doing things like
+     * closing the socket with a lateral.
+     * <p>
+     * @param observer
+     */
+    @Override
+    public void registerShutdownObserver( IShutdownObserver observer )
+    {
+        // synchronized to take care of iteration safety
+        // during shutdown.
+        synchronized ( shutdownObservers )
+        {
+            // the set will take care of duplication protection
+            shutdownObservers.add( observer );
+        }
+    }
+
+    /**
+     * @param observer
+     */
+    @Override
+    public void deregisterShutdownObserver( IShutdownObserver observer )
+    {
+        synchronized ( shutdownObservers )
+        {
+            shutdownObservers.remove( observer );
+        }
+    }
+
+    /**
+     * This is exposed so other manager can get access to the props.
+     * <p>
+     * @param props
+     */
+    void setConfigurationProperties( Properties props )
+    {
+        this.configurationProperties = props;
+    }
+
+    /**
+     * This is exposed so other manager can get access to the props.
+     * <p>
+     * @return the configurationProperties
+     */
+    @Override
+    public Properties getConfigurationProperties()
+    {
+        return configurationProperties;
+    }
+
+    /**
+     * @return the isInitialized
+     */
+    public boolean isInitialized()
+    {
+        return isInitialized;
+    }
+
+    /**
+     * @return the isConfigured
+     */
+    public boolean isConfigured()
+    {
+        return isConfigured;
+    }
+
+    public void setJmxName(final String name) {
+        if (isJMXRegistered)
+        {
+            throw new IllegalStateException("Too late, MBean registration is done");
+        }
+        jmxName = name;
+    }
+
+    /**
+     * Called on shutdown. This gives use a chance to store the keys and to optimize even if the
+     * cache manager's shutdown method was not called manually.
+     */
+    class ShutdownHook
+        extends Thread
+    {
+        /**
+         * This will persist the keys on shutdown.
+         * <p>
+         * @see java.lang.Thread#run()
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            if ( isInitialized() )
+            {
+                log.info( "Shutdown hook activated.  Shutdown was not called.  Shutting down JCS." );
+                shutDown();
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEvent.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEvent.java
new file mode 100644
index 0000000..685c3bd
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEvent.java
@@ -0,0 +1,63 @@
+package org.apache.commons.jcs.engine.control.event;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.event.behavior.ElementEventType;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+
+import java.util.EventObject;
+
+/**
+ * Element events will trigger the creation of Element Event objects. This is a wrapper around the
+ * cache element that indicates the event triggered.
+ */
+public class ElementEvent
+    extends EventObject
+    implements IElementEvent
+{
+    /** Don't change */
+    private static final long serialVersionUID = -5364117411457467056L;
+
+    /** default event code */
+    private ElementEventType elementEvent = ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND;
+
+    /**
+     * Constructor for the ElementEvent object
+     * <p>
+     * @param source The Cache Element (should restrict?)
+     * @param elementEvent The event id defined in the enum class.
+     */
+    public ElementEvent( Object source, ElementEventType elementEvent )
+    {
+        super( source );
+        this.elementEvent = elementEvent;
+    }
+
+    /**
+     * Gets the elementEvent attribute of the ElementEvent object
+     * <p>
+     * @return The elementEvent value. The List of values is defined in ElementEventType.
+     */
+    @Override
+    public ElementEventType getElementEvent()
+    {
+        return elementEvent;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEventQueue.java
new file mode 100644
index 0000000..ae0c21f
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/ElementEventQueue.java
@@ -0,0 +1,203 @@
+package org.apache.commons.jcs.engine.control.event;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventQueue;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An event queue is used to propagate ordered cache events to one and only one target listener.
+ */
+public class ElementEventQueue
+    implements IElementEventQueue
+{
+    private static final String THREAD_PREFIX = "JCS-ElementEventQueue-";
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( ElementEventQueue.class );
+
+    /** shutdown or not */
+    private boolean destroyed = false;
+
+    /** The event queue */
+    private LinkedBlockingQueue<Runnable> queue;
+
+    /** The worker thread pool. */
+    private ThreadPoolExecutor queueProcessor;
+
+    /**
+     * Constructor for the ElementEventQueue object
+     */
+    public ElementEventQueue()
+    {
+        queue = new LinkedBlockingQueue<Runnable>();
+        queueProcessor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
+                queue, new DaemonThreadFactory(THREAD_PREFIX));
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Constructed: " + this );
+        }
+    }
+
+    /**
+     * Dispose queue
+     */
+    @Override
+    public void dispose()
+    {
+        if ( !destroyed )
+        {
+            destroyed = true;
+
+            // synchronize on queue so the thread will not wait forever,
+            // and then interrupt the QueueProcessor
+            queueProcessor.shutdownNow();
+            queueProcessor = null;
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Element event queue destroyed: " + this );
+            }
+        }
+    }
+
+    /**
+     * Adds an ElementEvent to be handled
+     * @param hand The IElementEventHandler
+     * @param event The IElementEventHandler IElementEvent event
+     * @throws IOException
+     */
+    @Override
+    public void addElementEvent( IElementEventHandler hand, IElementEvent event )
+        throws IOException
+    {
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Adding Event Handler to QUEUE, !destroyed = " + !destroyed );
+        }
+
+        if (destroyed)
+        {
+            log.warn("Event submitted to disposed element event queue " + event);
+        }
+        else
+        {
+            ElementEventRunner runner = new ElementEventRunner( hand, event );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "runner = " + runner );
+            }
+
+            queueProcessor.execute(runner);
+        }
+    }
+
+    // /////////////////////////// Inner classes /////////////////////////////
+
+    /**
+     * Retries before declaring failure.
+     */
+    protected abstract class AbstractElementEventRunner
+        implements Runnable
+    {
+        /**
+         * Main processing method for the AbstractElementEvent object
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            try
+            {
+                doRun();
+                // happy and done.
+            }
+            catch ( IOException e )
+            {
+                // Too bad. The handler has problems.
+                log.warn( "Giving up element event handling " + ElementEventQueue.this, e );
+            }
+        }
+
+        /**
+         * This will do the work or trigger the work to be done.
+         * <p>
+         * @throws IOException
+         */
+        protected abstract void doRun()
+            throws IOException;
+    }
+
+    /**
+     * ElementEventRunner.
+     */
+    private class ElementEventRunner
+        extends AbstractElementEventRunner
+    {
+        /** the handler */
+        private final IElementEventHandler hand;
+
+        /** event */
+        private final IElementEvent event;
+
+        /**
+         * Constructor for the PutEvent object.
+         * <p>
+         * @param hand
+         * @param event
+         * @throws IOException
+         */
+        @SuppressWarnings("synthetic-access")
+        ElementEventRunner( IElementEventHandler hand, IElementEvent event )
+            throws IOException
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Constructing " + this );
+            }
+            this.hand = hand;
+            this.event = event;
+        }
+
+        /**
+         * Tells the handler to handle the event.
+         * <p>
+         * @throws IOException
+         */
+        @Override
+        protected void doRun()
+            throws IOException
+        {
+            hand.handleElementEvent( event );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/ElementEventType.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/ElementEventType.java
new file mode 100644
index 0000000..52b0fe8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/ElementEventType.java
@@ -0,0 +1,54 @@
+package org.apache.commons.jcs.engine.control.event.behavior;
+
+/*
+ * 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 describes the events that an item can encounter.
+ */
+public enum ElementEventType
+{
+    /** Background expiration */
+    EXCEEDED_MAXLIFE_BACKGROUND,
+
+    /*** Expiration discovered on request */
+    EXCEEDED_MAXLIFE_ONREQUEST,
+
+    /** Background expiration */
+    EXCEEDED_IDLETIME_BACKGROUND,
+
+    /** Expiration discovered on request */
+    EXCEEDED_IDLETIME_ONREQUEST,
+
+    /** Moving from memory to disk (what if no disk?) */
+    SPOOLED_DISK_AVAILABLE,
+
+    /** Moving from memory to disk (what if no disk?) */
+    SPOOLED_DISK_NOT_AVAILABLE,
+
+    /** Moving from memory to disk, but item is not spoolable */
+    SPOOLED_NOT_ALLOWED //,
+
+    /** Removed actively by a remove command. (Could distinguish between local and remote) */
+    //REMOVED,
+    /**
+     * Element was requested from cache. Not sure we ever want to implement this.
+     */
+    //GET
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEvent.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEvent.java
new file mode 100644
index 0000000..08a8bcb
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEvent.java
@@ -0,0 +1,42 @@
+package org.apache.commons.jcs.engine.control.event.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Defines how an element event object should behave.
+ */
+public interface IElementEvent
+    extends Serializable
+{
+    /**
+     * Gets the elementEvent attribute of the IElementEvent object. This code is Contained in the
+     * IElememtEventConstants class.
+     *<p>
+     * @return The elementEvent value
+     */
+    ElementEventType getElementEvent();
+
+    /**
+     * @return the source of the event.
+     */
+    Object getSource();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventHandler.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventHandler.java
new file mode 100644
index 0000000..e7829e7
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventHandler.java
@@ -0,0 +1,40 @@
+package org.apache.commons.jcs.engine.control.event.behavior;
+
+/*
+ * 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 interface defines the behavior for event handler. Event handlers are
+ * transient. They are not replicated and are not written to disk.
+ * <p>
+ * If you want an event handler by default for all elements in a region, then
+ * you can add it to the default element attributes. This way it will get created
+ * whenever an item gets put into the cache.
+ *
+ */
+public interface IElementEventHandler
+{
+    /**
+     * Handle events for this element. The events are typed.
+     *
+     * @param event
+     *            The event created by the cache.
+     */
+    void handleElementEvent( IElementEvent event );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventQueue.java
new file mode 100644
index 0000000..c5279e2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/event/behavior/IElementEventQueue.java
@@ -0,0 +1,48 @@
+package org.apache.commons.jcs.engine.control.event.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+/**
+ * Interface for an element event queue. An event queue is used to propagate
+ * ordered element events in one region.
+ *
+ */
+public interface IElementEventQueue
+{
+    /**
+     * Adds an ElementEvent to be handled
+     *
+     * @param hand
+     *            The IElementEventHandler
+     * @param event
+     *            The IElementEventHandler IElementEvent event
+     * @throws IOException
+     */
+    void addElementEvent( IElementEventHandler hand, IElementEvent event )
+        throws IOException;
+
+    /**
+     * Destroy the event queue
+     *
+     */
+    void dispose();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupAttrName.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupAttrName.java
new file mode 100644
index 0000000..8da08eb
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupAttrName.java
@@ -0,0 +1,117 @@
+package org.apache.commons.jcs.engine.control.group;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Description of the Class
+ */
+public class GroupAttrName<T>
+    implements Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = 1586079686300744198L;
+
+    /** Description of the Field */
+    public final GroupId groupId;
+
+    /** the name of the attribute */
+    public final T attrName;
+
+    /** Cached toString value */
+    private String toString;
+
+    /**
+     * Constructor for the GroupAttrName object
+     * @param groupId
+     * @param attrName
+     */
+    public GroupAttrName( GroupId groupId, T attrName )
+    {
+        this.groupId = groupId;
+        this.attrName = attrName;
+
+        if ( groupId == null )
+        {
+            throw new IllegalArgumentException( "groupId must not be null." );
+        }
+    }
+
+    /**
+     * Tests object equality.
+     * @param obj The <code>GroupAttrName</code> instance to test.
+     * @return Whether equal.
+     */
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( obj == null || !( obj instanceof GroupAttrName ) )
+        {
+            return false;
+        }
+        GroupAttrName<?> to = (GroupAttrName<?>) obj;
+
+        if (groupId.equals( to.groupId ))
+        {
+            if (attrName == null && to.attrName == null)
+            {
+                return true;
+            }
+            else if (attrName == null || to.attrName == null)
+            {
+                return false;
+            }
+
+            return  attrName.equals( to.attrName );
+        }
+
+        return false;
+    }
+
+    /**
+     * @return A hash code based on the hash code of @ #groupid} and {@link #attrName}.
+     */
+    @Override
+    public int hashCode()
+    {
+        if (attrName == null)
+        {
+            return groupId.hashCode();
+        }
+
+        return groupId.hashCode() ^ attrName.hashCode();
+    }
+
+    /**
+     * @return the cached value.
+     */
+    @Override
+    public String toString()
+    {
+        if ( toString == null )
+        {
+            toString = "[GAN: groupId=" + groupId + ", attrName=" + attrName + "]";
+        }
+
+        return toString;
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupId.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupId.java
new file mode 100644
index 0000000..7e243d5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/group/GroupId.java
@@ -0,0 +1,103 @@
+package org.apache.commons.jcs.engine.control.group;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Used to avoid name conflict when group cache items are mixed with non-group cache items in the
+ * same cache.
+ */
+public class GroupId
+    implements Serializable
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 4626368486444860133L;
+
+    /** Description of the Field */
+    public final String groupName;
+
+    /** the name of the region. */
+    public final String cacheName;
+
+    /** Cached toString value. */
+    private String toString;
+
+    /**
+     * Constructor for the GroupId object
+     * <p>
+     * @param cacheName
+     * @param groupName
+     */
+    public GroupId( String cacheName, String groupName )
+    {
+        this.cacheName = cacheName;
+        this.groupName = groupName;
+
+        if ( cacheName == null )
+        {
+            throw new IllegalArgumentException( "cacheName must not be null." );
+        }
+        if ( groupName == null )
+        {
+            throw new IllegalArgumentException( "groupName must not be null." );
+        }
+    }
+
+    /**
+     * @param obj
+     * @return cacheName.equals( g.cacheName ) && groupName.equals( g.groupName );
+     */
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( obj == null || !( obj instanceof GroupId ) )
+        {
+            return false;
+        }
+        GroupId g = (GroupId) obj;
+        return cacheName.equals( g.cacheName ) && groupName.equals( g.groupName );
+    }
+
+    /**
+     * @return cacheName.hashCode() + groupName.hashCode();
+     */
+    @Override
+    public int hashCode()
+    {
+        return cacheName.hashCode() + groupName.hashCode();
+    }
+
+    /**
+     * Caches the value.
+     * <p>
+     * @return debugging string.
+     */
+    @Override
+    public String toString()
+    {
+        if ( toString == null )
+        {
+            toString = "[groupId=" + cacheName + ", " + groupName + ']';
+        }
+
+        return toString;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/package.html
new file mode 100644
index 0000000..82b9a4c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/control/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+      The primary cache classes and the hub.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEvent.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEvent.java
new file mode 100644
index 0000000..c07ecac
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEvent.java
@@ -0,0 +1,177 @@
+package org.apache.commons.jcs.engine.logging;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+
+import java.util.Date;
+
+/** It's returned from create and passed into log. */
+public class CacheEvent<K>
+    implements ICacheEvent<K>
+{
+    /** Don't change. */
+    private static final long serialVersionUID = -5913139566421714330L;
+
+    /** The time at which this object was created. */
+    private final long createTime = System.currentTimeMillis();
+
+    /** The auxiliary or other source of the event. */
+    private String source;
+
+    /** The cache region */
+    private String region;
+
+    /** The event name: update, get, remove, etc. */
+    private String eventName;
+
+    /** disk location, ip, etc. */
+    private String optionalDetails;
+
+    /** The key that was put or retrieved. */
+    private K key;
+
+    /**
+     * @param source the source to set
+     */
+    @Override
+	public void setSource( String source )
+    {
+        this.source = source;
+    }
+
+    /**
+     * @return the source
+     */
+    @Override
+	public String getSource()
+    {
+        return source;
+    }
+
+    /**
+     * @param region the region to set
+     */
+    @Override
+	public void setRegion( String region )
+    {
+        this.region = region;
+    }
+
+    /**
+     * @return the region
+     */
+    @Override
+	public String getRegion()
+    {
+        return region;
+    }
+
+    /**
+     * @param eventName the eventName to set
+     */
+    @Override
+	public void setEventName( String eventName )
+    {
+        this.eventName = eventName;
+    }
+
+    /**
+     * @return the eventName
+     */
+    @Override
+	public String getEventName()
+    {
+        return eventName;
+    }
+
+    /**
+     * @param optionalDetails the optionalDetails to set
+     */
+    @Override
+	public void setOptionalDetails( String optionalDetails )
+    {
+        this.optionalDetails = optionalDetails;
+    }
+
+    /**
+     * @return the optionalDetails
+     */
+    @Override
+	public String getOptionalDetails()
+    {
+        return optionalDetails;
+    }
+
+    /**
+     * @param key the key to set
+     */
+    @Override
+	public void setKey( K key )
+    {
+        this.key = key;
+    }
+
+    /**
+     * @return the key
+     */
+    @Override
+	public K getKey()
+    {
+        return key;
+    }
+
+    /**
+     * The time at which this object was created.
+     * <p>
+     * @return the createTime
+     */
+    public long getCreateTime()
+    {
+        return createTime;
+    }
+
+    /**
+     * @return reflection toString
+     */
+    @Override
+    public String toString()
+    {
+    	StringBuilder sb = new StringBuilder();
+    	sb.append("CacheEvent: ").append(eventName).append(" Created: ").append(new Date(createTime));
+    	if (source != null)
+    	{
+        	sb.append(" Source: ").append(source);
+    	}
+    	if (region != null)
+    	{
+        	sb.append(" Region: ").append(region);
+    	}
+    	if (key != null)
+    	{
+        	sb.append(" Key: ").append(key);
+    	}
+    	if (optionalDetails != null)
+    	{
+        	sb.append(" Details: ").append(optionalDetails);
+    	}
+        return sb.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLogger.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLogger.java
new file mode 100644
index 0000000..e38a9c5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLogger.java
@@ -0,0 +1,113 @@
+package org.apache.commons.jcs.engine.logging;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This implementation simple logs to a commons logger at debug level, for all events. It's mainly
+ * for testing. It isn't very useful otherwise.
+ */
+public class CacheEventLoggerDebugLogger
+    implements ICacheEventLogger
+{
+    /** This is the name of the category. */
+    private String logCategoryName = CacheEventLoggerDebugLogger.class.getName();
+
+    /** The logger. This is recreated on set logCategoryName */
+    private Log log = LogFactory.getLog( logCategoryName );
+
+    /**
+     * @param source
+     * @param region
+     * @param eventName
+     * @param optionalDetails
+     * @param key
+     * @return ICacheEvent
+     */
+    @Override
+    public <T> ICacheEvent<T> createICacheEvent( String source, String region, String eventName,
+            String optionalDetails, T key )
+    {
+        ICacheEvent<T> event = new CacheEvent<T>();
+        event.setSource( source );
+        event.setRegion( region );
+        event.setEventName( eventName );
+        event.setOptionalDetails( optionalDetails );
+        event.setKey( key );
+
+        return event;
+    }
+
+    /**
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    @Override
+    public void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( source + " | " + eventName + " | " + optionalDetails );
+        }
+    }
+
+    /**
+     * @param source
+     * @param eventName
+     * @param errorMessage
+     */
+    @Override
+    public void logError( String source, String eventName, String errorMessage )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( source + " | " + eventName + " | " + errorMessage );
+        }
+    }
+
+    /**
+     * @param event
+     */
+    @Override
+    public <T> void logICacheEvent( ICacheEvent<T> event )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( event );
+        }
+    }
+
+    /**
+     * @param logCategoryName
+     */
+    public synchronized void setLogCategoryName( String logCategoryName )
+    {
+        if ( logCategoryName != null && !logCategoryName.equals( this.logCategoryName ) )
+        {
+            this.logCategoryName = logCategoryName;
+            log = LogFactory.getLog( logCategoryName );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEvent.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEvent.java
new file mode 100644
index 0000000..82238f2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEvent.java
@@ -0,0 +1,77 @@
+package org.apache.commons.jcs.engine.logging.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/** Defines the common fields required by a cache event. */
+public interface ICacheEvent<K>
+    extends Serializable
+{
+    /**
+     * @param source the source to set
+     */
+    void setSource( String source );
+
+    /**
+     * @return the source
+     */
+    String getSource();
+
+    /**
+     * @param region the region to set
+     */
+    void setRegion( String region );
+
+    /**
+     * @return the region
+     */
+    String getRegion();
+
+    /**
+     * @param eventName the eventName to set
+     */
+    void setEventName( String eventName );
+
+    /**
+     * @return the eventName
+     */
+    String getEventName();
+
+    /**
+     * @param optionalDetails the optionalDetails to set
+     */
+    void setOptionalDetails( String optionalDetails );
+
+    /**
+     * @return the optionalDetails
+     */
+    String getOptionalDetails();
+
+    /**
+     * @param key the key to set
+     */
+    void setKey( K key );
+
+    /**
+     * @return the key
+     */
+    K getKey();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEventLogger.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEventLogger.java
new file mode 100644
index 0000000..986247b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/logging/behavior/ICacheEventLogger.java
@@ -0,0 +1,91 @@
+package org.apache.commons.jcs.engine.logging.behavior;
+
+/*
+ * 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 defines the behavior for event logging. Auxiliaries will send events to injected event
+ * loggers.
+ * <p>
+ * In general all ICache interface methods should call the logger if one is configured. This will be
+ * done on an ad hoc basis for now. Various auxiliaries may have additional events.
+ */
+public interface ICacheEventLogger
+{
+    /** ICache update */
+    String UPDATE_EVENT = "update";
+
+    /** ICache get */
+    String GET_EVENT = "get";
+
+    /** ICache getMultiple */
+    String GETMULTIPLE_EVENT = "getMultiple";
+
+    /** ICache getMatching */
+    String GETMATCHING_EVENT = "getMatching";
+
+    /** ICache remove */
+    String REMOVE_EVENT = "remove";
+
+    /** ICache removeAll */
+    String REMOVEALL_EVENT = "removeAll";
+
+    /** ICache dispose */
+    String DISPOSE_EVENT = "dispose";
+
+    /** ICache enqueue. The time in the queue. */
+    //String ENQUEUE_EVENT = "enqueue";
+    /**
+     * Creates an event.
+     * <p>
+     * @param source - e.g. RemoteCacheServer
+     * @param region - the name of the region
+     * @param eventName - e.g. update, get, put, remove
+     * @param optionalDetails - any extra message
+     * @param key - the cache key
+     * @return ICacheEvent
+     */
+    <T> ICacheEvent<T> createICacheEvent( String source, String region,
+            String eventName, String optionalDetails, T key );
+
+    /**
+     * Logs an event.
+     * <p>
+     * @param event - the event created in createICacheEvent
+     */
+    <T> void logICacheEvent( ICacheEvent<T> event );
+
+    /**
+     * Logs an event. These are internal application events that do not correspond to ICache calls.
+     * <p>
+     * @param source - e.g. RemoteCacheServer
+     * @param eventName - e.g. update, get, put, remove
+     * @param optionalDetails - any extra message
+     */
+    void logApplicationEvent( String source, String eventName, String optionalDetails );
+
+    /**
+     * Logs an error.
+     * <p>
+     * @param source - e.g. RemoteCacheServer
+     * @param eventName - e.g. update, get, put, remove
+     * @param errorMessage - any error message
+     */
+    void logError( String source, String eventName, String errorMessage );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpl.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpl.java
new file mode 100644
index 0000000..4bd235a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpl.java
@@ -0,0 +1,66 @@
+package org.apache.commons.jcs.engine.match;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.match.behavior.IKeyMatcher;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** This implementation of the KeyMatcher uses standard Java Pattern matching. */
+public class KeyMatcherPatternImpl<K>
+    implements IKeyMatcher<K>
+{
+    /** Serial version */
+    private static final long serialVersionUID = 6667352064144381264L;
+
+    /**
+     * Creates a pattern and find matches on the array.
+     * <p>
+     * @param pattern
+     * @param keyArray
+     * @return Set of the matching keys
+     */
+    @Override
+    public Set<K> getMatchingKeysFromArray( String pattern, Set<K> keyArray )
+    {
+        Pattern compiledPattern = Pattern.compile( pattern );
+
+        Set<K> matchingKeys = new HashSet<K>();
+
+        // Look for matches
+        for (K key : keyArray)
+        {
+            // TODO we might want to match on the toString.
+            if ( key instanceof String )
+            {
+                Matcher matcher = compiledPattern.matcher( (String) key );
+                if ( matcher.matches() )
+                {
+                    matchingKeys.add( key );
+                }
+            }
+        }
+
+        return matchingKeys;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/behavior/IKeyMatcher.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/behavior/IKeyMatcher.java
new file mode 100644
index 0000000..74e9d01
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/match/behavior/IKeyMatcher.java
@@ -0,0 +1,36 @@
+package org.apache.commons.jcs.engine.match.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.Set;
+
+/** Key matchers need to implement this interface. */
+public interface IKeyMatcher<K> extends Serializable
+{
+    /**
+     * Creates a pattern and find matches on the array.
+     * <p>
+     * @param pattern
+     * @param keyArray
+     * @return Set of the matching keys
+     */
+    Set<K> getMatchingKeysFromArray( String pattern, Set<K> keyArray );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java
new file mode 100644
index 0000000..dc2ac30
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractDoubleLinkedListMemoryCache.java
@@ -0,0 +1,810 @@
+package org.apache.commons.jcs.engine.memory;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.jcs.utils.struct.DoubleLinkedList;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * This class contains methods that are common to memory caches using the double linked list, such
+ * as the LRU, MRU, FIFO, and LIFO caches.
+ * <p>
+ * Children can control the expiration algorithm by controlling the update and get. The last item in
+ * the list will be the one removed when the list fills. For instance LRU should more items to the
+ * front as they are used. FIFO should simply add new items to the front of the list.
+ */
+public abstract class AbstractDoubleLinkedListMemoryCache<K, V>
+    extends AbstractMemoryCache<K, V>
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 1422569420563967389L;
+
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( AbstractDoubleLinkedListMemoryCache.class );
+
+    /** thread-safe double linked list for lru */
+    protected DoubleLinkedList<MemoryElementDescriptor<K, V>> list; // TODO privatise
+
+    /** number of hits */
+    private volatile int hitCnt = 0;
+
+    /** number of misses */
+    private volatile int missCnt = 0;
+
+    /** number of puts */
+    private volatile int putCnt = 0;
+
+    /**
+     * For post reflection creation initialization.
+     * <p>
+     * @param hub
+     */
+    @Override
+    public void initialize( CompositeCache<K, V> hub )
+    {
+        lock.lock();
+        try
+        {
+            super.initialize(hub);
+            list = new DoubleLinkedList<MemoryElementDescriptor<K, V>>();
+            log.info("initialized MemoryCache for " + cacheName);
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * This is called by super initialize.
+     *
+     * NOTE: should return a thread safe map
+     *
+     * <p>
+     * @return new ConcurrentHashMap()
+     */
+    @Override
+    public Map<K, MemoryElementDescriptor<K, V>> createMap()
+    {
+        return new ConcurrentHashMap<K, MemoryElementDescriptor<K, V>>();
+    }
+
+    /**
+     * Calls the abstract method updateList.
+     * <p>
+     * If the max size is reached, an element will be put to disk.
+     * <p>
+     * @param ce The cache element, or entry wrapper
+     * @throws IOException
+     */
+    @Override
+    public final void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        lock.lock();
+        try
+        {
+            putCnt++;
+
+            MemoryElementDescriptor<K, V> newNode = adjustListForUpdate(ce);
+
+            // this should be synchronized if we were not using a ConcurrentHashMap
+            MemoryElementDescriptor<K, V> oldNode = map.put(newNode.ce.getKey(), newNode);
+
+            // If the node was the same as an existing node, remove it.
+            if (oldNode != null && newNode.ce.getKey().equals(oldNode.ce.getKey())) {
+                list.remove(oldNode);
+            }
+        }
+        finally
+        {
+            lock.unlock();
+        }
+
+        // If we are over the max spool some
+        spoolIfNeeded();
+    }
+
+    /**
+     * Children implement this to control the cache expiration algorithm
+     * <p>
+     * @param ce
+     * @return MemoryElementDescriptor the new node
+     * @throws IOException
+     */
+    protected abstract MemoryElementDescriptor<K, V> adjustListForUpdate( ICacheElement<K, V> ce )
+        throws IOException;
+
+    /**
+     * If the max size has been reached, spool.
+     * <p>
+     * @throws Error
+     */
+    private void spoolIfNeeded()
+        throws Error
+    {
+        int size = map.size();
+        // If the element limit is reached, we need to spool
+
+        if ( size <= this.cacheAttributes.getMaxObjects() )
+        {
+            return;
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "In memory limit reached, spooling" );
+        }
+
+        // Write the last 'chunkSize' items to disk.
+        int chunkSizeCorrected = Math.min( size, chunkSize );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "About to spool to disk cache, map size: " + size + ", max objects: "
+                + this.cacheAttributes.getMaxObjects() + ", items to spool: " + chunkSizeCorrected );
+        }
+
+        // The spool will put them in a disk event queue, so there is no
+        // need to pre-queue the queuing. This would be a bit wasteful
+        // and wouldn't save much time in this synchronous call.
+        for ( int i = 0; i < chunkSizeCorrected; i++ )
+        {
+            spoolLastElement();
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "update: After spool map size: " + map.size() + " linked list size = " + dumpCacheSize() );
+        }
+    }
+
+    /**
+     * Get an item from the cache If the item is found, it is removed from the list and added first.
+     * <p>
+     * @param key Identifies item to find
+     * @return ICacheElement<K, V> if found, else null
+     * @throws IOException
+     */
+    @Override
+    public final ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        ICacheElement<K, V> ce = null;
+
+        final boolean debugEnabled = log.isDebugEnabled();
+
+        if (debugEnabled)
+        {
+            log.debug( "getting item from cache " + cacheName + " for key " + key );
+        }
+
+        MemoryElementDescriptor<K, V> me = map.get( key );
+
+        if ( me != null )
+        {
+            lock.lock();
+            try
+            {
+                ce = me.ce;
+                hitCnt++;
+
+                // ABSTRACT
+                adjustListForGet( me );
+            }
+            finally
+            {
+                lock.unlock();
+            }
+
+            if (debugEnabled)
+            {
+                log.debug( cacheName + ": LRUMemoryCache hit for " + ce.getKey() );
+            }
+        }
+        else
+        {
+            lock.lock();
+            try
+            {
+                missCnt++;
+            }
+            finally
+            {
+                lock.unlock();
+            }
+
+            if (debugEnabled)
+            {
+                log.debug( cacheName + ": LRUMemoryCache miss for " + key );
+            }
+        }
+
+        verifyCache();
+        return ce;
+    }
+
+    /**
+     * Adjust the list as needed for a get. This allows children to control the algorithm
+     * <p>
+     * @param me
+     */
+    protected abstract void adjustListForGet( MemoryElementDescriptor<K, V> me );
+
+    /**
+     * This instructs the memory cache to remove the <i>numberToFree</i> according to its eviction
+     * policy. For example, the LRUMemoryCache will remove the <i>numberToFree</i> least recently
+     * used items. These will be spooled to disk if a disk auxiliary is available.
+     * <p>
+     * @param numberToFree
+     * @return the number that were removed. if you ask to free 5, but there are only 3, you will
+     *         get 3.
+     * @throws IOException
+     */
+    @Override
+    public int freeElements( int numberToFree )
+        throws IOException
+    {
+        int freed = 0;
+        for ( ; freed < numberToFree; freed++ )
+        {
+            ICacheElement<K, V> element = spoolLastElement();
+            if ( element == null )
+            {
+                break;
+            }
+        }
+        return freed;
+    }
+
+    /**
+     * This spools the last element in the LRU, if one exists.
+     * <p>
+     * @return ICacheElement<K, V> if there was a last element, else null.
+     * @throws Error
+     */
+    protected ICacheElement<K, V> spoolLastElement()
+        throws Error
+    {
+        ICacheElement<K, V> toSpool = null;
+        final MemoryElementDescriptor<K, V> last = list.getLast();
+        if ( last != null )
+        {
+            lock.lock();
+            try
+            {
+                toSpool = last.ce;
+                if (toSpool != null) {
+                    cache.spoolToDisk(last.ce);
+                    if (map.remove(last.ce.getKey()) == null) {
+                        log.warn("update: remove failed for key: "
+                                + last.ce.getKey());
+                        verifyCache();
+                    }
+                }
+                else
+                {
+                    throw new Error("update: last.ce is null!");
+                }
+                list.remove(last);
+            }
+            finally
+            {
+                lock.unlock();
+            }
+        }
+        else
+        {
+            verifyCache();
+            throw new Error( "update: last is null!" );
+        }
+
+        // If this is out of the sync block it can detect a mismatch
+        // where there is none.
+        if ( map.size() != dumpCacheSize() )
+        {
+            log.warn( "update: After spool, size mismatch: map.size() = " + map.size() + ", linked list size = "
+                + dumpCacheSize() );
+        }
+        return toSpool;
+    }
+
+    /**
+     * Removes an item from the cache. This method handles hierarchical removal. If the key is a
+     * String and ends with the CacheConstants.NAME_COMPONENT_DELIMITER, then all items with keys
+     * starting with the argument String will be removed.
+     * <p>
+     * @param key
+     * @return true if the removal was successful
+     * @throws IOException
+     */
+    @Override
+    public boolean remove( K key )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing item for key: " + key );
+        }
+
+        boolean removed = false;
+
+        // handle partial removal
+        if ( key instanceof String && ( (String) key ).endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+        {
+            // remove all keys of the same name hierarchy.
+            for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
+            {
+                Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
+                K k = entry.getKey();
+
+                if (k instanceof String && ((String) k).startsWith(key.toString()))
+                {
+                    lock.lock();
+                    try
+                    {
+                        list.remove(entry.getValue());
+                        itr.remove();
+                        removed = true;
+                    }
+                    finally
+                    {
+                        lock.unlock();
+                    }
+                }
+            }
+        }
+        else if ( key instanceof GroupAttrName )
+        {
+            // remove all keys of the same name hierarchy.
+            for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
+            {
+                Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
+                K k = entry.getKey();
+
+                if ( k instanceof GroupAttrName &&
+                    ((GroupAttrName<?>)k).groupId.equals(((GroupAttrName<?>)key).groupId))
+                {
+                    lock.lock();
+                    try
+                    {
+                        list.remove(entry.getValue());
+                        itr.remove();
+                        removed = true;
+                    }
+                    finally
+                    {
+                        lock.unlock();
+                    }
+                }
+            }
+        }
+        else
+        {
+            // remove single item.
+            lock.lock();
+            try
+            {
+                MemoryElementDescriptor<K, V> me = map.remove(key);
+                if (me != null)
+                {
+                    list.remove(me);
+                    removed = true;
+                }
+            }
+            finally
+            {
+                lock.unlock();
+            }
+        }
+
+        return removed;
+    }
+
+    /**
+     * Remove all of the elements from both the Map and the linked list implementation. Overrides
+     * base class.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        lock.lock();
+        try
+        {
+            list.removeAll();
+            map.clear();
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    // --------------------------- internal methods (linked list implementation)
+    /**
+     * Adds a new node to the start of the link list.
+     * <p>
+     * @param ce The feature to be added to the First
+     * @return MemoryElementDescriptor
+     */
+    protected MemoryElementDescriptor<K, V> addFirst( ICacheElement<K, V> ce )
+    {
+        lock.lock();
+        try
+        {
+            MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<K, V>(ce);
+            list.addFirst(me);
+            verifyCache(ce.getKey());
+            return me;
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Adds a new node to the end of the link list.
+     * <p>
+     * @param ce The feature to be added to the First
+     * @return MemoryElementDescriptor
+     */
+    protected MemoryElementDescriptor<K, V> addLast( ICacheElement<K, V> ce )
+    {
+        lock.lock();
+        try
+        {
+            MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<K, V>(ce);
+            list.addLast(me);
+            verifyCache(ce.getKey());
+            return me;
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    // ---------------------------------------------------------- debug methods
+
+    /**
+     * Dump the cache entries from first to list for debugging.
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    public void dumpCacheEntries()
+    {
+        log.debug( "dumpingCacheEntries" );
+        for ( MemoryElementDescriptor<K, V> me = list.getFirst(); me != null; me = (MemoryElementDescriptor<K, V>) me.next )
+        {
+            log.debug( "dumpCacheEntries> key=" + me.ce.getKey() + ", val=" + me.ce.getVal() );
+        }
+    }
+
+    /**
+     * Returns the size of the list.
+     * <p>
+     * @return the number of items in the map.
+     */
+    protected int dumpCacheSize()
+    {
+        return list.size();
+    }
+
+    /**
+     * Checks to see if all the items that should be in the cache are. Checks consistency between
+     * List and map.
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    protected void verifyCache()
+    {
+        if ( !log.isDebugEnabled() )
+        {
+            return;
+        }
+
+        boolean found = false;
+        log.debug( "verifycache[" + cacheName + "]: mapContains " + map.size() + " elements, linked list contains "
+            + dumpCacheSize() + " elements" );
+        log.debug( "verifycache: checking linked list by key " );
+        for ( MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next )
+        {
+            Object key = li.ce.getKey();
+            if ( !map.containsKey( key ) )
+            {
+                log.error( "verifycache[" + cacheName + "]: map does not contain key : " + li.ce.getKey() );
+                log.error( "li.hashcode=" + li.ce.getKey().hashCode() );
+                log.error( "key class=" + key.getClass() );
+                log.error( "key hashcode=" + key.hashCode() );
+                log.error( "key toString=" + key.toString() );
+                if ( key instanceof GroupAttrName )
+                {
+                    GroupAttrName<?> name = (GroupAttrName<?>) key;
+                    log.error( "GroupID hashcode=" + name.groupId.hashCode() );
+                    log.error( "GroupID.class=" + name.groupId.getClass() );
+                    log.error( "AttrName hashcode=" + name.attrName.hashCode() );
+                    log.error( "AttrName.class=" + name.attrName.getClass() );
+                }
+                dumpMap();
+            }
+            else if ( map.get( li.ce.getKey() ) == null )
+            {
+                log.error( "verifycache[" + cacheName + "]: linked list retrieval returned null for key: "
+                    + li.ce.getKey() );
+            }
+        }
+
+        log.debug( "verifycache: checking linked list by value " );
+        for ( MemoryElementDescriptor<K, V> li3 = list.getFirst(); li3 != null; li3 = (MemoryElementDescriptor<K, V>) li3.next )
+        {
+            if ( map.containsValue( li3 ) == false )
+            {
+                log.error( "verifycache[" + cacheName + "]: map does not contain value : " + li3 );
+                dumpMap();
+            }
+        }
+
+        log.debug( "verifycache: checking via keysets!" );
+        for (Object val : map.keySet())
+        {
+            found = false;
+
+            for ( MemoryElementDescriptor<K, V> li2 = list.getFirst(); li2 != null; li2 = (MemoryElementDescriptor<K, V>) li2.next )
+            {
+                if ( val.equals( li2.ce.getKey() ) )
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if ( !found )
+            {
+                log.error( "verifycache[" + cacheName + "]: key not found in list : " + val );
+                dumpCacheEntries();
+                if ( map.containsKey( val ) )
+                {
+                    log.error( "verifycache: map contains key" );
+                }
+                else
+                {
+                    log.error( "verifycache: map does NOT contain key, what the HECK!" );
+                }
+            }
+        }
+    }
+
+    /**
+     * Logs an error if an element that should be in the cache is not.
+     * <p>
+     * @param key
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    private void verifyCache( K key )
+    {
+        if ( !log.isDebugEnabled() )
+        {
+            return;
+        }
+
+        boolean found = false;
+
+        // go through the linked list looking for the key
+        for ( MemoryElementDescriptor<K, V> li = list.getFirst(); li != null; li = (MemoryElementDescriptor<K, V>) li.next )
+        {
+            if ( li.ce.getKey() == key )
+            {
+                found = true;
+                log.debug( "verifycache(key) key match: " + key );
+                break;
+            }
+        }
+        if ( !found )
+        {
+            log.error( "verifycache(key)[" + cacheName + "], couldn't find key! : " + key );
+        }
+    }
+
+    // --------------------------- iteration methods (iteration helpers)
+    /**
+     * iteration aid
+     */
+    public static class IteratorWrapper<K extends Serializable, V extends Serializable>
+        implements Iterator<Entry<K, MemoryElementDescriptor<K, V>>>
+    {
+        /** The internal iterator */
+        private final Iterator<Entry<K, MemoryElementDescriptor<K, V>>> i;
+
+        /**
+         * Wrapped to remove our wrapper object
+         * @param m
+         */
+        protected IteratorWrapper(Map<K, MemoryElementDescriptor<K, V>> m)
+        {
+            i = m.entrySet().iterator();
+        }
+
+        /** @return i.hasNext() */
+        @Override
+        public boolean hasNext()
+        {
+            return i.hasNext();
+        }
+
+        /** @return new MapEntryWrapper( (Map.Entry) i.next() ) */
+        @Override
+        public Entry<K, MemoryElementDescriptor<K, V>> next()
+        {
+            // return new MapEntryWrapper<Serializable>( i.next() );
+            return i.next();
+        }
+
+        /** i.remove(); */
+        @Override
+        public void remove()
+        {
+            i.remove();
+        }
+
+        /**
+         * @param o
+         * @return i.equals( o ))
+         */
+        @Override
+        public boolean equals( Object o )
+        {
+            return i.equals( o );
+        }
+
+        /** @return i.hashCode() */
+        @Override
+        public int hashCode()
+        {
+            return i.hashCode();
+        }
+    }
+
+    /**
+     * @author Aaron Smuts
+     */
+    public static class MapEntryWrapper<K extends Serializable, V extends Serializable>
+        implements Map.Entry<K, ICacheElement<K, V>>
+    {
+        /** The internal entry */
+        private final Map.Entry<K, MemoryElementDescriptor<K, V>> e;
+
+        /**
+         * @param e
+         */
+        private MapEntryWrapper( Map.Entry<K, MemoryElementDescriptor<K, V>> e )
+        {
+            this.e = e;
+        }
+
+        /**
+         * @param o
+         * @return e.equals( o )
+         */
+        @Override
+        public boolean equals( Object o )
+        {
+            return e.equals( o );
+        }
+
+        /** @return e.getKey() */
+        @Override
+        public K getKey()
+        {
+            return e.getKey();
+        }
+
+        /** @return ( (MemoryElementDescriptor) e.getValue() ).ce */
+        @Override
+        public ICacheElement<K, V> getValue()
+        {
+            return e.getValue().ce;
+        }
+
+        /** @return e.hashCode() */
+        @Override
+        public int hashCode()
+        {
+            return e.hashCode();
+        }
+
+        /**
+         * invalid
+         * @param value
+         * @return always throws
+         */
+        @Override
+        public ICacheElement<K, V> setValue(ICacheElement<K, V> value)
+        {
+            throw new UnsupportedOperationException( "Use normal cache methods"
+                + " to alter the contents of the cache." );
+        }
+    }
+
+    /**
+     * Get an Array of the keys for all elements in the memory cache
+     * @return An Object[]
+     */
+    @Override
+    public Set<K> getKeySet()
+    {
+        return new LinkedHashSet<K>(map.keySet());
+    }
+
+    /**
+     * This returns semi-structured information on the memory cache, such as the size, put count,
+     * hit count, and miss count.
+     * <p>
+     * @see org.apache.commons.jcs.engine.memory.behavior.IMemoryCache#getStatistics()
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( /*add algorithm name*/"Memory Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        lock.lock(); // not sure that's really relevant here but not that important
+        try
+        {
+            elems.add(new StatElement<Integer>("List Size", Integer.valueOf(list.size())));
+            elems.add(new StatElement<Integer>("Map Size", Integer.valueOf(map.size())));
+            elems.add(new StatElement<Integer>("Put Count", Integer.valueOf(putCnt)));
+            elems.add(new StatElement<Integer>("Hit Count", Integer.valueOf(hitCnt)));
+            elems.add(new StatElement<Integer>("Miss Count", Integer.valueOf(missCnt)));
+        }
+        finally
+        {
+            lock.unlock();
+        }
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java
new file mode 100644
index 0000000..8532cf4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/AbstractMemoryCache.java
@@ -0,0 +1,339 @@
+package org.apache.commons.jcs.engine.memory;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.memory.behavior.IMemoryCache;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * This base includes some common code for memory caches.
+ * <p>
+ * This keeps a static reference to a memory shrinker clock daemon. If this region is configured to
+ * use the shrinker, the clock daemon will be setup to run the shrinker on this region.
+ */
+public abstract class AbstractMemoryCache<K, V>
+    implements IMemoryCache<K, V>
+{
+    /** Log instance */
+    private static final Log log = LogFactory.getLog( AbstractMemoryCache.class );
+
+    /** The region name. This defines a namespace of sorts. */
+    protected String cacheName; // TODO privatise (mainly seems to be used externally for debugging)
+
+    /** Map where items are stored by key.  This is created by the concrete child class. */
+    public Map<K, MemoryElementDescriptor<K, V>> map;// TODO privatise
+
+    /** Region Elemental Attributes, used as a default and copied for each item. */
+    public IElementAttributes elementAttributes;// TODO privatise
+
+    /** Cache Attributes.  Regions settings. */
+    public ICompositeCacheAttributes cacheAttributes;// TODO privatise
+
+    /** The cache region this store is associated with */
+    public CompositeCache<K, V> cache;// TODO privatise
+
+    /** status */
+    private CacheStatus status;
+
+    /** How many to spool at a time. */
+    protected int chunkSize;
+
+    protected final Lock lock = new ReentrantLock();
+
+    /**
+     * For post reflection creation initialization
+     * <p>
+     * @param hub
+     */
+    @Override
+    public void initialize( CompositeCache<K, V> hub )
+    {
+        lock.lock();
+        try
+        {
+            this.cacheName = hub.getCacheName();
+            this.cacheAttributes = hub.getCacheAttributes();
+            this.cache = hub;
+            map = createMap();
+
+            chunkSize = cacheAttributes.getSpoolChunkSize();
+            status = CacheStatus.ALIVE;
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Children must implement this method. A FIFO implementation may use a tree map. An LRU might
+     * use a hashtable. The map returned should be threadsafe.
+     * <p>
+     * @return a threadsafe Map
+     */
+    public abstract Map<K, MemoryElementDescriptor<K, V>> createMap();
+
+    /**
+     * Removes an item from the cache
+     * <p>
+     * @param key Identifies item to be removed
+     * @return Description of the Return Value
+     * @throws IOException Description of the Exception
+     */
+    @Override
+    public abstract boolean remove( K key )
+        throws IOException;
+
+    /**
+     * Get an item from the cache
+     * <p>
+     * @param key Description of the Parameter
+     * @return Description of the Return Value
+     * @throws IOException Description of the Exception
+     */
+    @Override
+    public abstract ICacheElement<K, V> get( K key )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            for (K key : keys)
+            {
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * Get an item from the cache without affecting its last access time or position. Not all memory
+     * cache implementations can get quietly.
+     * <p>
+     * @param key Identifies item to find
+     * @return Element matching key if found, or null
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> getQuiet( K key )
+        throws IOException
+    {
+        ICacheElement<K, V> ce = null;
+
+        MemoryElementDescriptor<K, V> me = map.get( key );
+        if ( me != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( cacheName + ": MemoryCache quiet hit for " + key );
+            }
+
+            ce = me.ce;
+        }
+        else if ( log.isDebugEnabled() )
+        {
+            log.debug( cacheName + ": MemoryCache quiet miss for " + key );
+        }
+
+        return ce;
+    }
+
+    /**
+     * Puts an item to the cache.
+     * <p>
+     * @param ce Description of the Parameter
+     * @throws IOException Description of the Exception
+     */
+    @Override
+    public abstract void update( ICacheElement<K, V> ce )
+        throws IOException;
+
+    /**
+     * Get a set of the keys for all elements in the memory cache
+     * <p>
+     * @return A set of the key type
+     */
+    @Override
+    public abstract Set<K> getKeySet();
+
+    /**
+     * Removes all cached items from the cache.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        map.clear();
+    }
+
+    /**
+     * Prepares for shutdown.
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        log.info( "Memory Cache dispose called." );
+    }
+
+    /**
+     * @return statistics about the cache
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "Abstract Memory Cache" );
+        return stats;
+    }
+
+    /**
+     * Returns the current cache size.
+     * <p>
+     * @return The size value
+     */
+    @Override
+    public int getSize()
+    {
+        return this.map.size();
+    }
+
+    /**
+     * Returns the cache status.
+     * <p>
+     * @return The status value
+     */
+    public CacheStatus getStatus()
+    {
+        return this.status;
+    }
+
+    /**
+     * Returns the cache (aka "region") name.
+     * <p>
+     * @return The cacheName value
+     */
+    public String getCacheName()
+    {
+        String attributeCacheName = this.cacheAttributes.getCacheName();
+        if(attributeCacheName != null)
+            return attributeCacheName;
+        return cacheName;
+    }
+
+    /**
+     * Puts an item to the cache.
+     * <p>
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void waterfal( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        this.cache.spoolToDisk( ce );
+    }
+
+    // ---------------------------------------------------------- debug method
+    /**
+     * Dump the cache map for debugging.
+     */
+    public void dumpMap()
+    {
+        log.debug( "dumpingMap" );
+        for (Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet())
+        {
+            MemoryElementDescriptor<K, V> me = e.getValue();
+            log.debug( "dumpMap> key=" + e.getKey() + ", val=" + me.ce.getVal() );
+        }
+    }
+
+    /**
+     * Returns the CacheAttributes.
+     * <p>
+     * @return The CacheAttributes value
+     */
+    @Override
+    public ICompositeCacheAttributes getCacheAttributes()
+    {
+        return this.cacheAttributes;
+    }
+
+    /**
+     * Sets the CacheAttributes.
+     * <p>
+     * @param cattr The new CacheAttributes value
+     */
+    @Override
+    public void setCacheAttributes( ICompositeCacheAttributes cattr )
+    {
+        this.cacheAttributes = cattr;
+    }
+
+    /**
+     * Gets the cache hub / region that the MemoryCache is used by
+     * <p>
+     * @return The cache value
+     */
+    @Override
+    public CompositeCache<K, V> getCompositeCache()
+    {
+        return this.cache;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/behavior/IMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/behavior/IMemoryCache.java
new file mode 100644
index 0000000..fa21393
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/behavior/IMemoryCache.java
@@ -0,0 +1,187 @@
+package org.apache.commons.jcs.engine.memory.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+/** For the framework. Insures methods a MemoryCache needs to access. */
+public interface IMemoryCache<K, V>
+{
+    /**
+     * Initialize the memory cache
+     * <p>
+     * @param cache The cache (region) this memory store is attached to.
+     */
+    void initialize( CompositeCache<K, V> cache );
+
+    /**
+     * Destroy the memory cache
+     * <p>
+     * @throws IOException
+     */
+    void dispose()
+        throws IOException;
+
+    /**
+     * Get the number of elements contained in the memory store
+     * <p>
+     * @return Element count
+     */
+    int getSize();
+
+    /**
+     * Returns the historical and statistical data for a region's memory cache.
+     * <p>
+     * @return Statistics and Info for the Memory Cache.
+     */
+    IStats getStatistics();
+
+    /**
+     * Get a set of the keys for all elements in the memory cache.
+     * <p>
+     * @return a set of the key type
+     * TODO This should probably be done in chunks with a range passed in. This
+     *       will be a problem if someone puts a 1,000,000 or so items in a
+     *       region.
+     */
+    Set<K> getKeySet();
+
+    /**
+     * Removes an item from the cache
+     * <p>
+     * @param key
+     *            Identifies item to be removed
+     * @return Description of the Return Value
+     * @throws IOException
+     *                Description of the Exception
+     */
+    boolean remove( K key )
+        throws IOException;
+
+    /**
+     * Removes all cached items from the cache.
+     * <p>
+     * @throws IOException
+     *                Description of the Exception
+     */
+    void removeAll()
+        throws IOException;
+
+    /**
+     * This instructs the memory cache to remove the <i>numberToFree</i>
+     * according to its eviction policy. For example, the LRUMemoryCache will
+     * remove the <i>numberToFree</i> least recently used items. These will be
+     * spooled to disk if a disk auxiliary is available.
+     * <p>
+     * @param numberToFree
+     * @return the number that were removed. if you ask to free 5, but there are
+     *         only 3, you will get 3.
+     * @throws IOException
+     */
+    int freeElements( int numberToFree )
+        throws IOException;
+
+    /**
+     * Get an item from the cache
+     * <p>
+     * @param key
+     *            Description of the Parameter
+     * @return Description of the Return Value
+     * @throws IOException
+     *                Description of the Exception
+     */
+    ICacheElement<K, V> get( K key )
+        throws IOException;
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map
+     * if there is no data in cache for any of these keys
+     * @throws IOException
+     */
+    Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
+        throws IOException;
+
+    /**
+     * Get an item from the cache without effecting its order or last access
+     * time
+     * <p>
+     * @param key
+     *            Description of the Parameter
+     * @return The quiet value
+     * @throws IOException
+     *                Description of the Exception
+     */
+    ICacheElement<K, V> getQuiet( K key )
+        throws IOException;
+
+    /**
+     * Spools the item contained in the provided element to disk
+     * <p>
+     * @param ce
+     *            Description of the Parameter
+     * @throws IOException
+     *                Description of the Exception
+     */
+    void waterfal( ICacheElement<K, V> ce )
+        throws IOException;
+
+    /**
+     * Puts an item to the cache.
+     * <p>
+     * @param ce
+     *            Description of the Parameter
+     * @throws IOException
+     *                Description of the Exception
+     */
+    void update( ICacheElement<K, V> ce )
+        throws IOException;
+
+    /**
+     * Returns the CacheAttributes for the region.
+     * <p>
+     * @return The cacheAttributes value
+     */
+    ICompositeCacheAttributes getCacheAttributes();
+
+    /**
+     * Sets the CacheAttributes of the region.
+     * <p>
+     * @param cattr
+     *            The new cacheAttributes value
+     */
+    void setCacheAttributes( ICompositeCacheAttributes cattr );
+
+    /**
+     * Gets the cache hub / region that uses the MemoryCache.
+     * <p>
+     * @return The cache value
+     */
+    CompositeCache<K, V> getCompositeCache();
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCache.java
new file mode 100644
index 0000000..c7e9746
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCache.java
@@ -0,0 +1,60 @@
+package org.apache.commons.jcs.engine.memory.fifo;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.memory.AbstractDoubleLinkedListMemoryCache;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * The items are spooled in the order they are added. No adjustments to the list are made on get.
+ */
+public class FIFOMemoryCache<K extends Serializable, V extends Serializable>
+    extends AbstractDoubleLinkedListMemoryCache<K, V>
+{
+    /**
+     * Puts an item to the cache. Removes any pre-existing entries of the same key from the linked
+     * list and adds this one first.
+     * <p>
+     * @param ce The cache element, or entry wrapper
+     * @return MemoryElementDescriptor the new node
+     * @throws IOException
+     */
+    @Override
+    protected MemoryElementDescriptor<K, V> adjustListForUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        return addFirst( ce );
+    }
+
+    /**
+     * Does nothing.
+     * <p>
+     * @param me
+     */
+    @Override
+    protected void adjustListForGet( MemoryElementDescriptor<K, V> me )
+    {
+        // DO NOTHING
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java
new file mode 100644
index 0000000..419064c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCache.java
@@ -0,0 +1,361 @@
+package org.apache.commons.jcs.engine.memory.lru;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheConstants;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.memory.AbstractMemoryCache;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a test memory manager using the jdk1.4 LinkedHashMap.
+ */
+public class LHMLRUMemoryCache<K extends Serializable, V extends Serializable>
+    extends AbstractMemoryCache<K, V>
+{
+    /** The Logger. */
+    private static final Log log = LogFactory.getLog( LRUMemoryCache.class );
+
+    /** number of hits */
+    private int hitCnt = 0; // TODO should these be long values?
+
+    /** number of misses */
+    private int missCnt = 0;
+
+    /** number of puts */
+    private int putCnt = 0;
+
+    /**
+     * For post reflection creation initialization
+     * <p>
+     * @param hub
+     */
+    @Override
+    public synchronized void initialize( CompositeCache<K, V> hub )
+    {
+        super.initialize( hub );
+        log.info( "initialized LHMLRUMemoryCache for " + cacheName );
+    }
+
+    /**
+     * Returns a synchronized LHMSpooler
+     * <p>
+     * @return Collections.synchronizedMap( new LHMSpooler() )
+     */
+    @Override
+    public Map<K, MemoryElementDescriptor<K, V>> createMap()
+    {
+        return Collections.synchronizedMap( new LHMSpooler() );
+    }
+
+    /**
+     * Puts an item to the cache.
+     * <p>
+     * @param ce Description of the Parameter
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        putCnt++;
+        map.put( ce.getKey(), new MemoryElementDescriptor<K, V>(ce) );
+    }
+
+    /**
+     * Get an item from the cache without affecting its last access time or position. There is no
+     * way to do this with the LinkedHashMap!
+     * <p>
+     * @param key Identifies item to find
+     * @return Element matching key if found, or null
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> getQuiet( K key )
+        throws IOException
+    {
+        return map.get( key ).ce;
+    }
+
+    /**
+     * Get an item from the cache
+     * <p>
+     * @param key Identifies item to find
+     * @return ICacheElement<K, V> if found, else null
+     * @throws IOException
+     */
+    @Override
+    public synchronized ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        MemoryElementDescriptor<K, V> me = null;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "getting item from cache " + cacheName + " for key " + key );
+        }
+
+        me = map.get( key );
+
+        if ( me != null )
+        {
+            hitCnt++;
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( cacheName + ": LRUMemoryCache hit for " + key );
+            }
+            return me.ce;
+        }
+        else
+        {
+            missCnt++;
+            log.debug( cacheName + ": LRUMemoryCache miss for " + key );
+        }
+
+        return null;
+    }
+
+    /**
+     * Removes an item from the cache. This method handles hierarchical removal. If the key is a
+     * String and ends with the CacheConstants.NAME_COMPONENT_DELIMITER, then all items with keys
+     * starting with the argument String will be removed.
+     * <p>
+     * @param key
+     * @return true if removed
+     * @throws IOException
+     */
+    @Override
+    public synchronized boolean remove( K key )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing item for key: " + key );
+        }
+
+        boolean removed = false;
+
+        // handle partial removal
+        if ( key instanceof String && ( (String) key ).endsWith( CacheConstants.NAME_COMPONENT_DELIMITER ) )
+        {
+            // remove all keys of the same name hierarchy.
+            synchronized ( map )
+            {
+                for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
+                {
+                    Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
+                    K k = entry.getKey();
+
+                    if ( k instanceof String && ( (String) k ).startsWith( key.toString() ) )
+                    {
+                        itr.remove();
+                        removed = true;
+                    }
+                }
+            }
+        }
+        else if ( key instanceof GroupAttrName && ((GroupAttrName<?>)key).attrName == null )
+        {
+            // remove all keys of the same name hierarchy.
+            synchronized ( map )
+            {
+                for (Iterator<Map.Entry<K, MemoryElementDescriptor<K, V>>> itr = map.entrySet().iterator(); itr.hasNext(); )
+                {
+                    Map.Entry<K, MemoryElementDescriptor<K, V>> entry = itr.next();
+                    K k = entry.getKey();
+
+                    if ( k instanceof GroupAttrName &&
+                        ((GroupAttrName<?>)k).groupId.equals(((GroupAttrName<?>)key).groupId) )
+                    {
+                        itr.remove();
+                        removed = true;
+                    }
+                }
+            }
+        }
+        else
+        {
+            // remove single item.
+            MemoryElementDescriptor<K, V> me = map.remove( key );
+            if ( me != null )
+            {
+                removed = true;
+            }
+        }
+
+        return removed;
+    }
+
+    /**
+     * Get an Array of the keys for all elements in the memory cache
+     * <p>
+     * @return An Object[]
+     */
+    @Override
+    public Set<K> getKeySet()
+    {
+        // need a better locking strategy here.
+        synchronized ( this )
+        {
+            // may need to lock to map here?
+            return new LinkedHashSet<K>(map.keySet());
+        }
+    }
+
+    /**
+     * This returns semi-structured information on the memory cache, such as the size, put count,
+     * hit count, and miss count.
+     * <p>
+     * @return IStats
+     */
+    @Override
+    public synchronized IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "LHMLRU Memory Cache" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Integer>( "Map Size", Integer.valueOf(map.size()) ) );
+        elems.add(new StatElement<Integer>( "Put Count", Integer.valueOf(putCnt) ) );
+        elems.add(new StatElement<Integer>( "Hit Count", Integer.valueOf(hitCnt) ) );
+        elems.add(new StatElement<Integer>( "Miss Count", Integer.valueOf(missCnt) ) );
+
+        stats.setStatElements( elems );
+
+        // int rate = ((hitCnt + missCnt) * 100) / (hitCnt * 100) * 100;
+        // buf.append("\n Hit Rate = " + rate + " %" );
+
+        return stats;
+    }
+
+    // ---------------------------------------------------------- debug methods
+
+    /**
+     * Dump the cache entries from first to last for debugging.
+     */
+    public void dumpCacheEntries()
+    {
+        dumpMap();
+    }
+
+    /**
+     * This can't be implemented.
+     * <p>
+     * @param numberToFree
+     * @return 0
+     * @throws IOException
+     */
+    @Override
+    public int freeElements( int numberToFree )
+        throws IOException
+    {
+        // can't be implemented using the LHM
+        return 0;
+    }
+
+    // ---------------------------------------------------------- extended map
+
+    /**
+     * Implementation of removeEldestEntry in LinkedHashMap
+     */
+    public class LHMSpooler
+        extends java.util.LinkedHashMap<K, MemoryElementDescriptor<K, V>>
+    {
+        /** Don't change. */
+        private static final long serialVersionUID = -1255907868906762484L;
+
+        /**
+         * Initialize to a small size--for now, 1/2 of max 3rd variable "true" indicates that it
+         * should be access and not time governed. This could be configurable.
+         */
+        public LHMSpooler()
+        {
+            super( (int) ( cache.getCacheAttributes().getMaxObjects() * .5 ), .75F, true );
+        }
+
+        /**
+         * Remove eldest. Automatically called by LinkedHashMap.
+         * <p>
+         * @param eldest
+         * @return true if removed
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        protected boolean removeEldestEntry( Map.Entry<K, MemoryElementDescriptor<K, V>> eldest )
+        {
+            ICacheElement<K, V> element = eldest.getValue().ce;
+
+            if ( size() <= cache.getCacheAttributes().getMaxObjects() )
+            {
+                return false;
+            }
+            else
+            {
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "LHMLRU max size: " + cache.getCacheAttributes().getMaxObjects()
+                        + ".  Spooling element, key: " + element.getKey() );
+                }
+                spoolToDisk( element );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "LHMLRU size: " + map.size() );
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Puts the element in the DiskStore
+         * <p>
+         * @param element The CacheElement
+         */
+        @SuppressWarnings("synthetic-access")
+        private void spoolToDisk( ICacheElement<K, V> element )
+        {
+            cache.spoolToDisk( element );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( cache.getCacheName() + "Spooled element to disk: " + element.getKey() );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCache.java
new file mode 100644
index 0000000..ab36421
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCache.java
@@ -0,0 +1,67 @@
+package org.apache.commons.jcs.engine.memory.lru;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.memory.AbstractDoubleLinkedListMemoryCache;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+
+import java.io.IOException;
+
+/**
+ * A fast reference management system. The least recently used items move to the end of the list and
+ * get spooled to disk if the cache hub is configured to use a disk cache. Most of the cache
+ * bottlenecks are in IO. There are no io bottlenecks here, it's all about processing power.
+ * <p>
+ * Even though there are only a few adjustments necessary to maintain the double linked list, we
+ * might want to find a more efficient memory manager for large cache regions.
+ * <p>
+ * The LRUMemoryCache is most efficient when the first element is selected. The smaller the region,
+ * the better the chance that this will be the case. < .04 ms per put, p3 866, 1/10 of that per get
+ */
+public class LRUMemoryCache<K, V>
+    extends AbstractDoubleLinkedListMemoryCache<K, V>
+{
+    /**
+     * Puts an item to the cache. Removes any pre-existing entries of the same key from the linked
+     * list and adds this one first.
+     * <p>
+     * @param ce The cache element, or entry wrapper
+     * @return MemoryElementDescriptor the new node
+     * @throws IOException
+     */
+    @Override
+    protected MemoryElementDescriptor<K, V> adjustListForUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        return addFirst( ce );
+    }
+
+    /**
+     * Makes the item the first in the list.
+     * <p>
+     * @param me
+     */
+    @Override
+    protected void adjustListForGet( MemoryElementDescriptor<K, V> me )
+    {
+        list.makeFirst( me );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/package.html
new file mode 100644
index 0000000..af2bde5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/lru/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    The primary memory plugin using a 'least recently used' removal policy.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCache.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCache.java
new file mode 100644
index 0000000..4a2c936
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCache.java
@@ -0,0 +1,63 @@
+package org.apache.commons.jcs.engine.memory.mru;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.memory.AbstractDoubleLinkedListMemoryCache;
+import org.apache.commons.jcs.engine.memory.util.MemoryElementDescriptor;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * The most recently used items move to the front of the list and get spooled to disk if the cache
+ * hub is configured to use a disk cache.
+ */
+public class MRUMemoryCache<K extends Serializable, V extends Serializable>
+    extends AbstractDoubleLinkedListMemoryCache<K, V>
+{
+    /**
+     * Adds the item to the front of the list. A put doesn't count as a usage.
+     * <p>
+     * It's not clear if the put operation should be different. Perhaps this should remove the oldest
+     * if full, and then put.
+     * <p>
+     * @param ce
+     * @return MemoryElementDescriptor the new node
+     * @throws IOException
+     */
+    @Override
+    protected MemoryElementDescriptor<K, V> adjustListForUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        return addFirst( ce );
+    }
+
+    /**
+     * Makes the item the last in the list.
+     * <p>
+     * @param me
+     */
+    @Override
+    protected void adjustListForGet( MemoryElementDescriptor<K, V> me )
+    {
+        list.makeLast( me );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/package.html
new file mode 100644
index 0000000..2377c2d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/mru/package.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+     A memory plugin implemented using a 'most recently used' removal policy.
+     In general this is slow and should not be used.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/package.html
new file mode 100644
index 0000000..47f02c2
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    Parent package for memory type plugins.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThread.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThread.java
new file mode 100644
index 0000000..883e935
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThread.java
@@ -0,0 +1,222 @@
+package org.apache.commons.jcs.engine.memory.shrinking;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.event.behavior.ElementEventType;
+import org.apache.commons.jcs.engine.memory.behavior.IMemoryCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Set;
+
+/**
+ * A background memory shrinker. Memory problems and concurrent modification exception caused by
+ * acting directly on an iterator of the underlying memory cache should have been solved.
+ * @version $Id: ShrinkerThread.java 1593843 2014-05-11 19:43:30Z rmannibucau $
+ */
+public class ShrinkerThread<K, V>
+    implements Runnable
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( ShrinkerThread.class );
+
+    /** The CompositeCache instance which this shrinker is watching */
+    private final CompositeCache<K, V> cache;
+
+    /** Maximum memory idle time for the whole cache */
+    private final long maxMemoryIdleTime;
+
+    /** Maximum number of items to spool per run. Default is -1, or no limit. */
+    private final int maxSpoolPerRun;
+
+    /** Should we limit the number spooled per run. If so, the maxSpoolPerRun will be used. */
+    private boolean spoolLimit = false;
+
+    /**
+     * Constructor for the ShrinkerThread object.
+     * <p>
+     * @param cache The MemoryCache which the new shrinker should watch.
+     */
+    public ShrinkerThread( CompositeCache<K, V> cache )
+    {
+        super();
+
+        this.cache = cache;
+
+        long maxMemoryIdleTimeSeconds = cache.getCacheAttributes().getMaxMemoryIdleTimeSeconds();
+
+        if ( maxMemoryIdleTimeSeconds < 0 )
+        {
+            this.maxMemoryIdleTime = -1;
+        }
+        else
+        {
+            this.maxMemoryIdleTime = maxMemoryIdleTimeSeconds * 1000;
+        }
+
+        this.maxSpoolPerRun = cache.getCacheAttributes().getMaxSpoolPerRun();
+        if ( this.maxSpoolPerRun != -1 )
+        {
+            this.spoolLimit = true;
+        }
+
+    }
+
+    /**
+     * Main processing method for the ShrinkerThread object
+     */
+    @Override
+    public void run()
+    {
+        shrink();
+    }
+
+    /**
+     * This method is called when the thread wakes up. First the method obtains an array of keys for
+     * the cache region. It iterates through the keys and tries to get the item from the cache
+     * without affecting the last access or position of the item. The item is checked for
+     * expiration, the expiration check has 3 parts:
+     * <ol>
+     * <li>Has the cacheattributes.MaxMemoryIdleTimeSeconds defined for the region been exceeded? If
+     * so, the item should be move to disk.</li> <li>Has the item exceeded MaxLifeSeconds defined in
+     * the element attributes? If so, remove it.</li> <li>Has the item exceeded IdleTime defined in
+     * the element attributes? If so, remove it. If there are event listeners registered for the
+     * cache element, they will be called.</li>
+     * </ol>
+     * TODO Change element event handling to use the queue, then move the queue to the region and
+     *       access via the Cache.
+     */
+    protected void shrink()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Shrinking memory cache for: " + this.cache.getCacheName() );
+        }
+
+        IMemoryCache<K, V> memCache = cache.getMemoryCache();
+
+        try
+        {
+            Set<K> keys = memCache.getKeySet();
+            int size = keys.size();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Keys size: " + size );
+            }
+
+            ICacheElement<K, V> cacheElement;
+            IElementAttributes attributes;
+
+            int spoolCount = 0;
+
+            for (K key : keys)
+            {
+                cacheElement = memCache.getQuiet( key );
+
+                if ( cacheElement == null )
+                {
+                    continue;
+                }
+
+                attributes = cacheElement.getElementAttributes();
+
+                boolean remove = false;
+
+                long now = System.currentTimeMillis();
+
+                // If the element is not eternal, check if it should be
+                // removed and remove it if so.
+                if ( !cacheElement.getElementAttributes().getIsEternal() )
+                {
+                    remove = cache.isExpired( cacheElement, now,
+                            ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
+                            ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
+
+                    if ( remove )
+                    {
+                        memCache.remove( cacheElement.getKey() );
+                    }
+                }
+
+                // If the item is not removed, check is it has been idle
+                // long enough to be spooled.
+
+                if ( !remove && maxMemoryIdleTime != -1 )
+                {
+                    if ( !spoolLimit || spoolCount < this.maxSpoolPerRun )
+                    {
+                        final long lastAccessTime = attributes.getLastAccessTime();
+
+                        if ( lastAccessTime + maxMemoryIdleTime < now )
+                        {
+                            if ( log.isDebugEnabled() )
+                            {
+                                log.debug( "Exceeded memory idle time: " + cacheElement.getKey() );
+                            }
+
+                            // Shouldn't we ensure that the element is
+                            // spooled before removing it from memory?
+                            // No the disk caches have a purgatory. If it fails
+                            // to spool that does not affect the
+                            // responsibilities of the memory cache.
+
+                            spoolCount++;
+
+                            memCache.remove( cacheElement.getKey() );
+
+                            memCache.waterfal( cacheElement );
+
+                            key = null;
+                            cacheElement = null;
+                        }
+                    }
+                    else
+                    {
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "spoolCount = '" + spoolCount + "'; " + "maxSpoolPerRun = '" + maxSpoolPerRun
+                                + "'" );
+                        }
+
+                        // stop processing if limit has been reached.
+                        if ( spoolLimit && spoolCount >= this.maxSpoolPerRun )
+                        {
+                            keys = null;
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+        catch ( Throwable t )
+        {
+            log.info( "Unexpected trouble in shrink cycle", t );
+
+            // concurrent modifications should no longer be a problem
+            // It is up to the IMemoryCache to return an array of keys
+
+            // stop for now
+            return;
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java
new file mode 100644
index 0000000..fbd24ea
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/memory/util/MemoryElementDescriptor.java
@@ -0,0 +1,47 @@
+package org.apache.commons.jcs.engine.memory.util;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.utils.struct.DoubleLinkedListNode;
+
+/**
+ * This wrapper is needed for double linked lists.
+ */
+public class MemoryElementDescriptor<K, V>
+    extends DoubleLinkedListNode<ICacheElement<K, V>>
+{
+    /** Don't change */
+    private static final long serialVersionUID = -1905161209035522460L;
+
+    /** The CacheElement wrapped by this descriptor */
+    public final ICacheElement<K, V> ce; // TODO privatise
+
+    /**
+     * Constructs a usable MemoryElementDescriptor.
+     * <p>
+     * @param ce
+     */
+    public MemoryElementDescriptor( ICacheElement<K, V> ce )
+    {
+        super( ce );
+        this.ce = ce;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/package.html
new file mode 100644
index 0000000..9cb3956
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/package.html
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+     Interfaces used by the core and the auxiliary caches.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/CacheStats.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/CacheStats.java
new file mode 100644
index 0000000..eacede7
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/CacheStats.java
@@ -0,0 +1,116 @@
+package org.apache.commons.jcs.engine.stats;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.util.List;
+
+/**
+ * This class stores cache historical and statistics data for a region.
+ * <p>
+ * Only the composite cache knows what the hit count across all auxiliaries is.
+ */
+public class CacheStats
+    extends Stats
+    implements ICacheStats
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 529914708798168590L;
+
+    /** The region */
+    private String regionName = null;
+
+    /** What that auxiliaries are reporting. */
+    private List<IStats> auxStats = null;
+
+    /**
+     * Stats are for a region, though auxiliary data may be for more.
+     * <p>
+     * @return The region name
+     */
+    @Override
+    public String getRegionName()
+    {
+        return regionName;
+    }
+
+    /**
+     * Stats are for a region, though auxiliary data may be for more.
+     * <p>
+     * @param name - The region name
+     */
+    @Override
+    public void setRegionName( String name )
+    {
+        regionName = name;
+    }
+
+    /**
+     * @return IStats[]
+     */
+    @Override
+    public List<IStats> getAuxiliaryCacheStats()
+    {
+        return auxStats;
+    }
+
+    /**
+     * @param stats
+     */
+    @Override
+    public void setAuxiliaryCacheStats( List<IStats> stats )
+    {
+        auxStats = stats;
+    }
+
+    /**
+     * @return readable string that can be logged.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append( "Region Name = " + regionName );
+
+        if ( getStatElements() != null )
+        {
+            for ( Object stat : getStatElements() )
+            {
+                buf.append( "\n" );
+                buf.append( stat );
+            }
+        }
+
+        if ( auxStats != null )
+        {
+            for ( Object auxStat : auxStats )
+            {
+                buf.append( "\n" );
+                buf.append( "---------------------------" );
+                buf.append( auxStat );
+            }
+        }
+
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/StatElement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/StatElement.java
new file mode 100644
index 0000000..29e7b26
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/StatElement.java
@@ -0,0 +1,104 @@
+package org.apache.commons.jcs.engine.stats;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+
+/**
+ * This is a stat data holder.
+ */
+public class StatElement<V>
+    implements IStatElement<V>
+{
+    /** Don't change */
+    private static final long serialVersionUID = -2982373725267618092L;
+
+    /** name of the stat */
+    private String name = null;
+
+    /** the data */
+    private V data = null;
+
+    /**
+     * Constructor
+     *
+     * @param name
+     * @param data
+     */
+    public StatElement(String name, V data)
+    {
+        super();
+        this.name = name;
+        this.data = data;
+    }
+
+    /**
+     * Get the name of the stat element, ex. HitCount
+     * <p>
+     * @return the stat element name
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * @param name
+     */
+    @Override
+    public void setName( String name )
+    {
+        this.name = name;
+    }
+
+    /**
+     * Get the data, ex. for hit count you would get a value for some number.
+     * <p>
+     * @return data
+     */
+    @Override
+    public V getData()
+    {
+        return data;
+    }
+
+    /**
+     * Set the data for this element.
+     * <p>
+     * @param data
+     */
+    @Override
+    public void setData( V data )
+    {
+        this.data = data;
+    }
+
+    /**
+     * @return a readable string.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( name + " = " + data );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/Stats.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/Stats.java
new file mode 100644
index 0000000..dfbf388
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/Stats.java
@@ -0,0 +1,99 @@
+package org.apache.commons.jcs.engine.stats;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.util.List;
+
+/**
+ * @author aaronsm
+ */
+public class Stats
+    implements IStats
+{
+    /** Don't change */
+    private static final long serialVersionUID = 227327902875154010L;
+
+    /** The stats */
+    private List<IStatElement<?>> stats = null;
+
+    /** The type of stat */
+    private String typeName = null;
+
+    /**
+     * @return IStatElement[]
+     */
+    @Override
+    public List<IStatElement<?>> getStatElements()
+    {
+        return stats;
+    }
+
+    /**
+     * @param stats
+     */
+    @Override
+    public void setStatElements( List<IStatElement<?>> stats )
+    {
+        this.stats = stats;
+    }
+
+    /**
+     * @return typeName
+     */
+    @Override
+    public String getTypeName()
+    {
+        return typeName;
+    }
+
+    /**
+     * @param name
+     */
+    @Override
+    public void setTypeName( String name )
+    {
+        typeName = name;
+    }
+
+    /**
+     * @return the stats in a readable string
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append( typeName );
+
+        if ( stats != null )
+        {
+            for (Object stat : stats)
+            {
+                buf.append( "\n" );
+                buf.append( stat );
+            }
+        }
+
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/ICacheStats.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/ICacheStats.java
new file mode 100644
index 0000000..4efd282
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/ICacheStats.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.engine.stats.behavior;
+
+import java.util.List;
+
+/*
+ * 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 holds stat information on a region. It contains both auxiliary and core stats.
+ */
+public interface ICacheStats
+    extends IStats
+{
+    /**
+     * Stats are for a region, though auxiliary data may be for more.
+     * <p>
+     * @return The region name
+     */
+    String getRegionName();
+
+    /**
+     * @param name
+     */
+    void setRegionName( String name );
+
+    /**
+     * @return IStats[]
+     */
+    List<IStats> getAuxiliaryCacheStats();
+
+    /**
+     * @param stats
+     */
+    void setAuxiliaryCacheStats( List<IStats> stats );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStatElement.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStatElement.java
new file mode 100644
index 0000000..64139df
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStatElement.java
@@ -0,0 +1,54 @@
+package org.apache.commons.jcs.engine.stats.behavior;
+
+import java.io.Serializable;
+
+/*
+ * 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.
+ */
+
+/**
+ * IAuxiliaryCacheStats will hold these IStatElements.
+ */
+public interface IStatElement<V> extends Serializable
+{
+    /**
+     * Get the name of the stat element, ex. HitCount
+     * <p>
+     * @return the stat element name
+     */
+    String getName();
+
+    /**
+     * @param name
+     */
+    void setName( String name );
+
+    /**
+     * Get the data, ex. for hit count you would get a value for some number.
+     * <p>
+     * @return data
+     */
+    V getData();
+
+    /**
+     * Set the data for this element.
+     * <p>
+     * @param data
+     */
+    void setData( V data );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStats.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStats.java
new file mode 100644
index 0000000..352225a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/engine/stats/behavior/IStats.java
@@ -0,0 +1,63 @@
+package org.apache.commons.jcs.engine.stats.behavior;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * This interface defines the common behavior for a stats holder.
+ *
+ * @author aaronsm
+ *
+ */
+public interface IStats
+    extends Serializable
+{
+
+    /**
+     * Return generic statistical or historical data.
+     *
+     * @return list of IStatElements
+     */
+    List<IStatElement<?>> getStatElements();
+
+    /**
+     * Set the generic statistical or historical data.
+     *
+     * @param stats
+     */
+    void setStatElements( List<IStatElement<?>> stats );
+
+    /**
+     * Get the type name, such as "LRU Memory Cache." No formal type is defined.
+     *
+     * @return String
+     */
+    String getTypeName();
+
+    /**
+     * Set the type name, such as "LRU Memory Cache." No formal type is defined.
+     * If we need formal types, we can use the cachetype param
+     *
+     * @param name
+     */
+    void setTypeName( String name );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/io/ObjectInputStreamClassLoaderAware.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/io/ObjectInputStreamClassLoaderAware.java
new file mode 100644
index 0000000..dcb142d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/io/ObjectInputStreamClassLoaderAware.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Proxy;
+
+public class ObjectInputStreamClassLoaderAware extends ObjectInputStream
+{
+    private final ClassLoader classLoader;
+
+    public ObjectInputStreamClassLoaderAware(final InputStream in, final ClassLoader classLoader) throws IOException
+    {
+        super(in);
+        this.classLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
+    }
+
+    @Override
+    protected Class<?> resolveClass(final ObjectStreamClass desc) throws ClassNotFoundException
+    {
+        return Class.forName(desc.getName(), false, classLoader);
+    }
+
+    @Override
+    protected Class resolveProxyClass(final String[] interfaces) throws IOException, ClassNotFoundException
+    {
+        final Class[] cinterfaces = new Class[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++)
+        {
+            cinterfaces[i] = Class.forName(interfaces[i], false, classLoader);
+        }
+
+        try
+        {
+            return Proxy.getProxyClass(classLoader, cinterfaces);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new ClassNotFoundException(null, e);
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/package.html
new file mode 100644
index 0000000..bce936d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/package.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+     Contains the class JCS which provides a simple interface for clients to use
+     JCS.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/AbstractJCSWorkerHelper.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/AbstractJCSWorkerHelper.java
new file mode 100644
index 0000000..2c727a5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/AbstractJCSWorkerHelper.java
@@ -0,0 +1,59 @@
+package org.apache.commons.jcs.utils.access;
+
+/*
+ * 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 is an abstract template for JCSWorkerHelper implementations. it simple has a convenience
+ * method for setting the finished flag.
+ * <p>
+ * @author tsavo
+ */
+public abstract class AbstractJCSWorkerHelper
+    implements JCSWorkerHelper
+{
+    /** finished flag. Can't we use wait notify? */
+    private boolean finished = false;
+
+    /**
+     * Default
+     */
+    public AbstractJCSWorkerHelper()
+    {
+        super();
+    }
+
+    /**
+     * @return finished
+     */
+    @Override
+    public boolean isFinished()
+    {
+        return finished;
+    }
+
+    /**
+     * @param isFinished
+     */
+    @Override
+    public void setFinished( boolean isFinished )
+    {
+        finished = isFinished;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorker.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorker.java
new file mode 100644
index 0000000..dfe6417
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorker.java
@@ -0,0 +1,306 @@
+package org.apache.commons.jcs.utils.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.GroupCacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class to encapsulate doing a piece of work, and caching the results
+ * in JCS. Simply construct this class with the region name for the Cache and
+ * keep a static reference to it instead of the JCS itself. Then make a new
+ * org.apache.commons.jcs.utils.access.AbstractJCSWorkerHelper and implement Object
+ * doWork() and do the work in there, returning the object to be cached. Then
+ * call .getResult() with the key and the AbstractJCSWorkerHelper to get the
+ * result of the work. If the object isn't already in the Cache,
+ * AbstractJCSWorkerHelper.doWork() will get called, and the result will be put
+ * into the cache. If the object is already in cache, the cached result will be
+ * returned instead.
+ * <p>
+ * As an added bonus, multiple JCSWorkers with the same region, and key won't do
+ * the work multiple times: The first JCSWorker to get started will do the work,
+ * and all subsequent workers with the same region, group, and key will wait on
+ * the first one and use his resulting work instead of doing the work
+ * themselves.
+ * <p>
+ * This is ideal when the work being done is a query to the database where the
+ * results may take time to be retrieved.
+ * <p>
+ * For example:
+ *
+ * <pre>
+ *      public static JCSWorker cachingWorker = new JCSWorker("example region");
+ *   		public Object getSomething(Serializable aKey){
+ *        JCSWorkerHelper helper = new AbstractJCSWorkerHelper(){
+ *          public Object doWork(){
+ *            // Do some (DB?) work here which results in a list
+ *            // This only happens if the cache dosn't have a item in this region for aKey
+ *            // Note this is especially useful with Hibernate, which will cache indiviual
+ *            // Objects, but not entire query result sets.
+ *            List results = query.list();
+ *            // Whatever we return here get's cached with aKey, and future calls to
+ *            // getResult() on a CachedWorker with the same region and key will return that instead.
+ *            return results;
+ *        };
+ *        List result = worker.getResult(aKey, helper);
+ *      }
+ * </pre>
+ *
+ * This is essentially the same as doing:
+ *
+ * <pre>
+ * JCS jcs = JCS.getInstance( "exampleregion" );
+ * List results = (List) jcs.get( aKey );
+ * if ( results != null )
+ * {
+ *     //do the work here
+ *     results = query.list();
+ *     jcs.put( aKey, results );
+ * }
+ * </pre>
+ *
+ * <p>
+ * But has the added benefit of the work-load sharing; under normal
+ * circumstances if multiple threads all tried to do the same query at the same
+ * time, the same query would happen multiple times on the database, and the
+ * resulting object would get put into JCS multiple times.
+ * <p>
+ * @author Travis Savo
+ */
+public class JCSWorker<K extends Serializable, V extends Serializable>
+{
+    /** The logger */
+    private static final Log logger = LogFactory.getLog( JCSWorker.class );
+
+    /** The cache we are working with */
+    private CacheAccess<K, V> cache;
+
+    /** The cache we are working with */
+    private GroupCacheAccess<K, V> groupCache;
+
+    /**
+     * Map to hold who's doing work presently.
+     */
+    private static volatile Map<String, JCSWorkerHelper> map = new HashMap<String, JCSWorkerHelper>();
+
+    /**
+     * Region for the JCS cache.
+     */
+    private final String region;
+
+    /**
+     * Constructor which takes a region for the JCS cache.
+     * @param aRegion
+     *            The Region to use for the JCS cache.
+     */
+    public JCSWorker( final String aRegion )
+    {
+        region = aRegion;
+        try
+        {
+            cache = JCS.getInstance( aRegion );
+            groupCache = JCS.getGroupCacheInstance( aRegion );
+        }
+        catch ( CacheException e )
+        {
+            throw new RuntimeException( e.getMessage() );
+        }
+    }
+
+    /**
+     * Getter for the region of the JCS Cache.
+     * @return The JCS region in which the result will be cached.
+     */
+    public String getRegion()
+    {
+        return region;
+    }
+
+    /**
+     * Gets the cached result for this region/key OR does the work and caches
+     * the result, returning the result. If the result has not been cached yet,
+     * this calls doWork() on the JCSWorkerHelper to do the work and cache the
+     * result. This is also an opportunity to do any post processing of the
+     * result in your CachedWorker implementation.
+     * @param aKey
+     *            The key to get/put with on the Cache.
+     * @param aWorker
+     *            The JCSWorkerHelper implementing Object doWork(). This gets
+     *            called if the cache get misses, and the result is put into
+     *            cache.
+     * @return The result of doing the work, or the cached result.
+     * @throws Exception
+     *             Throws an exception if anything goes wrong while doing the
+     *             work.
+     */
+    public V getResult( K aKey, JCSWorkerHelper aWorker )
+        throws Exception
+    {
+        return run( aKey, null, aWorker );
+    }
+
+    /**
+     * Gets the cached result for this region/key OR does the work and caches
+     * the result, returning the result. If the result has not been cached yet,
+     * this calls doWork() on the JCSWorkerHelper to do the work and cache the
+     * result. This is also an opertunity to do any post processing of the
+     * result in your CachedWorker implementation.
+     * @param aKey
+     *            The key to get/put with on the Cache.
+     * @param aGroup
+     *            The cache group to put the result in.
+     * @param aWorker
+     *            The JCSWorkerHelper implementing Object doWork(). This gets
+     *            called if the cache get misses, and the result is put into
+     *            cache.
+     * @return The result of doing the work, or the cached result.
+     * @throws Exception
+     *             Throws an exception if anything goes wrong while doing the
+     *             work.
+     */
+    public V getResult( K aKey, String aGroup, JCSWorkerHelper aWorker )
+        throws Exception
+    {
+        return run( aKey, aGroup, aWorker );
+    }
+
+    /**
+     * Try and get the object from the cache, and if it's not there, do the work
+     * and cache it. This also ensures that only one CachedWorker is doing the
+     * work and subsequent calls to a CachedWorker with identical
+     * region/key/group will wait on the results of this call. It will call the
+     * JCSWorkerHelper.doWork() if the cache misses, and will put the result.
+     * @param aKey
+     * @param aGroup
+     * @param aHelper
+     * @return Either the result of doing the work, or the cached result.
+     * @throws Exception
+     *             If something goes wrong while doing the work, throw an
+     *             exception.
+     */
+    private V run( K aKey, String aGroup, JCSWorkerHelper aHelper )
+        throws Exception
+    {
+        V result = null;
+        // long start = 0;
+        // long dbTime = 0;
+        JCSWorkerHelper helper = null;
+
+        synchronized ( map )
+        {
+            // Check to see if we already have a thread doing this work.
+            helper = map.get( getRegion() + aKey );
+            if ( helper == null )
+            {
+                // If not, add ourselves as the Worker so
+                // calls in another thread will use this worker's result
+                map.put( getRegion() + aKey, aHelper );
+            }
+        }
+        if ( helper != null )
+        {
+            synchronized ( helper )
+            {
+                if ( logger.isDebugEnabled() )
+                {
+                    logger.debug( "Found a worker already doing this work (" + getRegion() + ":" + aKey + ")." );
+                }
+                if ( !helper.isFinished() )
+                {
+                    helper.wait();
+                }
+                if ( logger.isDebugEnabled() )
+                {
+                    logger.debug( "Another thread finished our work for us. Using those results instead. ("
+                        + getRegion() + ":" + aKey + ")." );
+                }
+            }
+        }
+        // Do the work
+        try
+        {
+            if ( logger.isDebugEnabled() )
+            {
+                logger.debug( getRegion() + " is doing the work." );
+            }
+
+            // Try to get the item from the cache
+            if ( aGroup != null )
+            {
+                result = groupCache.getFromGroup( aKey, aGroup );
+            }
+            else
+            {
+                result = cache.get( aKey );
+            }
+            // If the cache dosn't have it, do the work.
+            if ( result == null )
+            {
+                @SuppressWarnings("unchecked") // Need to cast from Object
+                V doWork = (V)aHelper.doWork();
+                result = doWork;
+                if ( logger.isDebugEnabled() )
+                {
+                    logger.debug( "Work Done, caching: key:" + aKey + ", group:" + aGroup + ", result:" + result + "." );
+                }
+                // Stick the result of the work in the cache.
+                if ( aGroup != null )
+                {
+                    groupCache.putInGroup( aKey, aGroup, result );
+                }
+                else
+                {
+                    cache.put( aKey, result );
+                }
+            }
+            // return the result
+            return result;
+        }
+        finally
+        {
+            if ( logger.isDebugEnabled() )
+            {
+                logger.debug( getRegion() + ":" + aKey + " entered finally." );
+            }
+            synchronized ( map )
+            {
+                // Remove ourselves as the worker.
+                if ( helper == null )
+                {
+                    map.remove( getRegion() + aKey );
+                }
+                synchronized ( aHelper )
+                {
+                    aHelper.setFinished( true );
+                    // Wake everyone waiting on us
+                    aHelper.notifyAll();
+                }
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorkerHelper.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorkerHelper.java
new file mode 100644
index 0000000..2fd2b10
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/access/JCSWorkerHelper.java
@@ -0,0 +1,61 @@
+package org.apache.commons.jcs.utils.access;
+
+/*
+ * 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.
+ */
+
+/**
+ * Interface for doing a piece of work which is expected to be cached. This is
+ * ment to be used in conjunction with JCSWorker.
+ * <p>
+ * Implement doWork() to return the work being done. isFinished() should return
+ * false until setFinished(true) is called, after which time it should return
+ * true.
+ * <p>
+ * @author tsavo
+ */
+public interface JCSWorkerHelper
+{
+    /**
+     * Tells us whether or not the work has been completed. This will be called
+     * automatically by JCSWorker. You should not call it yourself.
+     * <p>
+     * @return True if the work has already been done, otherwise false.
+     */
+    boolean isFinished();
+
+    /**
+     * Sets whether or not the work has been done.
+     * <p>
+     * @param isFinished
+     *            True if the work has already been done, otherwise false.
+     */
+    void setFinished( boolean isFinished );
+
+    /**
+     * The method to implement to do the work that should be cached. JCSWorker
+     * will call this itself! You should not call this directly.
+     * <p>
+     * @return The result of doing the work to be cached.
+     * @throws Exception
+     *             If anything goes wrong while doing the work, an Exception
+     *             should be thrown.
+     */
+    Object doWork()
+        throws Exception;
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/OptionConverter.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/OptionConverter.java
new file mode 100644
index 0000000..f95efb8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/OptionConverter.java
@@ -0,0 +1,427 @@
+package org.apache.commons.jcs.utils.config;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Properties;
+
+/**
+ * This class is based on the log4j class org.apache.log4j.helpers.OptionConverter that was made by
+ * Ceki Gülcü Simon Kitching; Avy Sharell (sharell at online.fr) Anders Kristensen Matthieu
+ * Verbert (mve at zurich.ibm.com) A convenience class to convert property values to specific types.
+ */
+public class OptionConverter
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( OptionConverter.class );
+
+    /** System property delimter */
+    private static final String DELIM_START = "${";
+
+    /** System property delimter */
+    private static final char DELIM_STOP = '}';
+
+    /** System property delimter start length */
+    private static final int DELIM_START_LEN = 2;
+
+    /** System property delimter end length */
+    private static final int DELIM_STOP_LEN = 1;
+
+    /** No instances please. */
+    private OptionConverter()
+    {
+        super();
+    }
+
+    /**
+     * Combines two arrays.
+     * @param l
+     * @param r
+     * @return String[]
+     */
+    public static String[] concatanateArrays( String[] l, String[] r )
+    {
+        int len = l.length + r.length;
+        String[] a = new String[len];
+
+        System.arraycopy( l, 0, a, 0, l.length );
+        System.arraycopy( r, 0, a, l.length, r.length );
+
+        return a;
+    }
+
+    /**
+     * Escapes special characters.
+     * <p>
+     * @param s
+     * @return String
+     */
+    public static String convertSpecialChars( String s )
+    {
+        char c;
+        int len = s.length();
+        StringBuilder sb = new StringBuilder( len );
+
+        int i = 0;
+        while ( i < len )
+        {
+            c = s.charAt( i++ );
+            if ( c == '\\' )
+            {
+                c = s.charAt( i++ );
+                if ( c == 'n' )
+                {
+                    c = '\n';
+                }
+                else if ( c == 'r' )
+                {
+                    c = '\r';
+                }
+                else if ( c == 't' )
+                {
+                    c = '\t';
+                }
+                else if ( c == 'f' )
+                {
+                    c = '\f';
+                }
+                else if ( c == '\b' )
+                {
+                    c = '\b';
+                }
+                else if ( c == '\"' )
+                {
+                    c = '\"';
+                }
+                else if ( c == '\'' )
+                {
+                    c = '\'';
+                }
+                else if ( c == '\\' )
+                {
+                    c = '\\';
+                }
+            }
+            sb.append( c );
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Very similar to <code>System.getProperty</code> except that the {@link SecurityException} is
+     * hidden.
+     * @param key The key to search for.
+     * @param def The default value to return.
+     * @return the string value of the system property, or the default value if there is no property
+     *         with that key.
+     * @since 1.1
+     */
+
+    public static String getSystemProperty( String key, String def )
+    {
+        try
+        {
+            return System.getProperty( key, def );
+        }
+        catch ( Throwable e )
+        {
+            // MS-Java throws com.ms.security.SecurityExceptionEx
+            log.debug( "Was not allowed to read system property \"" + key + "\"." );
+            return def;
+        }
+    }
+
+    /**
+     * Creates an object for the className value of the key.
+     * <p>
+     * @param props
+     * @param key
+     * @param defaultValue
+     * @return Object that was created
+     */
+    public static <T> T instantiateByKey( Properties props, String key, T defaultValue )
+    {
+
+        // Get the value of the property in string form
+        String className = findAndSubst( key, props );
+        if ( className == null )
+        {
+            if ( log.isTraceEnabled() )
+            {
+                log.info( "Could not find value for key " + key );
+            }
+            return defaultValue;
+        }
+        // Trim className to avoid trailing spaces that cause problems.
+        return OptionConverter.instantiateByClassName( className.trim(), defaultValue );
+    }
+
+    /**
+     * If <code>value</code> is "true", then <code>true</code> is returned. If <code>value</code> is
+     * "false", then <code>true</code> is returned. Otherwise, <code>default</code> is returned.
+     * <p>
+     * Case of value is unimportant.
+     * @param value
+     * @param dEfault
+     * @return Object
+     */
+
+    public static boolean toBoolean( String value, boolean dEfault )
+    {
+        if ( value == null )
+        {
+            return dEfault;
+        }
+        String trimmedVal = value.trim();
+        if ( "true".equalsIgnoreCase( trimmedVal ) )
+        {
+            return true;
+        }
+        if ( "false".equalsIgnoreCase( trimmedVal ) )
+        {
+            return false;
+        }
+        return dEfault;
+    }
+
+    /**
+     * Converts to int.
+     * <p>
+     * @param value
+     * @param dEfault
+     * @return int
+     */
+    public static int toInt( String value, int dEfault )
+    {
+        if ( value != null )
+        {
+            String s = value.trim();
+            try
+            {
+                return Integer.valueOf( s ).intValue();
+            }
+            catch ( NumberFormatException e )
+            {
+                log.error( "[" + s + "] is not in proper int form." );
+                e.printStackTrace();
+            }
+        }
+        return dEfault;
+    }
+
+    /**
+     * @param value
+     * @param dEfault
+     * @return long
+     */
+    public static long toFileSize( String value, long dEfault )
+    {
+        if ( value == null )
+        {
+            return dEfault;
+        }
+
+        String s = value.trim().toUpperCase();
+        long multiplier = 1;
+        int index;
+
+        if ( ( index = s.indexOf( "KB" ) ) != -1 )
+        {
+            multiplier = 1024;
+            s = s.substring( 0, index );
+        }
+        else if ( ( index = s.indexOf( "MB" ) ) != -1 )
+        {
+            multiplier = 1024 * 1024;
+            s = s.substring( 0, index );
+        }
+        else if ( ( index = s.indexOf( "GB" ) ) != -1 )
+        {
+            multiplier = 1024 * 1024 * 1024;
+            s = s.substring( 0, index );
+        }
+        if ( s != null )
+        {
+            try
+            {
+                return Long.valueOf( s ).longValue() * multiplier;
+            }
+            catch ( NumberFormatException e )
+            {
+                log.error( "[" + s + "] is not in proper int form" );
+                log.error( "[" + value + "] not in expected format", e );
+            }
+        }
+        return dEfault;
+    }
+
+    /**
+     * Find the value corresponding to <code>key</code> in <code>props</code>. Then perform variable
+     * substitution on the found value.
+     * <p>
+     * @param key
+     * @param props
+     * @return substituted string
+     */
+
+    public static String findAndSubst( String key, Properties props )
+    {
+        String value = props.getProperty( key );
+        if ( value == null )
+        {
+            return null;
+        }
+
+        try
+        {
+            return substVars( value, props );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            log.error( "Bad option value [" + value + "]", e );
+            return value;
+        }
+    }
+
+    /**
+     * Instantiate an object given a class name. Check that the <code>className</code> is a subclass
+     * of <code>superClass</code>. If that test fails or the object could not be instantiated, then
+     * <code>defaultValue</code> is returned.
+     * <p>
+     * @param className The fully qualified class name of the object to instantiate.
+     * @param defaultValue The object to return in case of non-fulfillment
+     * @return instantiated object
+     */
+
+    public static <T> T instantiateByClassName( String className, T defaultValue )
+    {
+        if ( className != null )
+        {
+            try
+            {
+                Class<?> classObj = Class.forName( className );
+                Object o = classObj.newInstance();
+
+                try
+                {
+                    @SuppressWarnings("unchecked") // CCE catched
+                    T t = (T) o;
+                    return t;
+                }
+                catch (ClassCastException e)
+                {
+                    log.error( "A \"" + className + "\" object is not assignable to the generic variable." );
+                    return defaultValue;
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Could not instantiate class [" + className + "]", e );
+            }
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Perform variable substitution in string <code>val</code> from the values of keys found in the
+     * system properties.
+     * <p>
+     * The variable substitution delimeters are <b>${ </b> and <b>} </b>.
+     * <p>
+     * For example, if the System properties contains "key=value", then the call
+     *
+     * <pre>
+     * String s = OptionConverter.substituteVars( "Value of key is ${key}." );
+     * </pre>
+     *
+     * will set the variable <code>s</code> to "Value of key is value.".
+     * <p>
+     * If no value could be found for the specified key, then the <code>props</code> parameter is
+     * searched, if the value could not be found there, then substitution defaults to the empty
+     * string.
+     * <p>
+     * For example, if system propeties contains no value for the key "inexistentKey", then the call
+     *
+     * <pre>
+     * String s = OptionConverter.subsVars( "Value of inexistentKey is [${inexistentKey}]" );
+     * </pre>
+     *
+     * will set <code>s</code> to "Value of inexistentKey is []"
+     * <p>
+     * An {@link java.lang.IllegalArgumentException}is thrown if <code>val</code> contains a start
+     * delimeter "${" which is not balanced by a stop delimeter "}".
+     * </p>
+     * <p>
+     * <b>Author </b> Avy Sharell </a>
+     * </p>
+     * @param val The string on which variable substitution is performed.
+     * @param props
+     * @return String
+     * @throws IllegalArgumentException if <code>val</code> is malformed.
+     */
+
+    public static String substVars( String val, Properties props )
+        throws IllegalArgumentException
+    {
+        StringBuilder sbuf = new StringBuilder();
+
+        int i = 0;
+        int j;
+        int k;
+
+        while ( true )
+        {
+            j = val.indexOf( DELIM_START, i );
+            if ( j == -1 )
+            {
+                if ( i == 0 )
+                {
+                    return val;
+                }
+                sbuf.append( val.substring( i, val.length() ) );
+                return sbuf.toString();
+            }
+            sbuf.append( val.substring( i, j ) );
+            k = val.indexOf( DELIM_STOP, j );
+            if ( k == -1 )
+            {
+                throw new IllegalArgumentException( '"' + val + "\" has no closing brace. Opening brace at position "
+                    + j + '.' );
+            }
+            j += DELIM_START_LEN;
+            String key = val.substring( j, k );
+            // first try in System properties
+            String replacement = getSystemProperty( key, null );
+            // then try props parameter
+            if ( replacement == null && props != null )
+            {
+                replacement = props.getProperty( key );
+            }
+
+            if ( replacement != null )
+            {
+                sbuf.append( replacement );
+            }
+            i = k + DELIM_STOP_LEN;
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetter.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetter.java
new file mode 100644
index 0000000..f33800d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetter.java
@@ -0,0 +1,300 @@
+package org.apache.commons.jcs.utils.config;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * This class is based on the log4j class org.apache.log4j.config.PropertySetter that was made by
+ * Anders Kristensen
+ * <p>
+ * General purpose Object property setter. Clients repeatedly invokes {@link #setProperty
+ * setProperty(name,value)} in order to invoke setters on the Object specified in the constructor.
+ * This class relies on the JavaBeans {@link Introspector}to analyze the given Object Class using
+ * reflection.
+ * <p>
+ * Usage:
+ *
+ * <pre>
+ * PropertySetter ps = new PropertySetter( anObject );
+ * ps.set( "name", "Joe" );
+ * ps.set( "age", "32" );
+ * ps.set( "isMale", "true" );
+ * </pre>
+ *
+ * will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and setMale(true) if
+ * such methods exist with those signatures. Otherwise an {@link IntrospectionException}are thrown.
+ */
+public class PropertySetter
+{
+    /** Logger */
+    private static final Log log = LogFactory.getLog( PropertySetter.class );
+
+    /** Description of the Field */
+    private final Object obj;
+
+    /** Description of the Field */
+    private PropertyDescriptor[] props;
+
+    /**
+     * Create a new PropertySetter for the specified Object. This is done in preparation for invoking
+     * {@link #setProperty}one or more times.
+     * @param obj the object for which to set properties
+     */
+    public PropertySetter( Object obj )
+    {
+        this.obj = obj;
+    }
+
+    /**
+     * Uses JavaBeans {@link Introspector}to compute setters of object to be configured.
+     */
+    protected void introspect()
+    {
+        try
+        {
+            BeanInfo bi = Introspector.getBeanInfo( obj.getClass() );
+            props = bi.getPropertyDescriptors();
+        }
+        catch ( IntrospectionException ex )
+        {
+            log.error( "Failed to introspect " + obj + ": " + ex.getMessage() );
+            props = new PropertyDescriptor[0];
+        }
+    }
+
+    /**
+     * Set the properties of an object passed as a parameter in one go. The <code>properties</code>
+     * are parsed relative to a <code>prefix</code>.
+     * <p>
+     * @param obj The object to configure.
+     * @param properties A java.util.Properties containing keys and values.
+     * @param prefix Only keys having the specified prefix will be set.
+     */
+    public static void setProperties( Object obj, Properties properties, String prefix )
+    {
+        new PropertySetter( obj ).setProperties( properties, prefix );
+    }
+
+    /**
+     * Set the properties for the object that match the <code>prefix</code> passed as parameter.
+     * <p>
+     * @param properties The new properties value
+     * @param prefix The new properties value
+     */
+    public void setProperties( Properties properties, String prefix )
+    {
+        int len = prefix.length();
+
+        for ( Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); )
+        {
+            String key = (String) e.nextElement();
+
+            // handle only properties that start with the desired prefix.
+            if ( key.startsWith( prefix ) )
+            {
+
+                // ignore key if it contains dots after the prefix
+                if ( key.indexOf( '.', len + 1 ) > 0 )
+                {
+                    //System.err.println("----------Ignoring---["+key
+                    //	     +"], prefix=["+prefix+"].");
+                    continue;
+                }
+
+                String value = OptionConverter.findAndSubst( key, properties );
+                key = key.substring( len );
+
+                setProperty( key, value );
+            }
+        }
+
+    }
+
+    /**
+     * Set a property on this PropertySetter's Object. If successful, this method will invoke a
+     * setter method on the underlying Object. The setter is the one for the specified property name
+     * and the value is determined partly from the setter argument type and partly from the value
+     * specified in the call to this method.
+     * <p>
+     * If the setter expects a String no conversion is necessary. If it expects an int, then an
+     * attempt is made to convert 'value' to an int using Integer.valueOf(value). If the setter expects
+     * a boolean, the conversion is by Boolean.valueOf(value).
+     * @param name name of the property
+     * @param value String value of the property
+     */
+
+    public void setProperty( String name, String value )
+    {
+        if ( value == null )
+        {
+            return;
+        }
+
+        name = Introspector.decapitalize( name );
+        PropertyDescriptor prop = getPropertyDescriptor( name );
+
+        //log.debug("---------Key: "+name+", type="+prop.getPropertyType());
+
+        if ( prop == null )
+        {
+            log.warn( "No such property [" + name + "] in " + obj.getClass().getName() + "." );
+        }
+        else
+        {
+            try
+            {
+                setProperty( prop, name, value );
+            }
+            catch ( PropertySetterException ex )
+            {
+                log.warn( "Failed to set property " + name + " to value \"" + value + "\". " + ex.getMessage() );
+            }
+        }
+    }
+
+    /**
+     * Set the named property given a {@link PropertyDescriptor}.
+     * @param prop A PropertyDescriptor describing the characteristics of the property to set.
+     * @param name The named of the property to set.
+     * @param value The value of the property.
+     * @throws PropertySetterException
+     */
+
+    public void setProperty( PropertyDescriptor prop, String name, String value )
+        throws PropertySetterException
+    {
+        Method setter = prop.getWriteMethod();
+        if ( setter == null )
+        {
+            throw new PropertySetterException( "No setter for property" );
+        }
+        Class<?>[] paramTypes = setter.getParameterTypes();
+        if ( paramTypes.length != 1 )
+        {
+            throw new PropertySetterException( "#params for setter != 1" );
+        }
+
+        Object arg;
+        try
+        {
+            arg = convertArg( value, paramTypes[0] );
+        }
+        catch ( Throwable t )
+        {
+            throw new PropertySetterException( "Conversion to type [" + paramTypes[0] + "] failed. Reason: " + t );
+        }
+        if ( arg == null )
+        {
+            throw new PropertySetterException( "Conversion to type [" + paramTypes[0] + "] failed." );
+        }
+        log.debug( "Setting property [" + name + "] to [" + arg + "]." );
+        try
+        {
+            setter.invoke( obj, new Object[] { arg } );
+        }
+        catch ( Exception ex )
+        {
+            throw new PropertySetterException( ex );
+        }
+    }
+
+    /**
+     * Convert <code>val</code> a String parameter to an object of a given type.
+     * @param val
+     * @param type
+     * @return Object
+     */
+    protected Object convertArg( String val, Class<?> type )
+    {
+        if ( val == null )
+        {
+            return null;
+        }
+
+        String v = val.trim();
+        if ( String.class.isAssignableFrom( type ) )
+        {
+            return val;
+        }
+        else if ( Integer.TYPE.isAssignableFrom( type ) )
+        {
+            return Integer.valueOf( v );
+        }
+        else if ( Long.TYPE.isAssignableFrom( type ) )
+        {
+            return Long.valueOf( v );
+        }
+        else if ( Boolean.TYPE.isAssignableFrom( type ) )
+        {
+            if ( "true".equalsIgnoreCase( v ) )
+            {
+                return Boolean.TRUE;
+            }
+            else if ( "false".equalsIgnoreCase( v ) )
+            {
+                return Boolean.FALSE;
+            }
+        }
+        else if( type.isEnum() )
+        {
+            @SuppressWarnings("unchecked") // type check in if()
+            Enum<?> en = Enum.valueOf(type.asSubclass(Enum.class), v );
+            return en;
+        }
+        else if ( File.class.isAssignableFrom( type ) )
+        {
+            return new File( v );
+        }
+        return null;
+    }
+
+    /**
+     * Gets the propertyDescriptor attribute of the PropertySetter object
+     * @param name
+     * @return The propertyDescriptor value
+     */
+    protected PropertyDescriptor getPropertyDescriptor( String name )
+    {
+        if ( props == null )
+        {
+            introspect();
+        }
+
+        for ( int i = 0; i < props.length; i++ )
+        {
+            if ( name.equals( props[i].getName() ) )
+            {
+                return props[i];
+            }
+        }
+        return null;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetterException.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetterException.java
new file mode 100644
index 0000000..0898a54
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/PropertySetterException.java
@@ -0,0 +1,75 @@
+package org.apache.commons.jcs.utils.config;
+
+/*
+ * 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 class is based on the log4j class org.apache.log4j.config.PropertySetter that was made by
+ * Anders Kristensen
+ * <p>
+ * Thrown when an error is encountered whilst attempting to set a property using the
+ * {@link PropertySetter}utility class.
+ */
+public class PropertySetterException
+    extends Exception
+{
+    /** DOn't change */
+    private static final long serialVersionUID = -210271658004609028L;
+
+    /** Description of the Field */
+    private final Throwable rootCause;
+
+    /**
+     * Constructor for the PropertySetterException object
+     * <p>
+     * @param msg
+     */
+    public PropertySetterException( String msg )
+    {
+        super( msg );
+        this.rootCause = null;
+    }
+
+    /**
+     * Constructor for the PropertySetterException object
+     * <p>
+     * @param rootCause
+     */
+    public PropertySetterException( Throwable rootCause )
+    {
+        super();
+        this.rootCause = rootCause;
+    }
+
+    /**
+     * Returns descriptive text on the cause of this exception.
+     * <p>
+     * @return The message value
+     */
+    @Override
+    public String getMessage()
+    {
+        String msg = super.getMessage();
+        if ( msg == null && rootCause != null )
+        {
+            msg = rootCause.getMessage();
+        }
+        return msg;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/package.html b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/package.html
new file mode 100644
index 0000000..1de5d40
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/config/package.html
@@ -0,0 +1,28 @@
+<!--
+ 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.
+-->
+<html>
+  <head>
+  </head>
+  <body>
+    This package contains utility classes that are used when configuring the
+    cache. <br/>
+    NOTE: It is likely that these classes will be removed in the future in favor
+    of commons-configuration.
+  </body>
+</html>
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/DiscoveredService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/DiscoveredService.java
new file mode 100644
index 0000000..cbfd737
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/DiscoveredService.java
@@ -0,0 +1,183 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * This contains info about a discovered service. These objects are stored in a set in the
+ * UDPDiscoveryService.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class DiscoveredService
+    implements Serializable
+{
+    /** For serialization. Don't change. */
+    private static final long serialVersionUID = -7810164772089509751L;
+
+    /** region names */
+    private ArrayList<String> cacheNames;
+
+    /** service address */
+    private String serviceAddress;
+
+    /** service port */
+    private int servicePort;
+
+    /** last time we heard from this service? */
+    private long lastHearFromTime = 0;
+
+    /**
+     * @param cacheNames the cacheNames to set
+     */
+    public void setCacheNames( ArrayList<String> cacheNames )
+    {
+        this.cacheNames = cacheNames;
+    }
+
+    /**
+     * @return the cacheNames
+     */
+    public ArrayList<String> getCacheNames()
+    {
+        return cacheNames;
+    }
+
+    /**
+     * @param serviceAddress The serviceAddress to set.
+     */
+    public void setServiceAddress( String serviceAddress )
+    {
+        this.serviceAddress = serviceAddress;
+    }
+
+    /**
+     * @return Returns the serviceAddress.
+     */
+    public String getServiceAddress()
+    {
+        return serviceAddress;
+    }
+
+    /**
+     * @param servicePort The servicePort to set.
+     */
+    public void setServicePort( int servicePort )
+    {
+        this.servicePort = servicePort;
+    }
+
+    /**
+     * @return Returns the servicePort.
+     */
+    public int getServicePort()
+    {
+        return servicePort;
+    }
+
+    /**
+     * @param lastHearFromTime The lastHearFromTime to set.
+     */
+    public void setLastHearFromTime( long lastHearFromTime )
+    {
+        this.lastHearFromTime = lastHearFromTime;
+    }
+
+    /**
+     * @return Returns the lastHearFromTime.
+     */
+    public long getLastHearFromTime()
+    {
+        return lastHearFromTime;
+    }
+
+    /** @return hashcode based on address/port */
+	@Override
+	public int hashCode()
+	{
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((serviceAddress == null) ? 0 : serviceAddress.hashCode());
+		result = prime * result + servicePort;
+		return result;
+	}
+
+	/**
+     * NOTE - this object is often put into sets, so equals needs to be overridden.
+     * <p>
+     * We can't use cache names as part of the equals unless we manually only use the address and
+     * port in a contains check. So that we can use normal set functionality, I've kept the cache
+     * names out.
+     * <p>
+     * @param otherArg other
+     * @return equality based on the address/port
+     */
+	@Override
+	public boolean equals(Object otherArg)
+	{
+		if (this == otherArg)
+		{
+			return true;
+		}
+		if (otherArg == null)
+		{
+			return false;
+		}
+		if (!(otherArg instanceof DiscoveredService))
+		{
+			return false;
+		}
+		DiscoveredService other = (DiscoveredService) otherArg;
+		if (serviceAddress == null)
+		{
+			if (other.serviceAddress != null)
+			{
+				return false;
+			}
+		} else if (!serviceAddress.equals(other.serviceAddress))
+		{
+			return false;
+		}
+		if (servicePort != other.servicePort)
+		{
+			return false;
+		}
+
+		return true;
+	}
+
+    /**
+     * @return string for debugging purposes.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n DiscoveredService" );
+        buf.append( "\n CacheNames = [" + getCacheNames() + "]" );
+        buf.append( "\n ServiceAddress = [" + getServiceAddress() + "]" );
+        buf.append( "\n ServicePort = [" + getServicePort() + "]" );
+        buf.append( "\n LastHearFromTime = [" + getLastHearFromTime() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPCleanupRunner.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPCleanupRunner.java
new file mode 100644
index 0000000..d000f20
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPCleanupRunner.java
@@ -0,0 +1,97 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class periodically check the lastHeardFrom time on the services.
+ * <p>
+ * If they exceed the configurable limit, it removes them from the set.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class UDPCleanupRunner
+    implements Runnable
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( UDPCleanupRunner.class );
+
+    /** UDP discovery service */
+    private final UDPDiscoveryService discoveryService;
+
+    /** default for max idle time, in seconds */
+    private static final long DEFAULT_MAX_IDLE_TIME_SECONDS = 180;
+
+    /** The configured max idle time, in seconds */
+    private final long maxIdleTimeSeconds = DEFAULT_MAX_IDLE_TIME_SECONDS;
+
+    /**
+     * @param service UDPDiscoveryService
+     */
+    public UDPCleanupRunner( UDPDiscoveryService service )
+    {
+        this.discoveryService = service;
+    }
+
+    /**
+     * This goes through the list of services and removes those that we haven't heard from in longer
+     * than the max idle time.
+     * <p>
+     * @see java.lang.Runnable#run()
+     */
+    @Override
+    public void run()
+    {
+        long now = System.currentTimeMillis();
+
+        // iterate through the set
+        // it is thread safe
+        // http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/CopyOnWriteArraySet.
+        // html
+        // TODO this should get a copy.  you can't simply remove from this.
+        // the listeners need to be notified.
+        Set<DiscoveredService> toRemove = new HashSet<DiscoveredService>();
+        // can't remove via the iterator. must remove directly
+        for (DiscoveredService service : discoveryService.getDiscoveredServices())
+        {
+            if ( ( now - service.getLastHearFromTime() ) > ( maxIdleTimeSeconds * 1000 ) )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Removing service, since we haven't heard from it in " + maxIdleTimeSeconds
+                        + " seconds.  service = " + service );
+                }
+                toRemove.add( service );
+            }
+        }
+
+        // remove the bad ones
+        for (DiscoveredService service : toRemove)
+        {
+            // call this so the listeners get notified
+            discoveryService.removeDiscoveredService( service );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryAttributes.java
new file mode 100644
index 0000000..996b5a8
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryAttributes.java
@@ -0,0 +1,231 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+/**
+ * Configuration properties for UDP discover service.
+ * <p>
+ * The service will allow out applications to find each other.
+ * <p>
+ * @author Aaron Smuts
+ */
+public final class UDPDiscoveryAttributes
+    implements Cloneable
+{
+    /** service name */
+    private String serviceName;
+
+    /** service address */
+    private String serviceAddress;
+
+    /** service port */
+    private int servicePort;
+
+    /**
+     * false -> this service instance is not ready to receive requests. true -> ready for use
+     */
+    private boolean isDark;
+
+    /** default udp discovery address */
+    private static final String DEFAULT_UDP_DISCOVERY_ADDRESS = "228.4.5.6";
+
+    /** default udp discovery port */
+    private static final int DEFAULT_UDP_DISCOVERY_PORT = 5678;
+
+    /** udp discovery address */
+    private String udpDiscoveryAddr = DEFAULT_UDP_DISCOVERY_ADDRESS;
+
+    /** udp discovery port */
+    private int udpDiscoveryPort = DEFAULT_UDP_DISCOVERY_PORT;
+
+    /** default delay between sending passive broadcasts */
+    private static final int DEFAULT_SEND_DELAY_SEC = 60;
+
+    /** delay between sending passive broadcasts */
+    private int sendDelaySec = DEFAULT_SEND_DELAY_SEC;
+
+    /** default amount of time before we remove services that we haven't heard from */
+    private static final int DEFAULT_MAX_IDLE_TIME_SEC = 180;
+
+    /** amount of time before we remove services that we haven't heard from */
+    private int maxIdleTimeSec = DEFAULT_MAX_IDLE_TIME_SEC;
+
+    /**
+     * @param serviceName The serviceName to set.
+     */
+    public void setServiceName( String serviceName )
+    {
+        this.serviceName = serviceName;
+    }
+
+    /**
+     * @return Returns the serviceName.
+     */
+    public String getServiceName()
+    {
+        return serviceName;
+    }
+
+    /**
+     * @param serviceAddress The serviceAddress to set.
+     */
+    public void setServiceAddress( String serviceAddress )
+    {
+        this.serviceAddress = serviceAddress;
+    }
+
+    /**
+     * @return Returns the serviceAddress.
+     */
+    public String getServiceAddress()
+    {
+        return serviceAddress;
+    }
+
+    /**
+     * @param servicePort The servicePort to set.
+     */
+    public void setServicePort( int servicePort )
+    {
+        this.servicePort = servicePort;
+    }
+
+    /**
+     * @return Returns the servicePort.
+     */
+    public int getServicePort()
+    {
+        return servicePort;
+    }
+
+    /**
+     * @param udpDiscoveryAddr The udpDiscoveryAddr to set.
+     */
+    public void setUdpDiscoveryAddr( String udpDiscoveryAddr )
+    {
+        this.udpDiscoveryAddr = udpDiscoveryAddr;
+    }
+
+    /**
+     * @return Returns the udpDiscoveryAddr.
+     */
+    public String getUdpDiscoveryAddr()
+    {
+        return udpDiscoveryAddr;
+    }
+
+    /**
+     * @param udpDiscoveryPort The udpDiscoveryPort to set.
+     */
+    public void setUdpDiscoveryPort( int udpDiscoveryPort )
+    {
+        this.udpDiscoveryPort = udpDiscoveryPort;
+    }
+
+    /**
+     * @return Returns the udpDiscoveryPort.
+     */
+    public int getUdpDiscoveryPort()
+    {
+        return udpDiscoveryPort;
+    }
+
+    /**
+     * @param sendDelaySec The sendDelaySec to set.
+     */
+    public void setSendDelaySec( int sendDelaySec )
+    {
+        this.sendDelaySec = sendDelaySec;
+    }
+
+    /**
+     * @return Returns the sendDelaySec.
+     */
+    public int getSendDelaySec()
+    {
+        return sendDelaySec;
+    }
+
+    /**
+     * @param maxIdleTimeSec The maxIdleTimeSec to set.
+     */
+    public void setMaxIdleTimeSec( int maxIdleTimeSec )
+    {
+        this.maxIdleTimeSec = maxIdleTimeSec;
+    }
+
+    /**
+     * @return Returns the maxIdleTimeSec.
+     */
+    public int getMaxIdleTimeSec()
+    {
+        return maxIdleTimeSec;
+    }
+
+    /**
+     * @return Returns the isDark.
+     */
+    public boolean isDark()
+    {
+        return isDark;
+    }
+
+    /**
+     * @param isDark The isDark to set.
+     */
+    public void setDark( boolean isDark )
+    {
+        this.isDark = isDark;
+    }
+
+    /** @return a clone of this object */
+    @Override
+    public Object clone()
+    {
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setSendDelaySec( this.getSendDelaySec() );
+        attributes.setMaxIdleTimeSec( this.getMaxIdleTimeSec() );
+        attributes.setServiceName( this.getServiceName() );
+        attributes.setServicePort( this.getServicePort() );
+        attributes.setUdpDiscoveryAddr( this.getUdpDiscoveryAddr() );
+        attributes.setUdpDiscoveryPort( this.getUdpDiscoveryPort() );
+        attributes.setDark( this.isDark() );
+        return attributes;
+    }
+
+    /**
+     * @return string for debugging purposes.
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n UDPDiscoveryAttributes" );
+        buf.append( "\n ServiceName = [" + getServiceName() + "]" );
+        buf.append( "\n ServiceAddress = [" + getServiceAddress() + "]" );
+        buf.append( "\n ServicePort = [" + getServicePort() + "]" );
+        buf.append( "\n UdpDiscoveryAddr = [" + getUdpDiscoveryAddr() + "]" );
+        buf.append( "\n UdpDiscoveryPort = [" + getUdpDiscoveryPort() + "]" );
+        buf.append( "\n SendDelaySec = [" + getSendDelaySec() + "]" );
+        buf.append( "\n MaxIdleTimeSec = [" + getMaxIdleTimeSec() + "]" );
+        buf.append( "\n IsDark = [" + isDark() + "]" );
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryManager.java
new file mode 100644
index 0000000..521525d
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryManager.java
@@ -0,0 +1,114 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IProvideScheduler;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This manages UDPDiscovery Services. We should end up with one service per Lateral Cache Manager
+ * Instance. One service works for multiple regions. We don't want a connection for each region.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class UDPDiscoveryManager
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( UDPDiscoveryManager.class );
+
+    /** Singleton instance */
+    private static UDPDiscoveryManager INSTANCE = new UDPDiscoveryManager();
+
+    /** Known services */
+    private final Map<String, UDPDiscoveryService> services = new HashMap<String, UDPDiscoveryService>();
+
+    /** private for singleton */
+    private UDPDiscoveryManager()
+    {
+        // noopt
+    }
+
+    /**
+     * Singleton
+     * <p>
+     * @return UDPDiscoveryManager
+     */
+    public static UDPDiscoveryManager getInstance()
+    {
+        return INSTANCE;
+    }
+
+    /**
+     * Creates a service for the address and port if one doesn't exist already.
+     * <p>
+     * We need to key this using the listener port too. TODO think of making one discovery service
+     * work for multiple types of clients.
+     * <p>
+     * @param discoveryAddress
+     * @param discoveryPort
+     * @param servicePort
+     * @param cacheMgr
+     * @return UDPDiscoveryService
+     */
+    public synchronized UDPDiscoveryService getService( String discoveryAddress, int discoveryPort, int servicePort,
+                                                        ICompositeCacheManager cacheMgr )
+    {
+        String key = discoveryAddress + ":" + discoveryPort + ":" + servicePort;
+
+        UDPDiscoveryService service = services.get( key );
+        if ( service == null )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Creating service for address:port:servicePort [" + key + "]" );
+            }
+
+            UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+            attributes.setUdpDiscoveryAddr( discoveryAddress );
+            attributes.setUdpDiscoveryPort( discoveryPort );
+            attributes.setServicePort( servicePort );
+
+            service = new UDPDiscoveryService( attributes );
+
+            // register for shutdown notification
+            cacheMgr.registerShutdownObserver( service );
+
+            // inject scheduler
+            if ( cacheMgr instanceof IProvideScheduler)
+            {
+                service.setScheduledExecutorService(((IProvideScheduler)cacheMgr).getScheduledExecutorService());
+            }
+
+            services.put( key, service );
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Returning service [" + service + "] for key [" + key + "]" );
+        }
+
+        return service;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryMessage.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryMessage.java
new file mode 100644
index 0000000..efe424c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryMessage.java
@@ -0,0 +1,166 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * The message sent by the discovery mechanism.
+ */
+public class UDPDiscoveryMessage
+    implements Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = -5332377899560951793L;
+
+    public enum BroadcastType
+    {
+        /**
+         * This is the periodic broadcast of a servers location. This type of message is also sent in
+         * response to a REQUEST_BROADCAST.
+         */
+        PASSIVE,
+
+        /**
+         * This asks recipients to broadcast their location. This is used on startup.
+         */
+        REQUEST,
+
+        /**
+         * This message instructs the receiver to remove this service from its list.
+         */
+        REMOVE
+    }
+
+    /** The message type */
+    private BroadcastType messageType = BroadcastType.PASSIVE;
+
+    /** udp port */
+    private int port = 6789;
+
+    /** UDP host */
+    private String host = "228.5.6.7";
+
+    /** Id of the requester, allows self-filtration */
+    private long requesterId;
+
+    /** Names of regions */
+    private ArrayList<String> cacheNames = new ArrayList<String>();
+
+    /**
+     * @param port The port to set.
+     */
+    public void setPort( int port )
+    {
+        this.port = port;
+    }
+
+    /**
+     * @return Returns the port.
+     */
+    public int getPort()
+    {
+        return port;
+    }
+
+    /**
+     * @param host The host to set.
+     */
+    public void setHost( String host )
+    {
+        this.host = host;
+    }
+
+    /**
+     * @return Returns the host.
+     */
+    public String getHost()
+    {
+        return host;
+    }
+
+    /**
+     * @param requesterId The requesterId to set.
+     */
+    public void setRequesterId( long requesterId )
+    {
+        this.requesterId = requesterId;
+    }
+
+    /**
+     * @return Returns the requesterId.
+     */
+    public long getRequesterId()
+    {
+        return requesterId;
+    }
+
+    /**
+     * @param messageType The messageType to set.
+     */
+    public void setMessageType( BroadcastType messageType )
+    {
+        this.messageType = messageType;
+    }
+
+    /**
+     * @return Returns the messageType.
+     */
+    public BroadcastType getMessageType()
+    {
+        return messageType;
+    }
+
+    /**
+     * @param cacheNames The cacheNames to set.
+     */
+    public void setCacheNames( ArrayList<String> cacheNames )
+    {
+        this.cacheNames = cacheNames;
+    }
+
+    /**
+     * @return Returns the cacheNames.
+     */
+    public ArrayList<String> getCacheNames()
+    {
+        return cacheNames;
+    }
+
+    /**
+     * @return debugging string
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n host = [" + host + "]" );
+        buf.append( "\n port = [" + port + "]" );
+        buf.append( "\n requesterId = [" + requesterId + "]" );
+        buf.append( "\n messageType = [" + messageType + "]" );
+        buf.append( "\n Cache Names" );
+        for (String name : cacheNames)
+        {
+            buf.append( " cacheName = [" + name + "]" );
+        }
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryReceiver.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryReceiver.java
new file mode 100644
index 0000000..8ecce5b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryReceiver.java
@@ -0,0 +1,380 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.jcs.utils.discovery.UDPDiscoveryMessage.BroadcastType;
+import org.apache.commons.jcs.utils.threadpool.DaemonThreadFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/** Receives UDP Discovery messages. */
+public class UDPDiscoveryReceiver
+    implements Runnable, IShutdownObserver
+{
+    /** The log factory */
+    private static final Log log = LogFactory.getLog( UDPDiscoveryReceiver.class );
+
+    /** buffer */
+    private final byte[] mBuffer = new byte[65536];
+
+    /** The socket used for communication. */
+    private MulticastSocket mSocket;
+
+    /**
+     * TODO: Consider using the threadpool manager to get this thread pool. For now place a tight
+     * restriction on the pool size
+     */
+    private static final int maxPoolSize = 2;
+
+    /** The processor */
+    private ThreadPoolExecutor pooledExecutor = null;
+
+    /** number of messages received. For debugging and testing. */
+    private int cnt = 0;
+
+    /** Service to get cache names and handle request broadcasts */
+    private UDPDiscoveryService service = null;
+
+    /** Address */
+    private String multicastAddressString = "";
+
+    /** The port */
+    private int multicastPort = 0;
+
+    /** Is it shutdown. */
+    private boolean shutdown = false;
+
+    /**
+     * Constructor for the LateralUDPReceiver object.
+     * <p>
+     * We determine out own host using InetAddress
+     *<p>
+     * @param service
+     * @param multicastAddressString
+     * @param multicastPort
+     * @throws IOException
+     */
+    public UDPDiscoveryReceiver( UDPDiscoveryService service, String multicastAddressString, int multicastPort )
+        throws IOException
+    {
+        this.service = service;
+        this.multicastAddressString = multicastAddressString;
+        this.multicastPort = multicastPort;
+
+        // create a small thread pool to handle a barrage
+        pooledExecutor = (ThreadPoolExecutor)Executors.newFixedThreadPool(maxPoolSize,
+                new DaemonThreadFactory("JCS-UDPDiscoveryReceiver-", Thread.MIN_PRIORITY));
+        pooledExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
+        //pooledExecutor.setMinimumPoolSize(1);
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Constructing listener, [" + this.multicastAddressString + ":" + this.multicastPort + "]" );
+        }
+
+        try
+        {
+            createSocket( this.multicastAddressString, this.multicastPort );
+        }
+        catch ( IOException ioe )
+        {
+            // consider eating this so we can go on, or constructing the socket
+            // later
+            throw ioe;
+        }
+    }
+
+    /**
+     * Creates the socket for this class.
+     * <p>
+     * @param multicastAddressString
+     * @param multicastPort
+     * @throws IOException
+     */
+    private void createSocket( String multicastAddressString, int multicastPort )
+        throws IOException
+    {
+        try
+        {
+            mSocket = new MulticastSocket( multicastPort );
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Joining Group: [" + InetAddress.getByName( multicastAddressString ) + "]" );
+            }
+            mSocket.joinGroup( InetAddress.getByName( multicastAddressString ) );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Could not bind to multicast address [" + InetAddress.getByName( multicastAddressString ) + ":" + multicastPort + "]", e );
+            throw e;
+        }
+    }
+
+    /**
+     * Highly unreliable. If it is processing one message while another comes in, the second
+     * message is lost. This is for low concurrency peppering.
+     * <p>
+     * @return the object message
+     * @throws IOException
+     */
+    public Object waitForMessage()
+        throws IOException
+    {
+        final DatagramPacket packet = new DatagramPacket( mBuffer, mBuffer.length );
+
+        Object obj = null;
+        try
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Waiting for message." );
+            }
+
+            mSocket.receive( packet );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Received packet from address [" + packet.getSocketAddress() + "]" );
+            }
+
+            final ByteArrayInputStream byteStream = new ByteArrayInputStream( mBuffer, 0, packet.getLength() );
+            final ObjectInputStream objectStream = new ObjectInputStreamClassLoaderAware( byteStream, null );
+            obj = objectStream.readObject();
+
+            if ( obj != null && obj instanceof UDPDiscoveryMessage )
+            {
+            	// Ensure that the address we're supposed to send to is, indeed, the address
+            	// of the machine on the other end of this connection.  This guards against
+            	// instances where we don't exactly get the right local host address
+            	UDPDiscoveryMessage msg = (UDPDiscoveryMessage) obj;
+            	msg.setHost(packet.getAddress().getHostAddress());
+
+	            if ( log.isDebugEnabled() )
+	            {
+	                log.debug( "Read object from address [" + packet.getSocketAddress() + "], object=[" + obj + "]" );
+	            }
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Error receiving multicast packet", e );
+        }
+        return obj;
+    }
+
+    /** Main processing method for the LateralUDPReceiver object */
+    @Override
+    public void run()
+    {
+        try
+        {
+            while ( !shutdown )
+            {
+                Object obj = waitForMessage();
+
+                // not thread safe, but just for debugging
+                cnt++;
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( getCnt() + " messages received." );
+                }
+
+                UDPDiscoveryMessage message = null;
+
+                try
+                {
+                    message = (UDPDiscoveryMessage) obj;
+                    // check for null
+                    if ( message != null )
+                    {
+                        MessageHandler handler = new MessageHandler( message );
+
+                        pooledExecutor.execute( handler );
+
+                        if ( log.isDebugEnabled() )
+                        {
+                            log.debug( "Passed handler to executor." );
+                        }
+                    }
+                    else
+                    {
+                        log.warn( "message is null" );
+                    }
+                }
+                catch ( ClassCastException cce )
+                {
+                    log.warn( "Received unknown message type " + cce.getMessage() );
+                }
+            } // end while
+        }
+        catch ( Exception e )
+        {
+            log.error( "Unexpected exception in UDP receiver.", e );
+            try
+            {
+                Thread.sleep( 100 );
+                // TODO consider some failure count so we don't do this
+                // forever.
+            }
+            catch ( Exception e2 )
+            {
+                log.error( "Problem sleeping", e2 );
+            }
+        }
+    }
+
+    /**
+     * @param cnt The cnt to set.
+     */
+    public void setCnt( int cnt )
+    {
+        this.cnt = cnt;
+    }
+
+    /**
+     * @return Returns the cnt.
+     */
+    public int getCnt()
+    {
+        return cnt;
+    }
+
+    /**
+     * Separate thread run when a command comes into the UDPDiscoveryReceiver.
+     */
+    public class MessageHandler
+        implements Runnable
+    {
+        /** The message to handle. Passed in during construction. */
+        private UDPDiscoveryMessage message = null;
+
+        /**
+         * @param message
+         */
+        public MessageHandler( UDPDiscoveryMessage message )
+        {
+            this.message = message;
+        }
+
+        /**
+         * Process the message.
+         */
+        @SuppressWarnings("synthetic-access")
+        @Override
+        public void run()
+        {
+            // consider comparing ports here instead.
+            if ( message.getRequesterId() == CacheInfo.listenerId )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Ignoring message sent from self" );
+                }
+            }
+            else
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Process message sent from another" );
+                    log.debug( "Message = " + message );
+                }
+
+                if ( message.getHost() == null || message.getCacheNames() == null || message.getCacheNames().isEmpty() )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Ignoring invalid message: " + message );
+                    }
+                }
+                else
+                {
+                    processMessage();
+                }
+            }
+        }
+
+        /**
+         * Process the incoming message.
+         */
+        @SuppressWarnings("synthetic-access")
+        private void processMessage()
+        {
+            DiscoveredService discoveredService = new DiscoveredService();
+            discoveredService.setServiceAddress( message.getHost() );
+            discoveredService.setCacheNames( message.getCacheNames() );
+            discoveredService.setServicePort( message.getPort() );
+            discoveredService.setLastHearFromTime( System.currentTimeMillis() );
+
+            // if this is a request message, have the service handle it and
+            // return
+            if ( message.getMessageType() == BroadcastType.REQUEST )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Message is a Request Broadcast, will have the service handle it." );
+                }
+                service.serviceRequestBroadcast();
+                return;
+            }
+            else if ( message.getMessageType() == BroadcastType.REMOVE )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Removing service from set " + discoveredService );
+                }
+                service.removeDiscoveredService( discoveredService );
+            }
+            else
+            {
+                service.addOrUpdateService( discoveredService );
+            }
+        }
+    }
+
+    /** Shuts down the socket. */
+    @Override
+    public void shutdown()
+    {
+        try
+        {
+            shutdown = true;
+            mSocket.leaveGroup( InetAddress.getByName( multicastAddressString ) );
+            mSocket.close();
+            pooledExecutor.shutdownNow();
+        }
+        catch ( IOException e )
+        {
+            log.error( "Problem closing socket" );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySender.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySender.java
new file mode 100644
index 0000000..6b8c9e7
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySender.java
@@ -0,0 +1,292 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheInfo;
+import org.apache.commons.jcs.utils.discovery.UDPDiscoveryMessage.BroadcastType;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.util.ArrayList;
+
+/**
+ * This is a generic sender for the UDPDiscovery process.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class UDPDiscoverySender
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( UDPDiscoverySender.class );
+
+    /** The socket */
+    private MulticastSocket localSocket;
+
+    /** The address */
+    private InetAddress multicastAddress;
+
+    /** The port */
+    private final int multicastPort;
+
+    /** Used to serialize messages */
+    private final StandardSerializer serializer = new StandardSerializer();
+
+    /**
+     * Constructor for the UDPDiscoverySender object
+     * <p>
+     * This sender can be used to send multiple messages.
+     * <p>
+     * When you are done sending, you should destroy the socket sender.
+     * <p>
+     * @param host
+     * @param port
+     * @throws IOException
+     */
+    public UDPDiscoverySender( String host, int port )
+        throws IOException
+    {
+        try
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Constructing socket for sender on port [" + port + "]" );
+            }
+            localSocket = new MulticastSocket( port );
+
+            // Remote address.
+            multicastAddress = InetAddress.getByName( host );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Could not bind to multicast address [" + host + "]", e );
+
+            throw e;
+        }
+
+        multicastPort = port;
+    }
+
+    /**
+     * Closes the socket connection.
+     */
+    public void destroy()
+    {
+        try
+        {
+            if ( this.localSocket != null && !this.localSocket.isClosed() )
+            {
+                this.localSocket.close();
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem destrying sender", e );
+        }
+    }
+
+    /**
+     * Just being careful about closing the socket.
+     * <p>
+     * @throws Throwable
+     */
+    @Override
+    protected void finalize()
+        throws Throwable
+    {
+        super.finalize();
+        destroy();
+    }
+
+    /**
+     * Send messages.
+     * <p>
+     * @param message
+     * @throws IOException
+     */
+    public void send( UDPDiscoveryMessage message )
+        throws IOException
+    {
+        if ( this.localSocket == null )
+        {
+            throw new IOException( "Socket is null, cannot send message." );
+        }
+
+        if ( this.localSocket.isClosed() )
+        {
+            throw new IOException( "Socket is closed, cannot send message." );
+        }
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "sending UDPDiscoveryMessage, address [" + multicastAddress + "], port [" + multicastPort
+                + "], message = " + message );
+        }
+
+        try
+        {
+            final byte[] bytes = serializer.serialize( message );
+
+            // put the byte array in a packet
+            final DatagramPacket packet = new DatagramPacket( bytes, bytes.length, multicastAddress, multicastPort );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Sending DatagramPacket. bytes.length [" + bytes.length + "] to " + multicastAddress + ":"
+                    + multicastPort );
+            }
+
+            localSocket.send( packet );
+        }
+        catch ( IOException e )
+        {
+            log.error( "Error sending message", e );
+            throw e;
+        }
+    }
+
+    /**
+     * Ask other to broadcast their info the the multicast address. If a lateral is non receiving it
+     * can use this. This is also called on startup so we can get info.
+     * <p>
+     * @throws IOException
+     */
+    public void requestBroadcast()
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "sending requestBroadcast " );
+        }
+
+        UDPDiscoveryMessage message = new UDPDiscoveryMessage();
+        message.setRequesterId( CacheInfo.listenerId );
+        message.setMessageType( BroadcastType.REQUEST );
+        send( message );
+    }
+
+    /**
+     * This sends a message broadcasting out that the host and port is available for connections.
+     * <p>
+     * It uses the vmid as the requesterDI
+     * @param host
+     * @param port
+     * @param cacheNames
+     * @throws IOException
+     */
+    public void passiveBroadcast( String host, int port, ArrayList<String> cacheNames )
+        throws IOException
+    {
+        passiveBroadcast( host, port, cacheNames, CacheInfo.listenerId );
+    }
+
+    /**
+     * This allows you to set the sender id. This is mainly for testing.
+     * <p>
+     * @param host
+     * @param port
+     * @param cacheNames names of the cache regions
+     * @param listenerId
+     * @throws IOException
+     */
+    protected void passiveBroadcast( String host, int port, ArrayList<String> cacheNames, long listenerId )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "sending passiveBroadcast " );
+        }
+
+        UDPDiscoveryMessage message = new UDPDiscoveryMessage();
+        message.setHost( host );
+        message.setPort( port );
+        message.setCacheNames( cacheNames );
+        message.setRequesterId( listenerId );
+        message.setMessageType( BroadcastType.PASSIVE );
+        send( message );
+    }
+
+    /**
+     * This sends a message broadcasting our that the host and port is no longer available.
+     * <p>
+     * It uses the vmid as the requesterID
+     * <p>
+     * @param host host
+     * @param port port
+     * @param cacheNames names of the cache regions
+     * @throws IOException on error
+     */
+    public void removeBroadcast( String host, int port, ArrayList<String> cacheNames )
+        throws IOException
+    {
+        removeBroadcast( host, port, cacheNames, CacheInfo.listenerId );
+    }
+
+    /**
+     * This allows you to set the sender id. This is mainly for testing.
+     * <p>
+     * @param host host
+     * @param port port
+     * @param cacheNames names of the cache regions
+     * @param listenerId listener ID
+     * @throws IOException on error
+     */
+    protected void removeBroadcast( String host, int port, ArrayList<String> cacheNames, long listenerId )
+        throws IOException
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "sending removeBroadcast " );
+        }
+
+        UDPDiscoveryMessage message = new UDPDiscoveryMessage();
+        message.setHost( host );
+        message.setPort( port );
+        message.setCacheNames( cacheNames );
+        message.setRequesterId( listenerId );
+        message.setMessageType( BroadcastType.REMOVE );
+        send( message );
+    }
+}
+
+/**
+ * This allows us to get the byte array from an output stream.
+ * <p>
+ * @author asmuts
+ * @created January 15, 2002
+ */
+
+class MyByteArrayOutputStream
+    extends ByteArrayOutputStream
+{
+    /**
+     * Gets the bytes attribute of the MyByteArrayOutputStream object
+     * @return The bytes value
+     */
+    public byte[] getBytes()
+    {
+        return buf;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderThread.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderThread.java
new file mode 100644
index 0000000..1332f3b
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderThread.java
@@ -0,0 +1,199 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+
+/**
+ * Used to periodically broadcast our location to other caches that might be listening.
+ */
+public class UDPDiscoverySenderThread
+    implements Runnable
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( UDPDiscoverySenderThread.class );
+
+    /**
+     * details of the host, port, and service being advertised to listen for TCP socket connections
+     */
+    private final UDPDiscoveryAttributes attributes;
+
+    /** List of known regions. */
+    private ArrayList<String> cacheNames = new ArrayList<String>();
+
+    /**
+     * @param cacheNames The cacheNames to set.
+     */
+    protected void setCacheNames( ArrayList<String> cacheNames )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "Resetting cacheNames = [" + cacheNames + "]" );
+        }
+        this.cacheNames = cacheNames;
+    }
+
+    /**
+     * @return Returns the cacheNames.
+     */
+    protected ArrayList<String> getCacheNames()
+    {
+        return cacheNames;
+    }
+
+    /**
+     * Constructs the sender with the port to tell others to connect to.
+     * <p>
+     * On construction the sender will request that the other caches let it know their addresses.
+     * @param attributes host, port, etc.
+     * @param cacheNames List of strings of the names of the region participating.
+     */
+    public UDPDiscoverySenderThread( UDPDiscoveryAttributes attributes, ArrayList<String> cacheNames )
+    {
+        this.attributes = attributes;
+
+        this.cacheNames = cacheNames;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Creating sender thread for discoveryAddress = [" + attributes.getUdpDiscoveryAddr()
+                + "] and discoveryPort = [" + attributes.getUdpDiscoveryPort() + "] myHostName = ["
+                + attributes.getServiceAddress() + "] and port = [" + attributes.getServicePort() + "]" );
+        }
+
+        UDPDiscoverySender sender = null;
+        try
+        {
+            // move this to the run method and determine how often to call it.
+            sender = new UDPDiscoverySender( attributes.getUdpDiscoveryAddr(), attributes.getUdpDiscoveryPort() );
+            sender.requestBroadcast();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Sent a request broadcast to the group" );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem sending a Request Broadcast", e );
+        }
+        finally
+        {
+            try
+            {
+                if ( sender != null )
+                {
+                    sender.destroy();
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem closing Request Broadcast sender", e );
+            }
+        }
+    }
+
+    /**
+     * Send a message.
+     */
+    @Override
+    public void run()
+    {
+        UDPDiscoverySender sender = null;
+        try
+        {
+            // create this connection each time.
+            // more robust
+            sender = new UDPDiscoverySender( attributes.getUdpDiscoveryAddr(), attributes.getUdpDiscoveryPort() );
+
+            sender.passiveBroadcast( attributes.getServiceAddress(), attributes.getServicePort(), cacheNames );
+
+            // todo we should consider sending a request broadcast every so
+            // often.
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Called sender to issue a passive broadcast" );
+            }
+
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem calling the UDP Discovery Sender [" + attributes.getUdpDiscoveryAddr() + ":"
+                + attributes.getUdpDiscoveryPort() + "]", e );
+        }
+        finally
+        {
+            if (sender != null)
+            {
+                try
+                {
+                    sender.destroy();
+                }
+                catch ( Exception e )
+                {
+                    log.error( "Problem closing Passive Broadcast sender", e );
+                }
+            }
+        }
+    }
+
+    /**
+     * Issues a remove broadcast to the others.
+     */
+    protected void shutdown()
+    {
+        UDPDiscoverySender sender = null;
+        try
+        {
+            // create this connection each time.
+            // more robust
+            sender = new UDPDiscoverySender( attributes.getUdpDiscoveryAddr(), attributes.getUdpDiscoveryPort() );
+
+            sender.removeBroadcast( attributes.getServiceAddress(), attributes.getServicePort(), cacheNames );
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Called sender to issue a remove broadcast in shudown." );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem calling the UDP Discovery Sender", e );
+        }
+        finally
+        {
+            try
+            {
+                if ( sender != null )
+                {
+                    sender.destroy();
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem closing Remote Broadcast sender", e );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryService.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryService.java
new file mode 100644
index 0000000..312d8cf
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryService.java
@@ -0,0 +1,438 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+import org.apache.commons.jcs.utils.discovery.behavior.IDiscoveryListener;
+import org.apache.commons.jcs.utils.net.HostNameUtil;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This service creates a listener that can create lateral caches and add them to the no wait list.
+ * <p>
+ * It also creates a sender that periodically broadcasts its availability.
+ * <p>
+ * The sender also broadcasts a request for other caches to broadcast their addresses.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class UDPDiscoveryService
+    implements IShutdownObserver, IRequireScheduler
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( UDPDiscoveryService.class );
+
+    /** thread that listens for messages */
+    private Thread udpReceiverThread;
+
+    /** the runnable that the receiver thread runs */
+    private UDPDiscoveryReceiver receiver;
+
+    /** the runnable that sends messages via the clock daemon */
+    private UDPDiscoverySenderThread sender = null;
+
+    /** attributes */
+    private UDPDiscoveryAttributes udpDiscoveryAttributes = null;
+
+    /** is this shut down? */
+    private boolean shutdown = false;
+
+    /** This is a set of services that have been discovered. */
+    private Set<DiscoveredService> discoveredServices = new CopyOnWriteArraySet<DiscoveredService>();
+
+    /** This a list of regions that are configured to use discovery. */
+    private final Set<String> cacheNames = new CopyOnWriteArraySet<String>();
+
+    /** Set of listeners. */
+    private final Set<IDiscoveryListener> discoveryListeners = new CopyOnWriteArraySet<IDiscoveryListener>();
+
+    /**
+     * @param attributes
+     */
+    public UDPDiscoveryService( UDPDiscoveryAttributes attributes)
+    {
+        udpDiscoveryAttributes = (UDPDiscoveryAttributes) attributes.clone();
+
+        try
+        {
+            // todo, you should be able to set this
+            udpDiscoveryAttributes.setServiceAddress( HostNameUtil.getLocalHostAddress() );
+        }
+        catch ( UnknownHostException e1 )
+        {
+            log.error( "Couldn't get localhost address", e1 );
+        }
+
+        try
+        {
+            // todo need some kind of recovery here.
+            receiver = new UDPDiscoveryReceiver( this, getUdpDiscoveryAttributes().getUdpDiscoveryAddr(),
+                                                 getUdpDiscoveryAttributes().getUdpDiscoveryPort() );
+            udpReceiverThread = new Thread( receiver );
+            udpReceiverThread.setDaemon( true );
+            // udpReceiverThread.setName( t.getName() + "--UDPReceiver" );
+            udpReceiverThread.start();
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem creating UDPDiscoveryReceiver, address ["
+                + getUdpDiscoveryAttributes().getUdpDiscoveryAddr() + "] port ["
+                + getUdpDiscoveryAttributes().getUdpDiscoveryPort() + "] we won't be able to find any other caches", e );
+        }
+
+        // create a sender thread
+        sender = new UDPDiscoverySenderThread( getUdpDiscoveryAttributes(), getCacheNames() );
+    }
+
+    /**
+     * @see org.apache.commons.jcs.engine.behavior.IRequireScheduler#setScheduledExecutorService(java.util.concurrent.ScheduledExecutorService)
+     */
+    @Override
+    public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutor)
+    {
+        if (sender != null)
+        {
+            scheduledExecutor.scheduleAtFixedRate(sender, 0, 15, TimeUnit.SECONDS);
+        }
+
+        /** removes things that have been idle for too long */
+        UDPCleanupRunner cleanup = new UDPCleanupRunner( this );
+        // I'm going to use this as both, but it could happen
+        // that something could hang around twice the time using this as the
+        // delay and the idle time.
+        scheduledExecutor.scheduleAtFixedRate(cleanup, 0, getUdpDiscoveryAttributes().getMaxIdleTimeSec(), TimeUnit.SECONDS);
+    }
+
+    /**
+     * Send a passive broadcast in response to a request broadcast. Never send a request for a
+     * request. We can respond to our own requests, since a request broadcast is not intended as a
+     * connection request. We might want to only send messages, so we would send a request, but
+     * never a passive broadcast.
+     */
+    protected void serviceRequestBroadcast()
+    {
+        UDPDiscoverySender sender1 = null;
+        try
+        {
+            // create this connection each time.
+            // more robust
+            sender1 = new UDPDiscoverySender( getUdpDiscoveryAttributes().getUdpDiscoveryAddr(),
+                                             getUdpDiscoveryAttributes().getUdpDiscoveryPort() );
+
+            sender1.passiveBroadcast( getUdpDiscoveryAttributes().getServiceAddress(), getUdpDiscoveryAttributes()
+                .getServicePort(), this.getCacheNames() );
+
+            // todo we should consider sending a request broadcast every so
+            // often.
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Called sender to issue a passive broadcast" );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem calling the UDP Discovery Sender. address ["
+                + getUdpDiscoveryAttributes().getUdpDiscoveryAddr() + "] port ["
+                + getUdpDiscoveryAttributes().getUdpDiscoveryPort() + "]", e );
+        }
+        finally
+        {
+            try
+            {
+                if ( sender1 != null )
+                {
+                    sender1.destroy();
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem closing Passive Broadcast sender, while servicing a request broadcast.", e );
+            }
+        }
+    }
+
+    /**
+     * Adds a region to the list that is participating in discovery.
+     * <p>
+     * @param cacheName
+     */
+    public void addParticipatingCacheName( String cacheName )
+    {
+        cacheNames.add( cacheName );
+        sender.setCacheNames( getCacheNames() );
+    }
+
+    /**
+     * Removes the discovered service from the list and calls the discovery listener.
+     * <p>
+     * @param service
+     */
+    public void removeDiscoveredService( DiscoveredService service )
+    {
+        boolean contained = getDiscoveredServices().remove( service );
+
+        if ( contained )
+        {
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Removing " + service );
+            }
+        }
+
+        for (IDiscoveryListener listener : getDiscoveryListeners())
+        {
+            listener.removeDiscoveredService( service );
+        }
+    }
+
+    /**
+     * Add a service to the list. Update the held copy if we already know about it.
+     * <p>
+     * @param discoveredService discovered service
+     */
+    protected void addOrUpdateService( DiscoveredService discoveredService )
+    {
+        synchronized ( getDiscoveredServices() )
+        {
+            // Since this is a set we can add it over an over.
+            // We want to replace the old one, since we may add info that is not part of the equals.
+            // The equals method on the object being added is intentionally restricted.
+            if ( !getDiscoveredServices().contains( discoveredService ) )
+            {
+                if ( log.isInfoEnabled() )
+                {
+                    log.info( "Set does not contain service. I discovered " + discoveredService );
+                }
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Adding service in the set " + discoveredService );
+                }
+                getDiscoveredServices().add( discoveredService );
+            }
+            else
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Set contains service." );
+                }
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Updating service in the set " + discoveredService );
+                }
+
+                // Update the list of cache names if it has changed.
+                DiscoveredService theOldServiceInformation = null;
+                // need to update the time this sucks. add has no effect convert to a map
+                for (DiscoveredService service1 : getDiscoveredServices())
+                {
+                    if ( discoveredService.equals( service1 ) )
+                    {
+                        theOldServiceInformation = service1;
+                        break;
+                    }
+                }
+                if ( theOldServiceInformation != null )
+                {
+                    if ( !theOldServiceInformation.getCacheNames().equals( discoveredService.getCacheNames() ) )
+                    {
+                        if ( log.isInfoEnabled() )
+                        {
+                            log.info( "List of cache names changed for service: " + discoveredService );
+                        }
+                    }
+                }
+
+                // replace it, we want to reset the payload and the last heard from time.
+                getDiscoveredServices().remove( discoveredService );
+                getDiscoveredServices().add( discoveredService );
+            }
+        }
+        // Always Notify the listeners
+        // If we don't do this, then if a region using the default config is initialized after notification,
+        // it will never get the service in it's no wait list.
+        // Leave it to the listeners to decide what to do.
+        for (IDiscoveryListener listener : getDiscoveryListeners())
+        {
+            listener.addDiscoveredService( discoveredService );
+        }
+
+    }
+
+    /**
+     * Get all the cache names we have facades for.
+     * <p>
+     * @return ArrayList
+     */
+    protected ArrayList<String> getCacheNames()
+    {
+        ArrayList<String> names = new ArrayList<String>();
+        names.addAll( cacheNames );
+        return names;
+    }
+
+    /**
+     * @param attr The UDPDiscoveryAttributes to set.
+     */
+    public void setUdpDiscoveryAttributes( UDPDiscoveryAttributes attr )
+    {
+        this.udpDiscoveryAttributes = attr;
+    }
+
+    /**
+     * @return Returns the lca.
+     */
+    public UDPDiscoveryAttributes getUdpDiscoveryAttributes()
+    {
+        return this.udpDiscoveryAttributes;
+    }
+
+    /**
+     * Shuts down the receiver.
+     */
+    @Override
+    public void shutdown()
+    {
+        if ( !shutdown )
+        {
+            shutdown = true;
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Shutting down UDP discovery service receiver." );
+            }
+
+            try
+            {
+                // no good way to do this right now.
+                receiver.shutdown();
+                udpReceiverThread.interrupt();
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem interrupting UDP receiver thread." );
+            }
+
+            if ( log.isInfoEnabled() )
+            {
+                log.info( "Shutting down UDP discovery service sender." );
+            }
+
+            // also call the shutdown on the sender thread itself, which
+            // will result in a remove command.
+            try
+            {
+                sender.shutdown();
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem issuing remove broadcast via UDP sender." );
+            }
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Shutdown already called." );
+            }
+        }
+    }
+
+    /**
+     * Call shutdown to be safe.
+     * <p>
+     * @throws Throwable on error
+     */
+    @Override
+    protected void finalize()
+        throws Throwable
+    {
+        super.finalize();
+
+        // TODO reconsider this, since it uses the logger
+        shutdown();
+    }
+
+    /**
+     * @param discoveredServices The discoveredServices to set.
+     */
+    public synchronized void setDiscoveredServices( Set<DiscoveredService> discoveredServices )
+    {
+        this.discoveredServices = discoveredServices;
+    }
+
+    /**
+     * @return Returns the discoveredServices.
+     */
+    public synchronized Set<DiscoveredService> getDiscoveredServices()
+    {
+        return discoveredServices;
+    }
+
+    /**
+     * @return the discoveryListeners
+     */
+    private Set<IDiscoveryListener> getDiscoveryListeners()
+    {
+        return discoveryListeners;
+    }
+
+    /**
+     * @return the discoveryListeners
+     */
+    public Set<IDiscoveryListener> getCopyOfDiscoveryListeners()
+    {
+        Set<IDiscoveryListener> copy = new HashSet<IDiscoveryListener>();
+        copy.addAll( getDiscoveryListeners() );
+        return copy;
+    }
+
+    /**
+     * Adds a listener.
+     * <p>
+     * @param listener
+     * @return true if it wasn't already in the set
+     */
+    public boolean addDiscoveryListener( IDiscoveryListener listener )
+    {
+        return getDiscoveryListeners().add( listener );
+    }
+
+    /**
+     * Removes a listener.
+     * <p>
+     * @param listener
+     * @return true if it was in the set
+     */
+    public boolean removeDiscoveryListener( IDiscoveryListener listener )
+    {
+        return getDiscoveryListeners().remove( listener );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/behavior/IDiscoveryListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/behavior/IDiscoveryListener.java
new file mode 100644
index 0000000..8a0d9d9
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/discovery/behavior/IDiscoveryListener.java
@@ -0,0 +1,44 @@
+package org.apache.commons.jcs.utils.discovery.behavior;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.utils.discovery.DiscoveredService;
+
+/**
+ * Interface for things that want to listen to discovery events. This will allow discovery to be
+ * used outside of the TCP lateral.
+ */
+public interface IDiscoveryListener
+{
+    /**
+     * Add the service if needed. This does not necessarily mean that the service is not already
+     * added. This can be called if there is a change in service information, such as the cacheNames.
+     * <p>
+     * @param service the service to add
+     */
+    void addDiscoveredService( DiscoveredService service );
+
+    /**
+     * Remove the service from the list.
+     * <p>
+     * @param service the service to remove
+     */
+    void removeDiscoveredService( DiscoveredService service );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/net/HostNameUtil.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/net/HostNameUtil.java
new file mode 100644
index 0000000..7d80df5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/net/HostNameUtil.java
@@ -0,0 +1,147 @@
+package org.apache.commons.jcs.utils.net;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+
+/**
+ * Simple utility for getting the local host name.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class HostNameUtil
+{
+    /** The logger. */
+    private static final Log log = LogFactory.getLog( HostNameUtil.class );
+
+    /**
+     * Gets the address for the local machine.
+     * <p>
+     * @return InetAddress.getLocalHost().getHostAddress()
+     * @throws UnknownHostException
+     */
+    public static String getLocalHostAddress() throws UnknownHostException
+    {
+        try
+        {
+            String hostAddress = getLocalHostLANAddress().getHostAddress();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "hostAddress = [" + hostAddress + "]" );
+            }
+            return hostAddress;
+        }
+        catch ( UnknownHostException e1 )
+        {
+            log.error( "Couldn't get localhost address", e1 );
+            throw e1;
+        }
+    }
+
+    /**
+     * Returns an <code>InetAddress</code> object encapsulating what is most likely the machine's
+     * LAN IP address.
+     * <p>
+     * This method is intended for use as a replacement of JDK method
+     * <code>InetAddress.getLocalHost</code>, because that method is ambiguous on Linux systems.
+     * Linux systems enumerate the loopback network interface the same way as regular LAN network
+     * interfaces, but the JDK <code>InetAddress.getLocalHost</code> method does not specify the
+     * algorithm used to select the address returned under such circumstances, and will often return
+     * the loopback address, which is not valid for network communication. Details <a
+     * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4665037">here</a>.
+     * <p>
+     * This method will scan all IP addresses on all network interfaces on the host machine to
+     * determine the IP address most likely to be the machine's LAN address. If the machine has
+     * multiple IP addresses, this method will prefer a site-local IP address (e.g. 192.168.x.x or
+     * 10.10.x.x, usually IPv4) if the machine has one (and will return the first site-local address
+     * if the machine has more than one), but if the machine does not hold a site-local address,
+     * this method will return simply the first non-loopback address found (IPv4 or IPv6). <p/> If
+     * this method cannot find a non-loopback address using this selection algorithm, it will fall
+     * back to calling and returning the result of JDK method <code>InetAddress.getLocalHost</code>.
+     * <p>
+     * <a href="http://issues.apache.org/jira/browse/JCS-40">JIR ISSUE JCS-40</a>
+     * <p>
+     * @return InetAddress
+     * @throws UnknownHostException If the LAN address of the machine cannot be found.
+     */
+    public static InetAddress getLocalHostLANAddress()
+        throws UnknownHostException
+    {
+        try
+        {
+            InetAddress candidateAddress = null;
+            // Iterate all NICs (network interface cards)...
+            for ( Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); )
+            {
+                NetworkInterface iface = ifaces.nextElement();
+                // Iterate all IP addresses assigned to each card...
+                for ( Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); )
+                {
+                    InetAddress inetAddr = inetAddrs.nextElement();
+                    if ( !inetAddr.isLoopbackAddress() )
+                    {
+                        if ( inetAddr.isSiteLocalAddress() )
+                        {
+                            // Found non-loopback site-local address. Return it immediately...
+                            return inetAddr;
+                        }
+                        else if ( candidateAddress == null )
+                        {
+                            // Found non-loopback address, but not necessarily site-local.
+                            // Store it as a candidate to be returned if site-local address is not subsequently found...
+                            candidateAddress = inetAddr;
+                            // Note that we don't repeatedly assign non-loopback non-site-local addresses as candidates,
+                            // only the first. For subsequent iterations, candidate will be non-null.
+                        }
+                    }
+                }
+            }
+            if ( candidateAddress != null )
+            {
+                // We did not find a site-local address, but we found some other non-loopback address.
+                // Server might have a non-site-local address assigned to its NIC (or it might be running
+                // IPv6 which deprecates the "site-local" concept).
+                // Return this non-loopback candidate address...
+                return candidateAddress;
+            }
+            // At this point, we did not find a non-loopback address.
+            // Fall back to returning whatever InetAddress.getLocalHost() returns...
+            InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
+            if ( jdkSuppliedAddress == null )
+            {
+                throw new UnknownHostException( "The JDK InetAddress.getLocalHost() method unexpectedly returned null." );
+            }
+            return jdkSuppliedAddress;
+        }
+        catch ( Exception e )
+        {
+            UnknownHostException unknownHostException = new UnknownHostException( "Failed to determine LAN address: "
+                + e );
+            unknownHostException.initCause( e );
+            throw unknownHostException;
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/AbstractPropertyContainer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/AbstractPropertyContainer.java
new file mode 100644
index 0000000..0c5f293
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/AbstractPropertyContainer.java
@@ -0,0 +1,190 @@
+package org.apache.commons.jcs.utils.props;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.ConfigurationException;
+
+import java.util.Properties;
+
+/**
+ * Provides a mechanism to load properties into objects.
+ * <p>
+ * Functions that depend on properties should call ensureProperties() before it uses any properties.
+ */
+public abstract class AbstractPropertyContainer
+{
+    /** File, db, etc */
+    private static final PropertiesFactory DEFAULT_PROPERTIES_FACTORY = new PropertiesFactoryFileImpl();
+
+    /**
+     * A property group is a subsection of properties. It's sent to the properties factory to
+     * specify which group of properties to pull back. This will probably mean different things to
+     * different property factories. For PropertiesFactoryFileImpl, the propertiesGroup maps to a
+     * filename.
+     */
+    private String propertiesGroup;
+
+    /**
+     * The property heading is used to specify a starting point in the properties object. This is
+     * used so that settings can be relative to this propertiesHeading, as opposed to being
+     * statically coded. There's no enforcement of this, but users are encouraged to call
+     * getProperties().get( getPropertiesHeading() + ".foo" );
+     */
+    private String propertiesHeading;
+
+    /** The factory to use. */
+    private PropertiesFactory propertiesFactory;
+
+    /** The loaded properties. */
+    private Properties properties;
+
+    /**
+     * Makes sure an AbstractPropertyClass has all the properties it needs.
+     * <p>
+     * Synchronized mutators so multiple threads cannot cause problems. We wouldn't want the
+     * properties heading to get changed as we were processing the properties.
+     * <p>
+     * @throws ConfigurationException on configuration failure
+     */
+    public synchronized void ensureProperties()
+        throws ConfigurationException
+    {
+        if ( getProperties() == null )
+        {
+            initializeProperties();
+        }
+    }
+
+    /**
+     * Loads the properties and then calls handleProperties. Typically, you don't need to call this.
+     * This is primarily intended for reinitialization.
+     * <p>
+     * If the properties object is null, when you call ensureProperties initialize will be called.
+     * <p>
+     * @throws ConfigurationException on configuration failure
+     */
+    public synchronized void initializeProperties()
+        throws ConfigurationException
+    {
+        loadProperties();
+
+        handleProperties();
+    }
+
+    /**
+     * This loads the properties regardless of whether or not they have already been loaded.
+     * <p>
+     * @throws ConfigurationException on configuration failure
+     */
+    private void loadProperties()
+        throws ConfigurationException
+    {
+        if ( getPropertiesGroup() == null )
+        {
+            throw new ConfigurationException( "Properties group is null and it shouldn't be" );
+        }
+
+        if ( getPropertiesHeading() == null )
+        {
+            throw new ConfigurationException( "Properties heading is null and it shouldn't be" );
+        }
+
+        if ( getPropertiesFactory() == null )
+        {
+            setProperties( DEFAULT_PROPERTIES_FACTORY.getProperties( getPropertiesGroup() ) );
+        }
+        else
+        {
+            setProperties( getPropertiesFactory().getProperties( getPropertiesGroup() ) );
+        }
+    }
+
+    /**
+     * Sets fields for properties, and verifies that all necessary properties are there.
+     * <p>
+     * @throws ConfigurationException on configuration failure
+     */
+    protected abstract void handleProperties()
+        throws ConfigurationException;
+
+    /**
+     * @return Returns the properties.
+     */
+    public synchronized Properties getProperties()
+    {
+        return properties;
+    }
+
+    /**
+     * @param properties The properties to set.
+     */
+    public synchronized void setProperties( Properties properties )
+    {
+        this.properties = properties;
+    }
+
+    /**
+     * @return Returns the propertiesHeading.
+     */
+    public synchronized String getPropertiesHeading()
+    {
+        return propertiesHeading;
+    }
+
+    /**
+     * @param propertiesHeading The propertiesHeading to set.
+     */
+    public synchronized void setPropertiesHeading( String propertiesHeading )
+    {
+        this.propertiesHeading = propertiesHeading;
+    }
+
+    /**
+     * @return Returns the propertiesFactory.
+     */
+    public PropertiesFactory getPropertiesFactory()
+    {
+        return propertiesFactory;
+    }
+
+    /**
+     * @param propertiesFactory The propertiesFactory to set.
+     */
+    public void setPropertiesFactory( PropertiesFactory propertiesFactory )
+    {
+        this.propertiesFactory = propertiesFactory;
+    }
+
+    /**
+     * @return Returns the propertiesGroup.
+     */
+    public String getPropertiesGroup()
+    {
+        return propertiesGroup;
+    }
+
+    /**
+     * @param propertiesGroup The propertiesGroup to set.
+     */
+    public void setPropertiesGroup( String propertiesGroup )
+    {
+        this.propertiesGroup = propertiesGroup;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactory.java
new file mode 100644
index 0000000..96e40de
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactory.java
@@ -0,0 +1,36 @@
+package org.apache.commons.jcs.utils.props;
+
+/*
+ * 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.
+ */
+
+import java.util.Properties;
+
+/**
+ * Retrieves properties from a configurable source.
+ */
+public interface PropertiesFactory
+{
+    /**
+     * Fetches a set of properties for the specified group.
+     * <p>
+     * @param groupName the group to pull properties from.
+     * @return a properties object.
+     */
+    Properties getProperties( String groupName );
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactoryFileImpl.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactoryFileImpl.java
new file mode 100644
index 0000000..5630ca4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertiesFactoryFileImpl.java
@@ -0,0 +1,40 @@
+package org.apache.commons.jcs.utils.props;
+
+/*
+ * 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.
+ */
+
+import java.util.Properties;
+
+/**
+ * Goes to the file system to load a properties file.
+ */
+public class PropertiesFactoryFileImpl
+    implements PropertiesFactory
+{
+    /**
+     * Loads the properties using the property loader.
+     * @param groupName property group name
+     * @return Properties
+     */
+    @Override
+    public Properties getProperties( String groupName )
+    {
+        return PropertyLoader.loadProperties( groupName );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertyLoader.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertyLoader.java
new file mode 100644
index 0000000..1baaf0a
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/props/PropertyLoader.java
@@ -0,0 +1,174 @@
+package org.apache.commons.jcs.utils.props;
+
+/*
+ * 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.
+ */
+
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * I modified this class to work with .ccf files in particular. I also removed
+ * the resource bundle functionality.
+ * <p>
+ * A simple class for loading java.util.Properties backed by .ccf files deployed
+ * as classpath resources. See individual methods for details.
+ * <p>
+ * The original source is from:
+ * <p>
+ * @author (C) <a
+ *         href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad
+ *         Roubtsov </a>, 2003
+ */
+public abstract class PropertyLoader
+{
+    /** throw an error if we can load the file */
+    private static final boolean THROW_ON_LOAD_FAILURE = true;
+
+    /** File suffix. */
+    private static final String SUFFIX = ".ccf";
+
+    /** property suffix */
+    private static final String SUFFIX_PROPERTIES = ".properties";
+
+    /**
+     * Looks up a resource named 'name' in the classpath. The resource must map
+     * to a file with .ccf extention. The name is assumed to be absolute and can
+     * use either "/" or "." for package segment separation with an optional
+     * leading "/" and optional ".ccf" suffix.
+     * <p>
+     * The suffix ".ccf" will be appended if it is not set. This can also handle
+     * .properties files
+     * <p>
+     * Thus, the following names refer to the same resource:
+     *
+     * <pre>
+     *
+     *       some.pkg.Resource
+     *       some.pkg.Resource.ccf
+     *       some/pkg/Resource
+     *       some/pkg/Resource.ccf
+     *       /some/pkg/Resource
+     *       /some/pkg/Resource.ccf
+     * </pre>
+     *
+     * @param name
+     *            classpath resource name [may not be null]
+     * @param loader
+     *            classloader through which to load the resource [null is
+     *            equivalent to the application loader]
+     * @return resource converted to java.util.properties [may be null if the
+     *         resource was not found and THROW_ON_LOAD_FAILURE is false]
+     * @throws IllegalArgumentException
+     *             if the resource was not found and THROW_ON_LOAD_FAILURE is
+     *             true
+     */
+    public static Properties loadProperties( String name, ClassLoader loader )
+    {
+        boolean isCCFSuffix = true;
+
+        if ( name == null )
+            throw new IllegalArgumentException( "null input: name" );
+
+        ClassLoader classLoader = ( loader == null ) ? ClassLoader.getSystemClassLoader() : loader;
+
+        String fileName = name.startsWith( "/" ) ? name.substring( 1 ) : name;
+
+        if ( fileName.endsWith( SUFFIX ) )
+        {
+            fileName = fileName.substring( 0, fileName.length() - SUFFIX.length() );
+        }
+
+        if ( fileName.endsWith( SUFFIX_PROPERTIES ) )
+        {
+            fileName = fileName.substring( 0, fileName.length() - SUFFIX_PROPERTIES.length() );
+            isCCFSuffix = false;
+        }
+
+        Properties result = null;
+
+        InputStream in = null;
+        try
+        {
+            fileName = fileName.replace( '.', '/' );
+
+            if ( !fileName.endsWith( SUFFIX ) && isCCFSuffix )
+            {
+                fileName = fileName.concat( SUFFIX );
+            }
+            else if ( !fileName.endsWith( SUFFIX_PROPERTIES ) && !isCCFSuffix )
+            {
+                fileName = fileName.concat( SUFFIX_PROPERTIES );
+            }
+
+            // returns null on lookup failures:
+            in = classLoader.getResourceAsStream( fileName );
+            if ( in != null )
+            {
+                result = new Properties();
+                result.load( in ); // can throw IOException
+            }
+        }
+        catch ( Exception e )
+        {
+            result = null;
+        }
+        finally
+        {
+            if ( in != null )
+                try
+                {
+                    in.close();
+                }
+                catch ( Throwable ignore )
+                {
+                    // swallow
+                }
+        }
+
+        if ( THROW_ON_LOAD_FAILURE && result == null )
+        {
+            throw new IllegalArgumentException( "could not load [" + fileName + "]" + " as " + "a classloader resource" );
+        }
+
+        return result;
+    }
+
+    /**
+     * A convenience overload of {@link #loadProperties(String, ClassLoader)}
+     * that uses the current thread's context classloader. A better strategy
+     * would be to use techniques shown in
+     * http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
+     * <p>
+     * @param name
+     * @return Properties
+     */
+    public static Properties loadProperties( final String name )
+    {
+        return loadProperties( name, Thread.currentThread().getContextClassLoader() );
+    }
+
+    /**
+     * Can't use this one.
+     */
+    private PropertyLoader()
+    {
+        super();
+    } // this class is not extentible
+
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/CompressingSerializer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/CompressingSerializer.java
new file mode 100644
index 0000000..e9f6af6
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/CompressingSerializer.java
@@ -0,0 +1,126 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+import org.apache.commons.jcs.utils.zip.CompressionUtil;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Performs default serialization and de-serialization. It gzips the value.
+ */
+public class CompressingSerializer
+    implements IElementSerializer
+{
+    /**
+     * Serializes an object using default serialization. Compresses the byte array.
+     * <p>
+     * @param obj object
+     * @return byte[]
+     * @throws IOException on i/o problem
+     */
+    @Override
+    public <T> byte[] serialize( T obj )
+        throws IOException
+    {
+        byte[] uncompressed = serializeObject( obj );
+        byte[] compressed = CompressionUtil.compressByteArray( uncompressed );
+        return compressed;
+    }
+
+    /**
+     * Does the basic serialization.
+     * <p>
+     * @param obj object
+     * @return byte[]
+     * @throws IOException on i/o problem
+     */
+    protected <T> byte[] serializeObject( T obj )
+        throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream( baos );
+        try
+        {
+            oos.writeObject( obj );
+        }
+        finally
+        {
+            oos.close();
+        }
+        byte[] uncompressed = baos.toByteArray();
+        return uncompressed;
+    }
+
+    /**
+     * Uses default de-serialization to turn a byte array into an object. Decompresses the value
+     * first. All exceptions are converted into IOExceptions.
+     * <p>
+     * @param data bytes of data
+     * @return Object
+     * @throws IOException on i/o problem
+     * @throws ClassNotFoundException if class is not found during deserialization
+     */
+    @Override
+    public <T> T deSerialize( byte[] data, ClassLoader loader )
+        throws IOException, ClassNotFoundException
+    {
+        if ( data == null )
+        {
+            return null;
+        }
+        byte[] decompressedByteArray = CompressionUtil.decompressByteArray( data );
+        return deserializeObject( decompressedByteArray );
+    }
+
+    /**
+     * Does the standard deserialization.
+     * <p>
+     * @param decompressedByteArray array of decompressed bytes
+     * @return Object
+     * @throws IOException on i/o error
+     * @throws ClassNotFoundException if class is not found during deserialization
+     */
+    protected <T> T deserializeObject( byte[] decompressedByteArray )
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayInputStream bais = new ByteArrayInputStream( decompressedByteArray );
+        BufferedInputStream bis = new BufferedInputStream( bais );
+        ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( bis, null );
+
+        try
+        {
+            @SuppressWarnings("unchecked") // Need to cast from Object
+            T readObject = (T) ois.readObject();
+            return readObject;
+        }
+        finally
+        {
+            ois.close();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtil.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtil.java
new file mode 100644
index 0000000..35c945c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtil.java
@@ -0,0 +1,147 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * This uses a supplied Serializer to convert to and from cache elements.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class SerializationConversionUtil
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( SerializationConversionUtil.class );
+
+    /**
+     * This returns a wrapper that has a serialized version of the value instead
+     * of the value.
+     * <p>
+     * @param element
+     * @param elementSerializer
+     *            the serializer to be used.
+     * @return null for null;
+     * @throws IOException
+     */
+    public static <K, V> ICacheElementSerialized<K, V> getSerializedCacheElement( ICacheElement<K, V> element,
+                                                                    IElementSerializer elementSerializer )
+        throws IOException
+    {
+        if ( element == null )
+        {
+            return null;
+        }
+
+        byte[] serializedValue = null;
+
+        // if it has already been serialized, don't do it again.
+        if ( element instanceof ICacheElementSerialized )
+        {
+            serializedValue = ( (ICacheElementSerialized<K, V>) element ).getSerializedValue();
+        }
+        else
+        {
+            if ( elementSerializer != null )
+            {
+                try
+                {
+                    serializedValue = elementSerializer.serialize( element.getVal() );
+                }
+                catch ( IOException e )
+                {
+                    log.error( "Problem serializing object.", e );
+                    throw e;
+                }
+            }
+            else
+            {
+                // we could just use the default.
+                log.warn( "ElementSerializer is null.  Could not serialize object." );
+                throw new IOException( "Could not serialize object.  The ElementSerializer is null." );
+            }
+        }
+        ICacheElementSerialized<K, V> serialized = new CacheElementSerialized<K, V>(
+                element.getCacheName(), element.getKey(), serializedValue, element.getElementAttributes() );
+
+        return serialized;
+    }
+
+    /**
+     * This returns a wrapper that has a de-serialized version of the value
+     * instead of the serialized value.
+     * <p>
+     * @param serialized
+     * @param elementSerializer
+     *            the serializer to be used.
+     * @return null for null;
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    public static <K, V> ICacheElement<K, V> getDeSerializedCacheElement( ICacheElementSerialized<K, V> serialized,
+                                                            IElementSerializer elementSerializer )
+        throws IOException, ClassNotFoundException
+    {
+        if ( serialized == null )
+        {
+            return null;
+        }
+
+        V deSerializedValue = null;
+
+        if ( elementSerializer != null )
+        {
+            try
+            {
+                try
+                {
+                    deSerializedValue = elementSerializer.deSerialize( serialized.getSerializedValue(), null );
+                }
+                catch ( ClassNotFoundException e )
+                {
+                    log.error( "Problem de-serializing object.", e );
+                    throw e;
+                }
+            }
+            catch ( IOException e )
+            {
+                log.error( "Problem de-serializing object.", e );
+                throw e;
+            }
+        }
+        else
+        {
+            // we could just use the default.
+            log.warn( "ElementSerializer is null.  Could not serialize object." );
+        }
+        ICacheElement<K, V> deSerialized = new CacheElement<K, V>( serialized.getCacheName(), serialized.getKey(), deSerializedValue );
+        deSerialized.setElementAttributes( serialized.getElementAttributes() );
+
+        return deSerialized;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/StandardSerializer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/StandardSerializer.java
new file mode 100644
index 0000000..455b962
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/serialization/StandardSerializer.java
@@ -0,0 +1,91 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.io.ObjectInputStreamClassLoaderAware;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Performs default serialization and de-serialization.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class StandardSerializer
+    implements IElementSerializer
+{
+    /**
+     * Serializes an object using default serialization.
+     * <p>
+     * @param obj
+     * @return byte[]
+     * @throws IOException
+     */
+    @Override
+    public <T> byte[] serialize( T obj )
+        throws IOException
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream( baos );
+        try
+        {
+            oos.writeObject( obj );
+        }
+        finally
+        {
+            oos.close();
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Uses default de-serialization to turn a byte array into an object. All exceptions are
+     * converted into IOExceptions.
+     * <p>
+     * @param data
+     * @return Object
+     * @throws IOException
+     * @throws ClassNotFoundException
+     */
+    @Override
+    public <T> T deSerialize( byte[] data, ClassLoader loader )
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayInputStream bais = new ByteArrayInputStream( data );
+        BufferedInputStream bis = new BufferedInputStream( bais );
+        ObjectInputStream ois = new ObjectInputStreamClassLoaderAware( bis, loader );
+        try
+        {
+            @SuppressWarnings("unchecked") // Need to cast from Object
+            T readObject = (T) ois.readObject();
+            return readObject;
+        }
+        finally
+        {
+            ois.close();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/servlet/JCSServletContextListener.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/servlet/JCSServletContextListener.java
new file mode 100644
index 0000000..08eb14e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/servlet/JCSServletContextListener.java
@@ -0,0 +1,87 @@
+package org.apache.commons.jcs.utils.servlet;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * If you add this to the context listeners section of your web.xml file, this will shutdown JCS
+ * gracefully.
+ * <p>
+ * Add the following to the top of your web.xml file.
+ *
+ * <pre>
+ *  <listener>
+ *  <listener-class>
+ *  org.apache.commons.jcs.utils.servlet.JCSServletContextListener
+ *  </listener-class>
+ *  </listener>
+ * </pre>
+ * @author Aaron Smuts
+ */
+public class JCSServletContextListener
+    implements ServletContextListener
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( JCSServletContextListener.class );
+
+    /**
+     * This does nothing. We don't want to initialize the cache here.
+     * <p>
+     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
+     */
+    @Override
+    public void contextInitialized( ServletContextEvent arg0 )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "contextInitialized" );
+        }
+    }
+
+    /**
+     * This gets the singleton instance of the CompositeCacheManager and calls shutdown.
+     * <p>
+     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
+     */
+    @Override
+    public void contextDestroyed( ServletContextEvent arg0 )
+    {
+        if ( log.isInfoEnabled() )
+        {
+            log.info( "contextDestroyed, shutting down JCS." );
+        }
+
+        try
+        {
+            CompositeCacheManager.getInstance().shutDown();
+        }
+        catch (CacheException e)
+        {
+            log.error( "Could not retrieve cache manager instance", e );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/BoundedQueue.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/BoundedQueue.java
new file mode 100644
index 0000000..73e4cd5
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/BoundedQueue.java
@@ -0,0 +1,94 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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 is a bounded queue. It only allows maxSize items.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BoundedQueue<T>
+{
+    /** Queue size limit. */
+    private final int maxSize;
+
+    /** The list backing the queue */
+    private final DoubleLinkedList<DoubleLinkedListNode<T>> list =
+        new DoubleLinkedList<DoubleLinkedListNode<T>>();
+
+    /**
+     * Initialize the bounded queue.
+     * <p>
+     * @param maxSize
+     */
+    public BoundedQueue( int maxSize )
+    {
+        this.maxSize = maxSize;
+    }
+
+    /**
+     * Adds an item to the end of the queue, which is the front of the list.
+     * <p>
+     * @param object
+     */
+    public void add( T object )
+    {
+        if ( list.size() >= maxSize )
+        {
+            list.removeLast();
+        }
+        list.addFirst( new DoubleLinkedListNode<T>( object ) );
+    }
+
+    /**
+     * Takes the last of the underlying double linked list.
+     * <p>
+     * @return null if it is epmpty.
+     */
+    public T take()
+    {
+        DoubleLinkedListNode<T> node = list.removeLast();
+        if ( node != null )
+        {
+            return node.getPayload();
+        }
+        return null;
+    }
+
+    /**
+     * Return the number of items in the queue.
+     * <p>
+     * @return size
+     */
+    public int size()
+    {
+        return list.size();
+    }
+
+    /**
+     * Return true if the size is <= 0;
+     * <p>
+     * @return true is size <= 0;
+     */
+    public boolean isEmpty()
+    {
+        return list.size() <= 0;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedList.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedList.java
new file mode 100644
index 0000000..e83a82c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedList.java
@@ -0,0 +1,303 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is a generic thread safe double linked list. It's very simple and all the operations are so
+ * quick that course grained synchronization is more than acceptable.
+ */
+ at SuppressWarnings({ "unchecked", "rawtypes" }) // Don't know how to resolve this with generics
+public class DoubleLinkedList<T extends DoubleLinkedListNode>
+{
+    /** record size to avoid having to iterate */
+    private int size = 0;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( DoubleLinkedList.class );
+
+    /** LRU double linked list head node */
+    private T first;
+
+    /** LRU double linked list tail node */
+    private T last;
+
+    /**
+     * Default constructor.
+     */
+    public DoubleLinkedList()
+    {
+        super();
+    }
+
+    /**
+     * Adds a new node to the end of the link list.
+     * <p>
+     * @param me The feature to be added to the Last
+     */
+    public synchronized void addLast(T me)
+    {
+        if ( first == null )
+        {
+            // empty list.
+            first = me;
+        }
+        else
+        {
+            last.next = me;
+            me.prev = last;
+        }
+        last = me;
+        size++;
+    }
+
+    /**
+     * Adds a new node to the start of the link list.
+     * <p>
+     * @param me The feature to be added to the First
+     */
+    public synchronized void addFirst(T me)
+    {
+        if ( last == null )
+        {
+            // empty list.
+            last = me;
+        }
+        else
+        {
+            first.prev = me;
+            me.next = first;
+        }
+        first = me;
+        size++;
+    }
+
+    /**
+     * Returns the last node from the link list, if there are any nodes.
+     * <p>
+     * @return The last node.
+     */
+    public synchronized T getLast()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "returning last node" );
+        }
+        return last;
+    }
+
+    /**
+     * Removes the specified node from the link list.
+     * <p>
+     * @return DoubleLinkedListNode, the first node.
+     */
+    public synchronized T getFirst()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "returning first node" );
+        }
+        return first;
+    }
+
+    /**
+     * Moves an existing node to the start of the link list.
+     * <p>
+     * @param ln The node to set as the head.
+     */
+    public synchronized void makeFirst(T ln)
+    {
+        if ( ln.prev == null )
+        {
+            // already the first node. or not a node
+            return;
+        }
+        // splice: remove it from the list
+        ln.prev.next = ln.next;
+
+        if ( ln.next == null )
+        {
+            // last but not the first.
+            last = (T) ln.prev;
+            last.next = null;
+        }
+        else
+        {
+            // neither the last nor the first.
+            ln.next.prev = ln.prev;
+        }
+        first.prev = ln;
+        ln.next = first;
+        ln.prev = null;
+        first = ln;
+    }
+
+    /**
+     * Moves an existing node to the end of the link list.
+     * <p>
+     * @param ln The node to set as the head.
+     */
+    public synchronized void makeLast(T ln)
+    {
+        if ( ln.next == null )
+        {
+            // already the last node. or not a node
+            return;
+        }
+        // splice: remove it from the list
+        if ( ln.prev != null )
+        {
+            ln.prev.next = ln.next;
+        }
+        else
+        {
+            // first
+            first = last;
+        }
+
+        if ( last != null )
+        {
+            last.next = ln;
+        }
+        ln.prev = last;
+        ln.next = null;
+        last = ln;
+    }
+
+    /**
+     * Remove all of the elements from the linked list implementation.
+     */
+    public synchronized void removeAll()
+    {
+        for (T me = first; me != null; )
+        {
+            if ( me.prev != null )
+            {
+                me.prev = null;
+            }
+            T next = (T) me.next;
+            me = next;
+        }
+        first = last = null;
+        // make sure this will work, could be add while this is happening.
+        size = 0;
+    }
+
+    /**
+     * Removes the specified node from the link list.
+     * <p>
+     * @param me Description of the Parameter
+     * @return true if an element was removed.
+     */
+    public synchronized boolean remove(T me)
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing node" );
+        }
+
+        if ( me.next == null )
+        {
+            if ( me.prev == null )
+            {
+                // Make sure it really is the only node before setting head and
+                // tail to null. It is possible that we will be passed a node
+                // which has already been removed from the list, in which case
+                // we should ignore it
+
+                if ( me == first && me == last )
+                {
+                    first = last = null;
+                }
+            }
+            else
+            {
+                // last but not the first.
+                last = (T) me.prev;
+                last.next = null;
+                me.prev = null;
+            }
+        }
+        else if ( me.prev == null )
+        {
+            // first but not the last.
+            first = (T) me.next;
+            first.prev = null;
+            me.next = null;
+        }
+        else
+        {
+            // neither the first nor the last.
+            me.prev.next = me.next;
+            me.next.prev = me.prev;
+            me.prev = me.next = null;
+        }
+        size--;
+
+        return true;
+    }
+
+    /**
+     * Removes the specified node from the link list.
+     * <p>
+     * @return The last node if there was one to remove.
+     */
+    public synchronized T removeLast()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing last node" );
+        }
+        T temp = last;
+        if ( last != null )
+        {
+            remove( last );
+        }
+        return temp;
+    }
+
+    /**
+     * Returns the size of the list.
+     * <p>
+     * @return int
+     */
+    public synchronized int size()
+    {
+        return size;
+    }
+
+    // ///////////////////////////////////////////////////////////////////
+    /**
+     * Dump the cache entries from first to list for debugging.
+     */
+    public synchronized void debugDumpEntries()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "dumping Entries" );
+            for (T me = first; me != null; me = (T) me.next)
+            {
+                log.debug( "dump Entries> payload= '" + me.getPayload() + "'" );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListNode.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListNode.java
new file mode 100644
index 0000000..26d080e
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListNode.java
@@ -0,0 +1,62 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * This serves as a placeholder in a double linked list. You can extend this to
+ * add functionality. This allows you to remove in constant time from a linked
+ * list.
+ * <p>
+ * It simply holds the payload and a reference to the items before and after it
+ * in the list.
+ */
+public class DoubleLinkedListNode<T>
+    implements Serializable
+{
+    /** Dont' change. */
+    private static final long serialVersionUID = -1114934407695836097L;
+
+    /** The object in the node. */
+    private final T payload;
+
+    /** Double Linked list references */
+    public DoubleLinkedListNode<T> prev;
+
+    /** Double Linked list references */
+    public DoubleLinkedListNode<T> next;
+
+    /**
+     * @param payloadP
+     */
+    public DoubleLinkedListNode(T payloadP)
+    {
+        payload = payloadP;
+    }
+
+    /**
+     * @return Object
+     */
+    public T getPayload()
+    {
+        return payload;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUElementDescriptor.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUElementDescriptor.java
new file mode 100644
index 0000000..f728466
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUElementDescriptor.java
@@ -0,0 +1,60 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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 is a node in the double linked list. It is stored as the value in the underlying map used by
+ * the LRUMap class.
+ */
+public class LRUElementDescriptor<K, V>
+    extends DoubleLinkedListNode<V>
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 8249555756363020156L;
+
+    /** The key value */
+    private K key;
+
+    /**
+     * @param key
+     * @param payloadP
+     */
+    public LRUElementDescriptor(K key, V payloadP)
+    {
+        super(payloadP);
+        this.setKey(key);
+    }
+
+    /**
+     * @param key The key to set.
+     */
+    public void setKey(K key)
+    {
+        this.key = key;
+    }
+
+    /**
+     * @return Returns the key.
+     */
+    public K getKey()
+    {
+        return key;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMap.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMap.java
new file mode 100644
index 0000000..78caf2c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMap.java
@@ -0,0 +1,698 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.stats.StatElement;
+import org.apache.commons.jcs.engine.stats.Stats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * This is a simple LRUMap. It implements most of the map methods. It is not recommended that you
+ * use any but put, get, remove, and clear.
+ * <p>
+ * Children can implement the processRemovedLRU method if they want to handle the removal of the
+ * lest recently used item.
+ * <p>
+ * This class was abstracted out of the LRU Memory cache. Put, remove, and get should be thread
+ * safe. It uses a hashtable and our own double linked list.
+ * <p>
+ * Locking is done on the instance.
+ * <p>
+ * @author aaron smuts
+ */
+public class LRUMap<K, V>
+    implements Map<K, V>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( LRUMap.class );
+
+    /** double linked list for lru */
+    private final DoubleLinkedList<LRUElementDescriptor<K, V>> list;
+
+    /** Map where items are stored by key. */
+    private Map<K, LRUElementDescriptor<K, V>> map;
+
+    /** stats */
+    int hitCnt = 0;
+
+    /** stats */
+    int missCnt = 0;
+
+    /** stats */
+    int putCnt = 0;
+
+    /** if the max is less than 0, there is no limit! */
+    int maxObjects = -1;
+
+    /** make configurable */
+    private int chunkSize = 1;
+
+    private final Lock lock = new ReentrantLock();
+
+    /**
+     * This creates an unbounded version. Setting the max objects will result in spooling on
+     * subsequent puts.
+     */
+    public LRUMap()
+    {
+        list = new DoubleLinkedList<LRUElementDescriptor<K, V>>();
+
+        // normal hshtable is faster for
+        // sequential keys.
+        map = new ConcurrentHashMap<K, LRUElementDescriptor<K, V>>();
+        // map = new ConcurrentHashMap();
+    }
+
+    /**
+     * This sets the size limit.
+     * <p>
+     * @param maxObjects
+     */
+    public LRUMap( int maxObjects )
+    {
+        this();
+        this.maxObjects = maxObjects;
+    }
+
+    /**
+     * This simply returned the number of elements in the map.
+     * <p>
+     * @see java.util.Map#size()
+     */
+    @Override
+    public int size()
+    {
+        return map.size();
+    }
+
+    /**
+     * This removes all the items. It clears the map and the double linked list.
+     * <p>
+     * @see java.util.Map#clear()
+     */
+    @Override
+    public void clear()
+    {
+        lock.lock();
+        try
+        {
+            map.clear();
+            list.removeAll();
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Returns true if the map is empty.
+     * <p>
+     * @see java.util.Map#isEmpty()
+     */
+    @Override
+    public boolean isEmpty()
+    {
+        return map.size() == 0;
+    }
+
+    /**
+     * Returns true if the map contains an element for the supplied key.
+     * <p>
+     * @see java.util.Map#containsKey(java.lang.Object)
+     */
+    @Override
+    public boolean containsKey( Object key )
+    {
+        return map.containsKey( key );
+    }
+
+    /**
+     * This is an expensive operation that determines if the object supplied is mapped to any key.
+     * <p>
+     * @see java.util.Map#containsValue(java.lang.Object)
+     */
+    @Override
+    public boolean containsValue( Object value )
+    {
+        return map.containsValue( value );
+    }
+
+    /**
+     * @return map.values();
+     */
+    @Override
+    public Collection<V> values()
+    {
+        List<V> valueList = new ArrayList<V>(map.size());
+
+        for (LRUElementDescriptor<K, V> value : map.values())
+        {
+            valueList.add(value.getPayload());
+        }
+
+        return valueList;
+    }
+
+    /**
+     * @param source
+     */
+    @Override
+    public void putAll( Map<? extends K, ? extends V> source )
+    {
+        if ( source != null )
+        {
+            for (Map.Entry<? extends K, ? extends V> entry : source.entrySet())
+            {
+                this.put( entry.getKey(), entry.getValue() );
+            }
+        }
+    }
+
+    /**
+     * @param key
+     * @return Object
+     */
+    @Override
+    public V get( Object key )
+    {
+        V retVal = null;
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "getting item  for key " + key );
+        }
+
+        LRUElementDescriptor<K, V> me = map.get( key );
+
+        if ( me != null )
+        {
+            hitCnt++;
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "LRUMap hit for " + key );
+            }
+
+            retVal = me.getPayload();
+
+            list.makeFirst( me );
+        }
+        else
+        {
+            missCnt++;
+            log.debug( "LRUMap miss for " + key );
+        }
+
+        // verifyCache();
+        return retVal;
+    }
+
+    /**
+     * This gets an element out of the map without adjusting it's posisiton in the LRU. In other
+     * words, this does not count as being used. If the element is the last item in the list, it
+     * will still be the last itme in the list.
+     * <p>
+     * @param key
+     * @return Object
+     */
+    public V getQuiet( Object key )
+    {
+        V ce = null;
+
+        LRUElementDescriptor<K, V> me = map.get( key );
+        if ( me != null )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "LRUMap quiet hit for " + key );
+            }
+
+            ce = me.getPayload();
+        }
+        else if ( log.isDebugEnabled() )
+        {
+            log.debug( "LRUMap quiet miss for " + key );
+        }
+
+        return ce;
+    }
+
+    /**
+     * @param key
+     * @return Object removed
+     */
+    @Override
+    public V remove( Object key )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "removing item for key: " + key );
+        }
+
+        // remove single item.
+        lock.lock();
+        try
+        {
+            LRUElementDescriptor<K, V> me = map.remove(key);
+
+            if (me != null)
+            {
+                list.remove(me);
+                return me.getPayload();
+            }
+        }
+        finally
+        {
+            lock.unlock();
+        }
+
+        return null;
+    }
+
+    /**
+     * @param key
+     * @param value
+     * @return Object
+     */
+    @Override
+    public V put(K key, V value)
+    {
+        putCnt++;
+
+        LRUElementDescriptor<K, V> old = null;
+        lock.lock();
+        try
+        {
+            // TODO address double synchronization of addFirst, use write lock
+            addFirst( key, value );
+            // this must be synchronized
+            LRUElementDescriptor<K, V> first = list.getFirst();
+            old = map.put(first.getKey(), first);
+
+            // If the node was the same as an existing node, remove it.
+            if ( old != null && first.getKey().equals(old.getKey()))
+            {
+                list.remove( old );
+            }
+        }
+        finally
+        {
+            lock.unlock();
+        }
+
+        int size = map.size();
+        // If the element limit is reached, we need to spool
+
+        if ( this.maxObjects >= 0 && size > this.maxObjects )
+        {
+            final boolean debugEnabled = log.isDebugEnabled();
+            if (debugEnabled)
+            {
+                log.debug( "In memory limit reached, removing least recently used." );
+            }
+
+            // Write the last 'chunkSize' items to disk.
+            int chunkSizeCorrected = Math.min( size, getChunkSize() );
+
+            if (debugEnabled)
+            {
+                log.debug( "About to remove the least recently used. map size: " + size + ", max objects: "
+                    + this.maxObjects + ", items to spool: " + chunkSizeCorrected );
+            }
+
+            // The spool will put them in a disk event queue, so there is no
+            // need to pre-queue the queuing. This would be a bit wasteful
+            // and wouldn't save much time in this synchronous call.
+
+            for ( int i = 0; i < chunkSizeCorrected; i++ )
+            {
+                lock.lock();
+                try
+                {
+                    LRUElementDescriptor<K, V> last = list.getLast();
+                    if (last != null)
+                    {
+                        processRemovedLRU(last.getKey(), last.getPayload());
+                        if (map.remove(last.getKey()) == null)
+                        {
+                            log.warn("update: remove failed for key: "
+                                    + last.getKey());
+                            verifyCache();
+                        }
+                        list.removeLast();
+                    }
+                    else
+                    {
+                        verifyCache();
+                        throw new Error("update: last is null!");
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "update: After spool map size: " + map.size() );
+            }
+            if ( map.size() != dumpCacheSize() )
+            {
+                log.error("update: After spool, size mismatch: map.size() = " + map.size() + ", linked list size = "
+                        + dumpCacheSize());
+            }
+        }
+
+        if ( old != null )
+        {
+            return old.getPayload();
+        }
+        return null;
+    }
+
+    /**
+     * Adds a new node to the start of the link list.
+     * <p>
+     * @param key
+     * @param val The feature to be added to the First
+     */
+    private void addFirst(K key, V val)
+    {
+        lock.lock();
+        try
+        {
+            LRUElementDescriptor<K, V> me = new LRUElementDescriptor<K, V>(key, val);
+            list.addFirst( me );
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Returns the size of the list.
+     * <p>
+     * @return int
+     */
+    private int dumpCacheSize()
+    {
+        return list.size();
+    }
+
+    /**
+     * Dump the cache entries from first to list for debugging.
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    public void dumpCacheEntries()
+    {
+        log.debug( "dumpingCacheEntries" );
+        for ( LRUElementDescriptor<K, V> me = list.getFirst(); me != null; me = (LRUElementDescriptor<K, V>) me.next )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "dumpCacheEntries> key=" + me.getKey() + ", val=" + me.getPayload() );
+            }
+        }
+    }
+
+    /**
+     * Dump the cache map for debugging.
+     */
+    public void dumpMap()
+    {
+        log.debug( "dumpingMap" );
+        for (Map.Entry<K, LRUElementDescriptor<K, V>> e : map.entrySet())
+        {
+            LRUElementDescriptor<K, V> me = e.getValue();
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "dumpMap> key=" + e.getKey() + ", val=" + me.getPayload() );
+            }
+        }
+    }
+
+    /**
+     * Checks to see if all the items that should be in the cache are. Checks consistency between
+     * List and map.
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    protected void verifyCache()
+    {
+        if ( !log.isDebugEnabled() )
+        {
+            return;
+        }
+
+        boolean found = false;
+        log.debug( "verifycache: mapContains " + map.size() + " elements, linked list contains " + dumpCacheSize()
+            + " elements" );
+        log.debug( "verifycache: checking linked list by key " );
+        for (LRUElementDescriptor<K, V> li = list.getFirst(); li != null; li = (LRUElementDescriptor<K, V>) li.next )
+        {
+            K key = li.getKey();
+            if ( !map.containsKey( key ) )
+            {
+                log.error( "verifycache: map does not contain key : " + li.getKey() );
+                log.error( "li.hashcode=" + li.getKey().hashCode() );
+                log.error( "key class=" + key.getClass() );
+                log.error( "key hashcode=" + key.hashCode() );
+                log.error( "key toString=" + key.toString() );
+                if ( key instanceof GroupAttrName )
+                {
+                    GroupAttrName<?> name = (GroupAttrName<?>) key;
+                    log.error( "GroupID hashcode=" + name.groupId.hashCode() );
+                    log.error( "GroupID.class=" + name.groupId.getClass() );
+                    log.error( "AttrName hashcode=" + name.attrName.hashCode() );
+                    log.error( "AttrName.class=" + name.attrName.getClass() );
+                }
+                dumpMap();
+            }
+            else if ( map.get( li.getKey() ) == null )
+            {
+                log.error( "verifycache: linked list retrieval returned null for key: " + li.getKey() );
+            }
+        }
+
+        log.debug( "verifycache: checking linked list by value " );
+        for (LRUElementDescriptor<K, V> li3 = list.getFirst(); li3 != null; li3 = (LRUElementDescriptor<K, V>) li3.next )
+        {
+            if ( map.containsValue( li3 ) == false )
+            {
+                log.error( "verifycache: map does not contain value : " + li3 );
+                dumpMap();
+            }
+        }
+
+        log.debug( "verifycache: checking via keysets!" );
+        for (Iterator<K> itr2 = map.keySet().iterator(); itr2.hasNext(); )
+        {
+            found = false;
+            Serializable val = null;
+            try
+            {
+                val = (Serializable) itr2.next();
+            }
+            catch ( NoSuchElementException nse )
+            {
+                log.error( "verifycache: no such element exception" );
+                continue;
+            }
+
+            for (LRUElementDescriptor<K, V> li2 = list.getFirst(); li2 != null; li2 = (LRUElementDescriptor<K, V>) li2.next )
+            {
+                if ( val.equals( li2.getKey() ) )
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if ( !found )
+            {
+                log.error( "verifycache: key not found in list : " + val );
+                dumpCacheEntries();
+                if ( map.containsKey( val ) )
+                {
+                    log.error( "verifycache: map contains key" );
+                }
+                else
+                {
+                    log.error( "verifycache: map does NOT contain key, what the HECK!" );
+                }
+            }
+        }
+    }
+
+    /**
+     * Logs an error is an element that should be in the cache is not.
+     * <p>
+     * @param key
+     */
+    @SuppressWarnings("unchecked") // No generics for public fields
+    protected void verifyCache( Object key )
+    {
+        if ( !log.isDebugEnabled() )
+        {
+            return;
+        }
+
+        boolean found = false;
+
+        // go through the linked list looking for the key
+        for (LRUElementDescriptor<K, V> li = list.getFirst(); li != null; li = (LRUElementDescriptor<K, V>) li.next )
+        {
+            if ( li.getKey() == key )
+            {
+                found = true;
+                log.debug( "verifycache(key) key match: " + key );
+                break;
+            }
+        }
+        if ( !found )
+        {
+            log.error( "verifycache(key), couldn't find key! : " + key );
+        }
+    }
+
+    /**
+     * This is called when an item is removed from the LRU. We just log some information.
+     * <p>
+     * Children can implement this method for special behavior.
+     * @param key
+     * @param value
+     */
+    protected void processRemovedLRU(K key, V value )
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Removing key: [" + key + "] from LRUMap store, value = [" + value + "]" );
+            log.debug( "LRUMap store size: '" + this.size() + "'." );
+        }
+    }
+
+    /**
+     * The chunk size is the number of items to remove when the max is reached. By default it is 1.
+     * <p>
+     * @param chunkSize The chunkSize to set.
+     */
+    public void setChunkSize( int chunkSize )
+    {
+        this.chunkSize = chunkSize;
+    }
+
+    /**
+     * @return Returns the chunkSize.
+     */
+    public int getChunkSize()
+    {
+        return chunkSize;
+    }
+
+    /**
+     * @return IStats
+     */
+    public IStats getStatistics()
+    {
+        IStats stats = new Stats();
+        stats.setTypeName( "LRUMap" );
+
+        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
+
+        elems.add(new StatElement<Integer>( "List Size", Integer.valueOf(list.size()) ) );
+        elems.add(new StatElement<Integer>( "Map Size", Integer.valueOf(map.size()) ) );
+        elems.add(new StatElement<Integer>( "Put Count", Integer.valueOf(putCnt) ) );
+        elems.add(new StatElement<Integer>( "Hit Count", Integer.valueOf(hitCnt) ) );
+        elems.add(new StatElement<Integer>( "Miss Count", Integer.valueOf(missCnt) ) );
+
+        stats.setStatElements( elems );
+
+        return stats;
+    }
+
+    /**
+     * This returns a set of entries. Our LRUMapEntry is used since the value stored in the
+     * underlying map is a node in the double linked list. We wouldn't want to return this to the
+     * client, so we construct a new entry with the payload of the node.
+     * <p>
+     * TODO we should return out own set wrapper, so we can avoid the extra object creation if it
+     * isn't necessary.
+     * <p>
+     * @see java.util.Map#entrySet()
+     */
+    @Override
+    public Set<Map.Entry<K, V>> entrySet()
+    {
+        lock.lock();
+        try
+        {
+            // todo, we should return a defensive copy
+            Set<Map.Entry<K, LRUElementDescriptor<K, V>>> entries = map.entrySet();
+
+            Set<Map.Entry<K, V>> unWrapped = new HashSet<Map.Entry<K, V>>();
+
+            for (Map.Entry<K, LRUElementDescriptor<K, V>> pre : entries) {
+                Map.Entry<K, V> post = new LRUMapEntry<K, V>(pre.getKey(), pre.getValue().getPayload());
+                unWrapped.add(post);
+            }
+
+            return unWrapped;
+        }
+        finally
+        {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * @return map.keySet();
+     */
+    @Override
+    public Set<K> keySet()
+    {
+        // TODO fix this, it needs to return the keys inside the wrappers.
+        return map.keySet();
+    }
+
+    /**
+     * @return the max objects size.
+     */
+    public int getMaxObjects()
+    {
+        return maxObjects;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMapEntry.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMapEntry.java
new file mode 100644
index 0000000..5747244
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/LRUMapEntry.java
@@ -0,0 +1,82 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+import java.util.Map.Entry;
+
+/**
+ * Entry for the LRUMap.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class LRUMapEntry<K, V>
+    implements Entry<K, V>, Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = -8176116317739129331L;
+
+    /** key */
+    private final K key;
+
+    /** value */
+    private V value;
+
+    /**
+     * S
+     * @param key
+     * @param value
+     */
+    public LRUMapEntry(K key, V value)
+    {
+        this.key = key;
+        this.value = value;
+    }
+
+    /**
+     * @return key
+     */
+    @Override
+    public K getKey()
+    {
+        return this.key;
+    }
+
+    /**
+     * @return value
+     */
+    @Override
+    public V getValue()
+    {
+        return this.value;
+    }
+
+    /**
+     * @param valueArg
+     * @return the old value
+     */
+    @Override
+    public V setValue(V valueArg)
+    {
+        V old = this.value;
+        this.value = valueArg;
+        return old;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SingleLinkedList.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SingleLinkedList.java
new file mode 100644
index 0000000..0e0f5f3
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SingleLinkedList.java
@@ -0,0 +1,137 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is an basic thread safe single linked list. It provides very limited functionality. It is
+ * small and fast.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class SingleLinkedList<T>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( SingleLinkedList.class );
+
+    /** for sync */
+    private final Object lock = new Object();
+
+    /** the head of the queue */
+    private Node<T> head = new Node<T>();
+
+    /** the end of the queue */
+    private Node<T> tail = head;
+
+    /** The size of the list */
+    private int size = 0;
+
+    /**
+     * Takes the first item off the list.
+     * <p>
+     * @return null if the list is empty.
+     */
+    public T takeFirst()
+    {
+        synchronized ( lock )
+        {
+            // wait until there is something to read
+            if ( head == tail )
+            {
+                return null;
+            }
+
+            Node<T> node = head.next;
+
+            T value = node.payload;
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "head.payload = " + head.payload );
+                log.debug( "node.payload = " + node.payload );
+            }
+
+            // Node becomes the new head (head is always empty)
+
+            node.payload = null;
+            head = node;
+
+            size--;
+            return value;
+        }
+    }
+
+    /**
+     * Adds an item to the end of the list.
+     * <p>
+     * @param payload
+     */
+    public void addLast( T payload )
+    {
+        Node<T> newNode = new Node<T>();
+
+        newNode.payload = payload;
+
+        synchronized ( lock )
+        {
+            size++;
+            tail.next = newNode;
+            tail = newNode;
+        }
+    }
+
+    /**
+     * Removes everything.
+     */
+    public void clear()
+    {
+        synchronized ( lock )
+        {
+            head = tail;
+            size = 0;
+        }
+    }
+
+    /**
+     * The list is composed of nodes.
+     * <p>
+     * @author Aaron Smuts
+     */
+    protected static class Node<T>
+    {
+        /** next in the list */
+        Node<T> next = null;
+
+        /** The data in this node */
+        T payload;
+    }
+
+    /**
+     * Returns the number of elements in the list.
+     * <p>
+     * @return number of items in the list.
+     */
+    public int size()
+    {
+        return size;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SortedPreferentialArray.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SortedPreferentialArray.java
new file mode 100644
index 0000000..165cf29
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/struct/SortedPreferentialArray.java
@@ -0,0 +1,612 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This maintains a sorted array with a preferential replacement policy when full.
+ * <p>
+ * Insertion time is n, search is log(n)
+ * <p>
+ * Clients must manage thread safety on previous version. I synchronized the public methods to add
+ * easy thread safety. I synchronized all public methods that make modifications.
+ */
+public class SortedPreferentialArray<T extends Comparable<? super T>>
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( SortedPreferentialArray.class );
+
+    /** prefer large means that the smallest will be removed when full. */
+    private boolean preferLarge = true;
+
+    /** maximum number allowed */
+    private int maxSize = 0;
+
+    /** The currency number */
+    private int curSize = 0;
+
+    /** The primary array */
+    private final T[] array;
+
+    /** the number that have been inserted. */
+    private int insertCnt = 0;
+
+    /**
+     * Construct the array with the maximum size.
+     * <p>
+     * @param maxSize int
+     */
+    public SortedPreferentialArray( int maxSize )
+    {
+        this.maxSize = maxSize;
+        @SuppressWarnings("unchecked") // No generic arrays in java
+        T[] ts = (T[]) new Comparable<?>[maxSize];
+        array = ts;
+    }
+
+    /**
+     * If the array is full this will remove the smallest if preferLarge==true and if obj is bigger,
+     * or the largest if preferLarge=false and obj is smaller than the largest.
+     * <p>
+     * @param obj Object
+     */
+    public synchronized void add(T obj)
+    {
+        if ( obj == null )
+        {
+            return;
+        }
+
+        if ( curSize < maxSize )
+        {
+            insert( obj );
+            return;
+        }
+        if ( preferLarge )
+        {
+            // insert if obj is larger than the smallest
+            T sma = getSmallest();
+            if ( obj.compareTo( sma ) > 0 )
+            {
+                insert( obj );
+                return;
+            }
+            // obj is less than or equal to the smallest.
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "New object is smaller than or equal to the smallest" );
+            }
+            return;
+        }
+        // Not preferLarge
+        T lar = getLargest();
+        // insert if obj is smaller than the largest
+        int diff = obj.compareTo( lar );
+        if ( diff > 0 || diff == 0 )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "New object is larger than or equal to the largest" );
+            }
+            return;
+        }
+        // obj is less than the largest.
+        insert( obj );
+    }
+
+    /**
+     * Returns the largest without removing it from the array.
+     * <p>
+     * @return Comparable
+     */
+    public synchronized T getLargest()
+    {
+        return array[curSize - 1];
+    }
+
+    /**
+     * Returns the smallest element without removing it from the array.
+     * <p>
+     * @return Comparable
+     */
+    public synchronized T getSmallest()
+    {
+        return array[0];
+    }
+
+    /**
+     * Insert looks for the nearest largest. It then determines which way to shuffle depending on
+     * the preference.
+     * <p>
+     * @param obj Comparable
+     */
+    private void insert(T obj)
+    {
+        try
+        {
+            int nLar = findNearestLargerEqualOrLastPosition( obj );
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "nLar = " + nLar + " obj = " + obj );
+            }
+
+            if ( nLar == curSize )
+            {
+                // this next check should be unnecessary
+                // findNearestLargerPosition should only return the curSize if
+                // there is
+                // room left. Check to be safe
+                if ( curSize < maxSize )
+                {
+                    array[nLar] = obj;
+                    curSize++;
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( this.dumpArray() );
+                    }
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Inserted object at the end of the array" );
+                    }
+                    return;
+                } // end if not full
+            }
+
+            boolean isFull = false;
+            if ( curSize == maxSize )
+            {
+                isFull = true;
+            }
+
+            // The array is full, we must replace
+            // remove smallest or largest to determine whether to
+            // shuffle left or right to insert
+            if ( preferLarge )
+            {
+                if ( isFull )
+                {
+                    // is full, prefer larger, remove smallest by shifting left
+                    int pnt = nLar - 1; // set iteration stop point
+                    for ( int i = 0; i < pnt; i++ )
+                    {
+                        array[i] = array[i + 1];
+                    }
+                    // use nLar-1 for insertion point
+                    array[nLar - 1] = obj;
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Inserted object at " + ( nLar - 1 ) );
+                    }
+                }
+                else
+                {
+                    // not full, shift right from spot
+                    int pnt = nLar; // set iteration stop point
+                    for ( int i = curSize; i > pnt; i-- )
+                    {
+                        array[i] = array[i - 1];
+                    }
+                    // use nLar-1 for insertion point
+                    array[nLar] = obj;
+                    curSize++;
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( "Inserted object at " + ( nLar ) );
+                    }
+                }
+            }
+            else
+            {
+                // prefer smaller, remove largest by shifting right
+                // use nLar for insertion point
+                int pnt = nLar + 1;
+                if ( !isFull )
+                {
+                    pnt = nLar;
+                }
+                for ( int i = curSize; i > pnt; i-- )
+                {
+                    array[i] = array[i - 1];
+                }
+                array[nLar] = obj;
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Inserted object at " + nLar );
+                }
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( this.dumpArray() );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Insertion problem" + this.dumpArray(), e );
+        }
+
+        insertCnt++;
+        if ( insertCnt % 100 == 0 )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( this.dumpArray() );
+            }
+        }
+    }
+
+    /**
+     * Determines whether the preference is for large or small.
+     * <p>
+     * @param pref boolean
+     */
+    public synchronized void setPreferLarge( boolean pref )
+    {
+        preferLarge = pref;
+    }
+
+    /**
+     * Returns and removes the nearer larger or equal object from the aray.
+     * <p>
+     * @param obj Comparable
+     * @return Comparable, null if arg is null or none was found.
+     */
+    public synchronized T takeNearestLargerOrEqual( T obj )
+    {
+        if ( obj == null )
+        {
+            return null;
+        }
+
+        T retVal = null;
+        try
+        {
+            int pos = findNearestOccupiedLargerOrEqualPosition( obj );
+            if ( pos == -1 )
+            {
+                return null;
+            }
+
+            try
+            {
+                retVal = array[pos];
+                remove( pos );
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem removing from array. pos [" + pos + "] " + obj, e );
+            }
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "obj = " + obj + " || retVal = " + retVal );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "Take problem" + this.dumpArray(), e );
+        }
+        return retVal;
+    }
+
+    /**
+     * Returns the current size of the array.
+     * <p>
+     * @return int
+     */
+    public synchronized int size()
+    {
+        return this.curSize;
+    }
+
+    /**
+     * This determines the position in the array that is occupied by an object that is larger or
+     * equal to the argument. If none exists, -1 is returned.
+     * <p>
+     * @param obj Object
+     * @return Object
+     */
+    private int findNearestOccupiedLargerOrEqualPosition(T obj)
+    {
+        if ( curSize == 0 )
+        {
+            // nothing in the array
+            return -1;
+        }
+
+        // this gives us an insert position.
+        int pos = findNearestLargerEqualOrLastPosition( obj );
+
+        // see if the previous will do to handle the empty insert spot position
+        if ( pos == curSize )
+        { // && curSize < maxSize ) {
+            // pos will be > 0 if it equals curSize, we check for this above.
+            if ( obj.compareTo(array[pos - 1] ) <= 0 )
+            {
+                pos = pos - 1;
+            }
+            else
+            {
+                pos = -1;
+            }
+        }
+        else
+        {
+            // the find nearest, returns the last, since it is used by insertion.
+            if ( obj.compareTo(array[pos] ) > 0 )
+            {
+                return -1;
+            }
+        }
+
+        return pos;
+    }
+
+    /**
+     * This method determines the position where an insert should take place for a given object.
+     * With some additional checking, this can also be used to find an object matching or greater
+     * than the argument.
+     * <p>
+     * If the array is not full and the current object is larger than all the rest the first open
+     * slot at the end will be returned.
+     * <p>
+     * NOTE: If the object is larger than the largest and it is full, it will return the last position.
+     * <p>
+     * If the array is empty, the first spot is returned.
+     * <p>
+     * If the object is smaller than all the rests, the first position is returned. The caller must
+     * decide what to do given the preference.
+     * <p>
+     * Returns the position of the object nearest to or equal to the larger object.
+     * <p>
+     * If you want to find the takePosition, you have to calculate it.
+     * findNearestOccupiedLargerOrEqualPosition will calculate this for you.
+     * <p>
+     * @param obj Comparable
+     * @return int
+     */
+    private int findNearestLargerEqualOrLastPosition(T obj)
+    {
+        // do nothing if a null was passed in
+        if ( obj == null )
+        {
+            return -1;
+        }
+
+        // return the first spot if the array is empty
+        if ( curSize <= 0 )
+        {
+            return 0;
+        }
+
+        // mark the numer to be returned, the greaterPos as unset
+        int greaterPos = -1;
+        // prepare for a binary search
+        int curPos = ( curSize - 1 ) / 2;
+        int prevPos = -1;
+
+        try
+        {
+            // set the loop exit flag to false
+            boolean done = false;
+
+            // check the ends
+            // return insert position 0 if obj is smaller
+            // than the smallest. the caller can determine what to
+            // do with this, depending on the preference setting
+            if ( obj.compareTo( getSmallest() ) <= 0 )
+            {
+                // LESS THAN OR EQUAL TO SMALLEST
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( obj + " is smaller than or equal to " + getSmallest() );
+                }
+                greaterPos = 0;
+                done = true;
+                // return greaterPos;
+            }
+            else
+            {
+                // GREATER THAN SMALLEST
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( obj + " is bigger than " + getSmallest() );
+                }
+
+                // return the largest position if obj is larger
+                // than the largest. the caller can determine what to
+                // do with this, depending on the preference setting
+                if ( obj.compareTo( getLargest() ) >= 0 )
+                {
+                    if ( curSize == maxSize )
+                    {
+                        // there is no room left in the array, return the last
+                        // spot
+                        greaterPos = curSize - 1;
+                        done = true;
+                    }
+                    else
+                    {
+                        // there is room left in the array
+                        greaterPos = curSize;
+                        done = true;
+                    }
+                }
+                else
+                {
+                    // the obj is less than or equal to the largest, so we know that the
+                    // last item is larger or equal
+                    greaterPos = curSize - 1;
+                }
+            }
+
+            // /////////////////////////////////////////////////////////////////////
+            // begin binary search for insertion spot
+            while ( !done )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "\n curPos = " + curPos + "; greaterPos = " + greaterPos + "; prevpos = " + prevPos );
+                }
+
+                // get out of loop if we have come to the end or passed it
+                if ( curPos == prevPos || curPos >= curSize )
+                {
+                    done = true;
+                    break;
+                }
+                else
+
+                // EQUAL TO
+                // object at current position is equal to the obj, use this,
+                // TODO could avoid some shuffling if I found a lower pos.
+                if (array[curPos].compareTo( obj ) == 0 )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( array[curPos] + " is equal to " + obj );
+                    }
+                    greaterPos = curPos;
+                    done = true;
+                    break;
+                }
+                else
+
+                // GREATER THAN
+                // array object at current position is greater than the obj, go
+                // left
+                if (array[curPos].compareTo( obj ) > 0 )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( array[curPos] + " is greater than " + obj );
+                    }
+                    // set the smallest greater equal to the current position
+                    greaterPos = curPos;
+                    // set the current position to
+                    // set the previous position to the current position
+                    // We could have an integer overflow, but this array could
+                    // never get that large.
+                    int newPos = Math.min( curPos, ( curPos + prevPos ) / 2 );
+                    prevPos = curPos;
+                    curPos = newPos;
+                }
+                else
+
+                // LESS THAN
+                // the object at the current position is smaller, go right
+                if (array[curPos].compareTo( obj ) < 0 )
+                {
+                    if ( log.isDebugEnabled() )
+                    {
+                        log.debug( array[curPos] + " is less than " + obj );
+                    }
+                    if ( ( greaterPos != -1 ) && greaterPos - curPos < 0 )
+                    {
+                        done = true;
+                        break; // return greaterPos;
+                    }
+                    else
+                    {
+                        int newPos = 0;
+                        if ( prevPos > curPos )
+                        {
+                            newPos = Math.min( ( curPos + prevPos ) / 2, curSize );
+                        }
+                        else if ( prevPos == -1 )
+                        {
+                            newPos = Math.min( ( curSize + curPos ) / 2, curSize );
+                        }
+                        prevPos = curPos;
+                        curPos = newPos;
+                    }
+                }
+            } // end while
+            // /////////////////////////////////////////////////////////////////////
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Greater Position is [" + greaterPos + "]" + " array[greaterPos] [" + array[greaterPos]
+                    + "]" );
+            }
+        }
+        catch ( Exception e )
+        {
+            log.error( "\n curPos = " + curPos + "; greaterPos = " + greaterPos + "; prevpos = " + prevPos + " "
+                + this.dumpArray(), e );
+        }
+
+        return greaterPos;
+    }
+
+    /**
+     * Removes the item from the array at the specified position. The remaining items to the right
+     * are shifted left.
+     * <p>
+     * @param position int
+     * @throw IndexOutOfBoundsException if position is out of range.
+     */
+    private void remove( int position )
+    {
+        if ( position >= curSize || position < 0 )
+        {
+            throw new IndexOutOfBoundsException( "position=" + position + " must be less than curSize=" + curSize );
+        }
+        curSize--;
+
+        if ( position < curSize )
+        {
+            try
+            {
+                System.arraycopy( array, position + 1, array, position, ( curSize - position ) );
+            }
+            catch ( IndexOutOfBoundsException ibe )
+            {
+                // throw this, log details for debugging. This shouldn't happen.
+                log.warn( "Caught index out of bounds exception. "
+                    + "called 'System.arraycopy( array, position + 1, array, position, (curSize - position) );'  "
+                    + "array.lengh [" + array.length + "] position [" + position + "] curSize [" + curSize + "]" );
+                throw ibe;
+            }
+        }
+    }
+
+    /**
+     * Debugging method to return a human readable display of array data.
+     * <p>
+     * @return String representation of the contents.
+     */
+    protected synchronized String dumpArray()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "\n ---------------------------" );
+        buf.append( "\n curSize = " + curSize );
+        buf.append( "\n array.length = " + array.length );
+        buf.append( "\n ---------------------------" );
+        buf.append( "\n Dump:" );
+        for ( int i = 0; i < curSize; i++ )
+        {
+            buf.append( "\n " + i + "=" + array[i] );
+        }
+        return buf.toString();
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/DaemonThreadFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/DaemonThreadFactory.java
new file mode 100644
index 0000000..e1ec271
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/DaemonThreadFactory.java
@@ -0,0 +1,74 @@
+package org.apache.commons.jcs.utils.threadpool;
+
+/*
+ * 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.
+ */
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Allows us to set the daemon status on the threads.
+ * <p>
+ * @author aaronsm
+ */
+public class DaemonThreadFactory
+    implements ThreadFactory
+{
+    private String prefix;
+    private boolean threadIsDaemon = true;
+    private int threadPriority = Thread.NORM_PRIORITY;
+
+    /**
+     * Constructor
+     *
+     * @param prefix thread name prefix
+     */
+    public DaemonThreadFactory(String prefix)
+    {
+        this(prefix, Thread.NORM_PRIORITY);
+    }
+
+    /**
+     * Constructor
+     *
+     * @param prefix thread name prefix
+     * @param threadPriority set thread priority
+     */
+    public DaemonThreadFactory(String prefix, int threadPriority)
+    {
+        this.prefix = prefix;
+        this.threadPriority = threadPriority;
+    }
+
+    /**
+     * Sets the thread to daemon.
+     * <p>
+     * @param runner
+     * @return a daemon thread
+     */
+    @Override
+    public Thread newThread( Runnable runner )
+    {
+        Thread t = new Thread( runner );
+        String oldName = t.getName();
+        t.setName( prefix + oldName );
+        t.setDaemon(threadIsDaemon);
+        t.setPriority(threadPriority);
+        return t;
+    }
+}
\ No newline at end of file
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/PoolConfiguration.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/PoolConfiguration.java
new file mode 100644
index 0000000..4c425f4
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/PoolConfiguration.java
@@ -0,0 +1,268 @@
+package org.apache.commons.jcs.utils.threadpool;
+
+/*
+ * 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 object holds configuration data for a thread pool.
+ * <p>
+ * @author Aaron Smuts
+ */
+public final class PoolConfiguration
+    implements Cloneable
+{
+    /** Should we bound the queue */
+    private boolean useBoundary = true;
+
+    /** If the queue is bounded, how big can it get */
+    private int boundarySize = 2000;
+
+    /** only has meaning if a boundary is used */
+    private int maximumPoolSize = 150;
+
+    /**
+     * the exact number that will be used in a boundless queue. If the queue has a boundary, more
+     * will be created if the queue fills.
+     */
+    private int minimumPoolSize = 4;
+
+    /** How long idle threads above the minimum should be kept alive. */
+    private int keepAliveTime = 1000 * 60 * 5;
+
+    public enum WhenBlockedPolicy {
+        /** abort when queue is full and max threads is reached. */
+        ABORT,
+
+        /** block when queue is full and max threads is reached. */
+        BLOCK,
+
+        /** run in current thread when queue is full and max threads is reached. */
+        RUN,
+
+        /** wait when queue is full and max threads is reached. */
+        WAIT,
+
+        /** discard oldest when queue is full and max threads is reached. */
+        DISCARDOLDEST
+    }
+
+    /** should be ABORT, BLOCK, RUN, WAIT, DISCARDOLDEST, */
+    private WhenBlockedPolicy whenBlockedPolicy = WhenBlockedPolicy.RUN;
+
+    /** The number of threads to create on startup */
+    private int startUpSize = 4;
+
+    /**
+     * @param useBoundary The useBoundary to set.
+     */
+    public void setUseBoundary( boolean useBoundary )
+    {
+        this.useBoundary = useBoundary;
+    }
+
+    /**
+     * @return Returns the useBoundary.
+     */
+    public boolean isUseBoundary()
+    {
+        return useBoundary;
+    }
+
+    /**
+     * Default
+     */
+    public PoolConfiguration()
+    {
+        // nop
+    }
+
+    /**
+     * Construct a completely configured instance.
+     * <p>
+     * @param useBoundary
+     * @param boundarySize
+     * @param maximumPoolSize
+     * @param minimumPoolSize
+     * @param keepAliveTime
+     * @param whenBlockedPolicy
+     * @param startUpSize
+     */
+    public PoolConfiguration( boolean useBoundary, int boundarySize, int maximumPoolSize, int minimumPoolSize,
+                              int keepAliveTime, WhenBlockedPolicy whenBlockedPolicy, int startUpSize )
+    {
+        setUseBoundary( useBoundary );
+        setBoundarySize( boundarySize );
+        setMaximumPoolSize( maximumPoolSize );
+        setMinimumPoolSize( minimumPoolSize );
+        setKeepAliveTime( keepAliveTime );
+        setWhenBlockedPolicy( whenBlockedPolicy );
+        setStartUpSize( startUpSize );
+    }
+
+    /**
+     * @param boundarySize The boundarySize to set.
+     */
+    public void setBoundarySize( int boundarySize )
+    {
+        this.boundarySize = boundarySize;
+    }
+
+    /**
+     * @return Returns the boundarySize.
+     */
+    public int getBoundarySize()
+    {
+        return boundarySize;
+    }
+
+    /**
+     * @param maximumPoolSize The maximumPoolSize to set.
+     */
+    public void setMaximumPoolSize( int maximumPoolSize )
+    {
+        this.maximumPoolSize = maximumPoolSize;
+    }
+
+    /**
+     * @return Returns the maximumPoolSize.
+     */
+    public int getMaximumPoolSize()
+    {
+        return maximumPoolSize;
+    }
+
+    /**
+     * @param minimumPoolSize The minimumPoolSize to set.
+     */
+    public void setMinimumPoolSize( int minimumPoolSize )
+    {
+        this.minimumPoolSize = minimumPoolSize;
+    }
+
+    /**
+     * @return Returns the minimumPoolSize.
+     */
+    public int getMinimumPoolSize()
+    {
+        return minimumPoolSize;
+    }
+
+    /**
+     * @param keepAliveTime The keepAliveTime to set.
+     */
+    public void setKeepAliveTime( int keepAliveTime )
+    {
+        this.keepAliveTime = keepAliveTime;
+    }
+
+    /**
+     * @return Returns the keepAliveTime.
+     */
+    public int getKeepAliveTime()
+    {
+        return keepAliveTime;
+    }
+
+    /**
+     * @param whenBlockedPolicy The whenBlockedPolicy to set.
+     */
+    public void setWhenBlockedPolicy( String whenBlockedPolicy )
+    {
+        if ( whenBlockedPolicy != null )
+        {
+            WhenBlockedPolicy policy = WhenBlockedPolicy.valueOf(whenBlockedPolicy.trim().toUpperCase());
+            setWhenBlockedPolicy(policy);
+        }
+        else
+        {
+            // the value is null, default to RUN
+            this.whenBlockedPolicy = WhenBlockedPolicy.RUN;
+        }
+    }
+
+    /**
+     * @param whenBlockedPolicy The whenBlockedPolicy to set.
+     */
+    public void setWhenBlockedPolicy( WhenBlockedPolicy whenBlockedPolicy )
+    {
+        if ( whenBlockedPolicy != null )
+        {
+            this.whenBlockedPolicy = whenBlockedPolicy;
+        }
+        else
+        {
+            // the value is null, default to RUN
+            this.whenBlockedPolicy = WhenBlockedPolicy.RUN;
+        }
+    }
+
+    /**
+     * @return Returns the whenBlockedPolicy.
+     */
+    public WhenBlockedPolicy getWhenBlockedPolicy()
+    {
+        return whenBlockedPolicy;
+    }
+
+    /**
+     * @param startUpSize The startUpSize to set.
+     */
+    public void setStartUpSize( int startUpSize )
+    {
+        this.startUpSize = startUpSize;
+    }
+
+    /**
+     * @return Returns the startUpSize.
+     */
+    public int getStartUpSize()
+    {
+        return startUpSize;
+    }
+
+    /**
+     * To string for debugging purposes.
+     * @return String
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append( "useBoundary = [" + isUseBoundary() + "] " );
+        buf.append( "boundarySize = [" + boundarySize + "] " );
+        buf.append( "maximumPoolSize = [" + maximumPoolSize + "] " );
+        buf.append( "minimumPoolSize = [" + minimumPoolSize + "] " );
+        buf.append( "keepAliveTime = [" + keepAliveTime + "] " );
+        buf.append( "whenBlockedPolicy = [" + getWhenBlockedPolicy() + "] " );
+        buf.append( "startUpSize = [" + startUpSize + "]" );
+        return buf.toString();
+    }
+
+    /**
+     * Copies the instance variables to another instance.
+     * <p>
+     * @return PoolConfiguration
+     */
+    @Override
+    public Object clone()
+    {
+        return new PoolConfiguration( isUseBoundary(), boundarySize, maximumPoolSize, minimumPoolSize, keepAliveTime,
+                                      getWhenBlockedPolicy(), startUpSize );
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManager.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManager.java
new file mode 100644
index 0000000..981eb1c
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManager.java
@@ -0,0 +1,445 @@
+package org.apache.commons.jcs.utils.threadpool;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.utils.props.PropertyLoader;
+import org.apache.commons.jcs.utils.threadpool.PoolConfiguration.WhenBlockedPolicy;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This manages threadpools for an application using Doug Lea's Util Concurrent package.
+ * http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
+ * <p>
+ * It is a singleton since threads need to be managed vm wide.
+ * <p>
+ * This manager forces you to use a bounded queue. By default it uses the current thread for
+ * execution when the buffer is full and no free threads can be created.
+ * <p>
+ * You can specify the props file to use or pass in a properties object prior to configuration. By
+ * default it looks for configuration information in thread_pool.properties.
+ * <p>
+ * If set, the Properties object will take precedence.
+ * <p>
+ * If a value is not set for a particular pool, the hard coded defaults will be used.
+ *
+ * <pre>
+ * int boundarySize_DEFAULT = 2000;
+ *
+ * int maximumPoolSize_DEFAULT = 150;
+ *
+ * int minimumPoolSize_DEFAULT = 4;
+ *
+ * int keepAliveTime_DEFAULT = 1000 * 60 * 5;
+ *
+ * boolean abortWhenBlocked = false;
+ *
+ * String whenBlockedPolicy_DEFAULT = IPoolConfiguration.POLICY_RUN;
+ *
+ * int startUpSize_DEFAULT = 4;
+ * </pre>
+ *
+ * You can configure default settings by specifying a default pool in the properties, ie "cache.ccf"
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ThreadPoolManager
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( ThreadPoolManager.class );
+
+    /**
+     * DEFAULT SETTINGS, these are not final since they can be set via the properties file or object
+     */
+    private static boolean useBoundary_DEFAULT = true;
+
+    /** Default queue size limit */
+    private static int boundarySize_DEFAULT = 2000;
+
+    /** Default max size */
+    private static int maximumPoolSize_DEFAULT = 150;
+
+    /** Default min */
+    private static int minimumPoolSize_DEFAULT = 4;
+
+    /** Default keep alive */
+    private static int keepAliveTime_DEFAULT = 1000 * 60 * 5;
+
+    /** Default when blocked */
+    private static WhenBlockedPolicy whenBlockedPolicy_DEFAULT = WhenBlockedPolicy.RUN;
+
+    /** Default startup size */
+    private static int startUpSize_DEFAULT = 4;
+
+    /** The default config, created using property defaults if present, else those above. */
+    private static PoolConfiguration defaultConfig;
+
+    /** This is the default value. */
+    public static final String DEFAULT_PROPS_FILE_NAME = "cache.ccf";
+
+    /** Setting this after initialization will have no effect.  */
+    private static String propsFileName = null;
+
+    /** the root property name */
+    public static String PROP_NAME_ROOT = "thread_pool";
+
+    /** default property file name */
+    private static String DEFAULT_PROP_NAME_ROOT = "thread_pool.default";
+
+    /**
+     * You can specify the properties to be used to configure the thread pool. Setting this post
+     * initialization will have no effect.
+     */
+    private static volatile Properties props = null;
+
+    /** Map of names to pools. */
+    private static HashMap<String, ThreadPoolExecutor> pools = new HashMap<String, ThreadPoolExecutor>();
+
+    /** singleton instance */
+    private static ThreadPoolManager INSTANCE = null;
+
+    /**
+     * No instances please. This is a singleton.
+     */
+    private ThreadPoolManager()
+    {
+        configure();
+    }
+
+    /**
+     * Creates a pool based on the configuration info.
+     * <p>
+     * @param config
+     * @return A ThreadPoll wrapper
+     */
+    private ThreadPoolExecutor createPool( PoolConfiguration config )
+    {
+        ThreadPoolExecutor pool = null;
+        BlockingQueue<Runnable> queue = null;
+        if ( config.isUseBoundary() )
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Creating a Bounded Buffer to use for the pool" );
+            }
+
+            queue = new LinkedBlockingQueue<Runnable>(config.getBoundarySize());
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "Creating a non bounded Linked Queue to use for the pool" );
+            }
+            queue = new LinkedBlockingQueue<Runnable>();
+        }
+
+        pool = new ThreadPoolExecutor(config.getStartUpSize(), config.getMaximumPoolSize(),
+                config.getKeepAliveTime(), TimeUnit.MILLISECONDS,
+                queue, new DaemonThreadFactory("JCS-ThreadPoolManager-"));
+
+        // when blocked policy
+        switch (config.getWhenBlockedPolicy())
+        {
+            case ABORT:
+                pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
+                break;
+
+            case RUN:
+                pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+                break;
+
+            case WAIT:
+                throw new RuntimeException("POLICY_WAIT no longer supported");
+
+            case DISCARDOLDEST:
+                pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
+                break;
+
+            default:
+                break;
+        }
+
+        pool.prestartAllCoreThreads();
+
+        return pool;
+    }
+
+    /**
+     * Returns a configured instance of the ThreadPoolManger To specify a configuration file or
+     * Properties object to use call the appropriate setter prior to calling getInstance.
+     * <p>
+     * @return The single instance of the ThreadPoolManager
+     */
+    public static synchronized ThreadPoolManager getInstance()
+    {
+        if ( INSTANCE == null )
+        {
+            INSTANCE = new ThreadPoolManager();
+        }
+        return INSTANCE;
+    }
+
+    /**
+     * Returns a pool by name. If a pool by this name does not exist in the configuration file or
+     * properties, one will be created using the default values.
+     * <p>
+     * Pools are lazily created.
+     * <p>
+     * @param name
+     * @return The thread pool configured for the name.
+     */
+    public ThreadPoolExecutor getPool( String name )
+    {
+        ThreadPoolExecutor pool = null;
+
+        synchronized ( pools )
+        {
+            pool = pools.get( name );
+            if ( pool == null )
+            {
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "Creating pool for name [" + name + "]" );
+                }
+                PoolConfiguration config = this.loadConfig( PROP_NAME_ROOT + "." + name );
+                pool = createPool( config );
+
+                if ( pool != null )
+                {
+                    pools.put( name, pool );
+                }
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "PoolName = " + getPoolNames() );
+                }
+            }
+        }
+
+        return pool;
+    }
+
+    /**
+     * Returns the names of all configured pools.
+     * <p>
+     * @return ArrayList of string names
+     */
+    public ArrayList<String> getPoolNames()
+    {
+        ArrayList<String> poolNames = new ArrayList<String>();
+        synchronized ( pools )
+        {
+            poolNames.addAll(pools.keySet());
+        }
+        return poolNames;
+    }
+
+    /**
+     * Setting this post initialization will have no effect.
+     * <p>
+     * @param propsFileName The propsFileName to set.
+     */
+    public static void setPropsFileName( String propsFileName )
+    {
+        ThreadPoolManager.propsFileName = propsFileName;
+    }
+
+    /**
+     * Returns the name of the properties file that we used to initialize the pools. If the value
+     * was set post-initialization, then it may not be the file used.
+     * <p>
+     * @return Returns the propsFileName.
+     */
+    public static String getPropsFileName()
+    {
+        return propsFileName;
+    }
+
+    /**
+     * This will be used if it is not null on initialization. Setting this post initialization will
+     * have no effect.
+     * <p>
+     * @param props The props to set.
+     */
+    public static void setProps( Properties props )
+    {
+        ThreadPoolManager.props = props;
+    }
+
+    /**
+     * @return Returns the props.
+     */
+    public static Properties getProps()
+    {
+        return props;
+    }
+
+    /**
+     * Initialize the ThreadPoolManager and create all the pools defined in the configuration.
+     */
+    protected void configure()
+    {
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "Initializing ThreadPoolManager" );
+        }
+
+        if ( props == null )
+        {
+            try
+            {
+                props = PropertyLoader.loadProperties( propsFileName );
+
+                if ( log.isDebugEnabled() )
+                {
+                    log.debug( "File contained " + props.size() + " properties" );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( "Problem loading properties. propsFileName [" + propsFileName + "]", e );
+            }
+        }
+
+        if ( props == null )
+        {
+            log.warn( "No configuration settings found.  Using hardcoded default values for all pools." );
+            props = new Properties();
+        }
+
+        // set intial default and then override if new
+        // settings are available
+        defaultConfig = new PoolConfiguration( useBoundary_DEFAULT, boundarySize_DEFAULT, maximumPoolSize_DEFAULT,
+                                               minimumPoolSize_DEFAULT, keepAliveTime_DEFAULT,
+                                               whenBlockedPolicy_DEFAULT, startUpSize_DEFAULT );
+
+        defaultConfig = loadConfig( DEFAULT_PROP_NAME_ROOT );
+    }
+
+    /**
+     * Configures the default PoolConfiguration settings.
+     * <p>
+     * @param root
+     * @return PoolConfiguration
+     */
+    protected PoolConfiguration loadConfig( String root )
+    {
+        PoolConfiguration config = (PoolConfiguration) defaultConfig.clone();
+
+        if ( props.containsKey( root + ".useBoundary" ) )
+        {
+            try
+            {
+                config.setUseBoundary( Boolean.valueOf( (String) props.get( root + ".useBoundary" ) ).booleanValue() );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "useBoundary not a boolean.", nfe );
+            }
+        }
+
+        // load default if they exist
+        if ( props.containsKey( root + ".boundarySize" ) )
+        {
+            try
+            {
+                config.setBoundarySize( Integer.parseInt( (String) props.get( root + ".boundarySize" ) ) );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "boundarySize not a number.", nfe );
+            }
+        }
+
+        // maximum pool size
+        if ( props.containsKey( root + ".maximumPoolSize" ) )
+        {
+            try
+            {
+                config.setMaximumPoolSize( Integer.parseInt( (String) props.get( root + ".maximumPoolSize" ) ) );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "maximumPoolSize not a number.", nfe );
+            }
+        }
+
+        // minimum pool size
+        if ( props.containsKey( root + ".minimumPoolSize" ) )
+        {
+            try
+            {
+                config.setMinimumPoolSize( Integer.parseInt( (String) props.get( root + ".minimumPoolSize" ) ) );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "minimumPoolSize not a number.", nfe );
+            }
+        }
+
+        // keep alive
+        if ( props.containsKey( root + ".keepAliveTime" ) )
+        {
+            try
+            {
+                config.setKeepAliveTime( Integer.parseInt( (String) props.get( root + ".keepAliveTime" ) ) );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "keepAliveTime not a number.", nfe );
+            }
+        }
+
+        // when blocked
+        if ( props.containsKey( root + ".whenBlockedPolicy" ) )
+        {
+            config.setWhenBlockedPolicy( (String) props.get( root + ".whenBlockedPolicy" ) );
+        }
+
+        // startupsize
+        if ( props.containsKey( root + ".startUpSize" ) )
+        {
+            try
+            {
+                config.setStartUpSize( Integer.parseInt( (String) props.get( root + ".startUpSize" ) ) );
+            }
+            catch ( NumberFormatException nfe )
+            {
+                log.error( "startUpSize not a number.", nfe );
+            }
+        }
+
+        if ( log.isInfoEnabled() )
+        {
+            log.info( root + " PoolConfiguration = " + config );
+        }
+
+        return config;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/ElapsedTimer.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/ElapsedTimer.java
new file mode 100644
index 0000000..bd728e9
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/ElapsedTimer.java
@@ -0,0 +1,58 @@
+package org.apache.commons.jcs.utils.timing;
+
+/*
+ * 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 is a simple timer utility.
+ */
+public class ElapsedTimer
+{
+    /** display suffix describing the unit of measure. */
+    private static final String SUFFIX = "ms.";
+
+    /**
+     * Sets the start time when created.
+     */
+    private long timeStamp = System.currentTimeMillis();
+
+    /**
+     * Gets the time elapsed between the start time and now. The start time is reset to now.
+     * Subsequent calls will get the time between then and now.
+     * <p>
+     * @return the elapsed time
+     */
+    public long getElapsedTime()
+    {
+        long now = System.currentTimeMillis();
+        long elapsed = now - timeStamp;
+        timeStamp = now;
+        return elapsed;
+    }
+
+    /**
+     * Returns the elapsed time with the display suffix.
+     * <p>
+     * @return formatted elapsed Time
+     */
+    public String getElapsedTimeString()
+    {
+        return String.valueOf( getElapsedTime() ) + SUFFIX;
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/SleepUtil.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/SleepUtil.java
new file mode 100644
index 0000000..e1c09cd
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/timing/SleepUtil.java
@@ -0,0 +1,50 @@
+package org.apache.commons.jcs.utils.timing;
+
+/*
+ * 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.
+ */
+
+/**
+ * Utility methods to help deal with thread issues.
+ */
+public class SleepUtil
+{
+    /**
+     * Sleep for a specified duration in milliseconds. This method is a
+     * platform-specific workaround for Windows due to its inability to resolve
+     * durations of time less than approximately 10 - 16 ms.
+     * <p>
+     * @param milliseconds the number of milliseconds to sleep
+     */
+    public static void sleepAtLeast( long milliseconds )
+    {
+        long endTime = System.currentTimeMillis() + milliseconds;
+
+        while ( System.currentTimeMillis() <= endTime )
+        {
+            try
+            {
+                Thread.sleep( milliseconds );
+            }
+            catch ( InterruptedException e )
+            {
+                // TODO - Do something here?
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/zip/CompressionUtil.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/zip/CompressionUtil.java
new file mode 100644
index 0000000..93f05fa
--- /dev/null
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/utils/zip/CompressionUtil.java
@@ -0,0 +1,203 @@
+package org.apache.commons.jcs.utils.zip;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+
+/** Compress / Decompress. */
+public final class CompressionUtil
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( CompressionUtil.class );
+
+    /**
+     * no instances.
+     */
+    private CompressionUtil()
+    {
+        // NO OP
+    }
+
+    /**
+     * Decompress the byte array passed using a default buffer length of 1024.
+     * <p>
+     * @param input compressed byte array webservice response
+     * @return uncompressed byte array
+     */
+    public static byte[] decompressByteArray( final byte[] input )
+    {
+        return decompressByteArray( input, 1024 );
+    }
+
+    /**
+     * Decompress the byte array passed
+     * <p>
+     * @param input compressed byte array webservice response
+     * @param bufferLength buffer length
+     * @return uncompressed byte array
+     */
+    public static byte[] decompressByteArray( final byte[] input, final int bufferLength )
+    {
+        if ( null == input )
+        {
+            throw new IllegalArgumentException( "Input was null" );
+        }
+
+        // Create the decompressor and give it the data to compress
+        final Inflater decompressor = new Inflater();
+
+        decompressor.setInput( input );
+
+        // Create an expandable byte array to hold the decompressed data
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream( input.length );
+
+        // Decompress the data
+        final byte[] buf = new byte[bufferLength];
+
+        try
+        {
+            while ( !decompressor.finished() )
+            {
+                int count = decompressor.inflate( buf );
+                baos.write( buf, 0, count );
+            }
+        }
+        catch ( DataFormatException ex )
+        {
+            log.error( "Problem decompressing.", ex );
+        }
+
+        decompressor.end();
+
+        try
+        {
+            baos.close();
+        }
+        catch ( IOException ex )
+        {
+            log.error( "Problem closing stream.", ex );
+        }
+
+        return baos.toByteArray();
+    }
+
+    /**
+     * Compress the byte array passed
+     * <p>
+     * @param input byte array
+     * @return compressed byte array
+     * @throws IOException thrown if we can't close the output stream
+     */
+    public static byte[] compressByteArray( byte[] input )
+        throws IOException
+    {
+        return compressByteArray( input, 1024 );
+    }
+
+    /**
+     * Compress the byte array passed
+     * <p>
+     * @param input byte array
+     * @param bufferLength buffer length
+     * @return compressed byte array
+     * @throws IOException thrown if we can't close the output stream
+     */
+    public static byte[] compressByteArray( byte[] input, int bufferLength )
+        throws IOException
+    {
+        // Compressor with highest level of compression
+        Deflater compressor = new Deflater();
+        compressor.setLevel( Deflater.BEST_COMPRESSION );
+
+        // Give the compressor the data to compress
+        compressor.setInput( input );
+        compressor.finish();
+
+        // Create an expandable byte array to hold the compressed data.
+        // It is not necessary that the compressed data will be smaller than
+        // the uncompressed data.
+        ByteArrayOutputStream bos = new ByteArrayOutputStream( input.length );
+
+        // Compress the data
+        byte[] buf = new byte[bufferLength];
+        while ( !compressor.finished() )
+        {
+            int count = compressor.deflate( buf );
+            bos.write( buf, 0, count );
+        }
+
+        // JCS-136 ( Details here : http://www.devguli.com/blog/eng/java-deflater-and-outofmemoryerror/ )
+        compressor.end();
+        bos.close();
+
+        // Get the compressed data
+        return bos.toByteArray();
+
+    }
+
+    /**
+     * decompress a gzip byte array, using a default buffer length of 1024
+     * <p>
+     * @param compressedByteArray gzip-compressed byte array
+     * @return decompressed byte array
+     * @throws IOException thrown if there was a failure to construct the GzipInputStream
+     */
+    public static byte[] decompressGzipByteArray( byte[] compressedByteArray )
+        throws IOException
+    {
+        return decompressGzipByteArray( compressedByteArray, 1024 );
+    }
+
+    /**
+     * decompress a gzip byte array, using a default buffer length of 1024
+     * <p>
+     * @param compressedByteArray gzip-compressed byte array
+     * @param bufferlength size of the buffer in bytes
+     * @return decompressed byte array
+     * @throws IOException thrown if there was a failure to construct the GzipInputStream
+     */
+    public static byte[] decompressGzipByteArray( byte[] compressedByteArray, int bufferlength )
+        throws IOException
+    {
+        ByteArrayOutputStream uncompressedStream = new ByteArrayOutputStream();
+
+        GZIPInputStream compressedStream = new GZIPInputStream( new ByteArrayInputStream( compressedByteArray ) );
+
+        byte[] buffer = new byte[bufferlength];
+
+        int index = -1;
+
+        while ( ( index = compressedStream.read( buffer ) ) != -1 )
+        {
+            uncompressedStream.write( buffer, 0, index );
+        }
+
+        return uncompressedStream.toByteArray();
+    }
+}
diff --git a/commons-jcs-core/src/test/conf/JCSAdminServlet.velocity.properties b/commons-jcs-core/src/test/conf/JCSAdminServlet.velocity.properties
new file mode 100644
index 0000000..2681e2b
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/JCSAdminServlet.velocity.properties
@@ -0,0 +1,21 @@
+# 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.
+runtime.log.logsystem.class = org.apache.velocity.runtime.log.AvalonLogSystem
+runtime.log.logsystem.log4j.category = org.apache.plexus.velocity.DefaultVelocityComponentTest
+
+resource.loader = classpath
+classpath.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
diff --git a/commons-jcs-core/src/test/conf/LocalStrings.properties b/commons-jcs-core/src/test/conf/LocalStrings.properties
new file mode 100644
index 0000000..78f1d5c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/LocalStrings.properties
@@ -0,0 +1,57 @@
+# 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.
+# Default localized resources for example servlets
+# This locale is en_US
+
+helloworld.title=Hello World!
+
+requestinfo.title=Request Information Example
+requestinfo.label.method=Method:
+requestinfo.label.requesturi=Request URI:
+requestinfo.label.protocol=Protocol:
+requestinfo.label.pathinfo=Path Info:
+requestinfo.label.remoteaddr=Remote Address:
+
+requestheader.title=Request Header Example
+
+requestparams.title=Request Parameters Example
+requestparams.params-in-req=Parameters in this request:
+requestparams.no-params=No Parameters, Please enter some
+requestparams.firstname=First Name:
+requestparams.lastname=Last Name:
+
+cookies.title=Cookies Example
+cookies.cookies=Your browser is sending the following cookies:
+cookies.no-cookies=Your browser isn't sending any cookies
+cookies.make-cookie=Create a cookie to send to your browser
+cookies.name=Name:
+cookies.value=Value:
+cookies.set=You just sent the following cookie to your browser:
+
+sessions.title=Sessions Example
+sessions.id=Session ID:
+sessions.created=Created:
+sessions.lastaccessed=Last Accessed:
+sessions.data=The following data is in your session:
+sessions.adddata=Add data to your session
+sessions.dataname=Name of Session Attribute:
+sessions.datavalue=Value of Session Attribute:
+sessions.requestedid=Requested Session ID:
+sessions.requestedidvalid=Requested Session ID is valid:
+sessions.fromcookie=Requested Session ID is from a cookie:
+sessions.fromurl=Requested Session ID is from a URL:
+sessions.isnew=Session is new:
diff --git a/commons-jcs-core/src/test/conf/cache.ccf b/commons-jcs-core/src/test/conf/cache.ccf
new file mode 100644
index 0000000..bbe3853
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cache.ccf
@@ -0,0 +1,69 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cache.policy b/commons-jcs-core/src/test/conf/cache.policy
new file mode 100644
index 0000000..0af5390
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cache.policy
@@ -0,0 +1,41 @@
+// 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.
+
+grant {
+    permission java.security.AllPermission;
+};
+
+
+grant codeBase "file:g:/dev/jakarta-turbine-jcs/target/classes/*" {
+    permission java.security.AllPermission;
+};
+
+grant codeBase "file:g:/dev/jakarta-turbine-jcs/src/conf/*" {
+    permission java.security.AllPermission;
+};
+
+grant codeBase "file:g:/dev/jakarta-turbine-jcs/src/conf/scripts/*" {
+    permission java.security.AllPermission;
+};
+
+grant codeBase "file:g:/dev/jakarta-turbine-jcs/*" {
+    permission java.security.AllPermission;
+};
+
+grant codeBase "file:/G:/*" {
+    permission java.security.AllPermission;
+};
diff --git a/commons-jcs-core/src/test/conf/cache2.ccf b/commons-jcs-core/src/test/conf/cache2.ccf
new file mode 100644
index 0000000..83ed80c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cache2.ccf
@@ -0,0 +1,153 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# should be defined for the storage of group attribute list
+jcs.system.groupIdCache=DC
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=1000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=10
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=1000
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# prefered config
+jcs.region.test2=DC
+jcs.region.test2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.test2.cacheattributes.MaxObjects=1000
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+jcs.auxiliary.HC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HC.attributes.DiskPath=@project_home@/hsql
+
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1202
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LJG.attributes.UdpMulticastPort=6789
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1112
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8181
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8182
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1111,localhost2:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cache3.ccf b/commons-jcs-core/src/test/conf/cache3.ccf
new file mode 100644
index 0000000..54e4962
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cache3.ccf
@@ -0,0 +1,145 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# should be defined for the storage of group attribute list
+jcs.system.groupIdCache=DC
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=1000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.elementattributes.IsLateral=true
+
+jcs.region.testCache2=DC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=1000
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# prefered config
+jcs.region.test2=DC
+jcs.region.test2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.test2.cacheattributes.MaxObjects=1000
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+jcs.auxiliary.HC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HC.attributes.DiskPath=@project_home@/hsql
+
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.RemoteHost=10.21.209.150
+jcs.auxiliary.RC.attributes.RemotePort=1102
+# jcs.auxiliary.RC.attributes.LocalPort=1103
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LJG.attributes.UdpMulticastPort=6789
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1112
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=false
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8181
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8182
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1111,localhost2:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cacheB.ccf b/commons-jcs-core/src/test/conf/cacheB.ccf
new file mode 100644
index 0000000..69c46b9
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheB.ccf
@@ -0,0 +1,241 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=1000
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=40
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLifeSeconds=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Remote RMI cache without failover
+jcs.auxiliary.RGroup=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RGroup.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RGroup.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RGroup.attributes.RemoteHost=localhost
+jcs.auxiliary.RGroup.attributes.RemotePort=1102
+jcs.auxiliary.RGroup.attributes.GetOnly=true
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=localhost:1102
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads does not help.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled.
+# SINGLE is best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# Berkeley DB JE
+jcs.auxiliary.JE=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.JE.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.JE.attributes.DiskPath=target/test-sandbox/bdbje-disk-cache-conc
+# the minimum cache size is 1024
+jcs.auxiliary.indexedDiskCache.attributes.CacheSize=1024
+# jcs.auxiliary.indexedDiskCache.attributes.CachePercent=0
+
+# HSQL Disk Cache -- too slow as is
+jcs.auxiliary.HDC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HDC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HDC.attributes.DiskPath=@project_home_f at hsql
+
+# JISP Disk Cache -- save memory with disk key storage
+jcs.auxiliary.JDC=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheFactory
+jcs.auxiliary.JDC.attributes=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheAttributes
+jcs.auxiliary.JDC.attributes.DiskPath=@project_home_f at raf
+jcs.auxiliary.JDC.attributes.ClearOnStart=false
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102,localhost:1103
+jcs.auxiliary.RC.attributes.LocalPort=1204
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=751):PING(timeout=3000):FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.javagroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=false
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP2.attributes.PutOnlyMode=true
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8182
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8181
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheBDB.ccf b/commons-jcs-core/src/test/conf/cacheBDB.ccf
new file mode 100644
index 0000000..90178aa
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheBDB.ccf
@@ -0,0 +1,55 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=bdbje
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=bdbje
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion2=bdbje
+jcs.region.indexedRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion2.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion3=bdbje
+jcs.region.indexedRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion3.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion4=bdbje
+jcs.region.indexedRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion4.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.indexedRegion4.cacheattributes.UseMemoryShrinker=false
+
+
+# #### AUXILIARY CACHES
+
+# Berkeley DB JE
+jcs.auxiliary.bdbje=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.bdbje.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.bdbje.attributes.DiskPath=target/
+jcs.auxiliary.bdbje.attributes.MaxPurgatorySize=100000
diff --git a/commons-jcs-core/src/test/conf/cacheD10A.ccf b/commons-jcs-core/src/test/conf/cacheD10A.ccf
new file mode 100644
index 0000000..c7ac3d3
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheD10A.ccf
@@ -0,0 +1,167 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC,RC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=1000
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=40
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLifeSeconds=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads does not help.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled.
+# SINGLE is best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=dev10.hq.site59.com:1101
+jcs.auxiliary.RC.attributes.LocalPort=1200
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+# jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+# jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+# jcs.auxiliary.RC.attributes.GetOnly=false
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.FailoverServers=dev10.hq.site59.com:1101
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=true
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheD10B.ccf b/commons-jcs-core/src/test/conf/cacheD10B.ccf
new file mode 100644
index 0000000..993ea38
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheD10B.ccf
@@ -0,0 +1,167 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC,RC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=1000
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=40
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLifeSeconds=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads does not help.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled.
+# SINGLE is best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=dev10.hq.site59.com:1101
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+# jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+# jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+# jcs.auxiliary.RC.attributes.GetOnly=false
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.FailoverServers=dev10.hq.site59.com:1101
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=true
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheID.ccf b/commons-jcs-core/src/test/conf/cacheID.ccf
new file mode 100644
index 0000000..27744e0
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheID.ccf
@@ -0,0 +1,65 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=0
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
diff --git a/commons-jcs-core/src/test/conf/cacheJG1.ccf b/commons-jcs-core/src/test/conf/cacheJG1.ccf
new file mode 100644
index 0000000..a0f7538
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheJG1.ccf
@@ -0,0 +1,100 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=LJG
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LJG
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafJG1
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# Lateral JavaGroups Distribution
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:QUEUE
+# jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE
+# jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=228.10.9.8;mcast_port=5678):PING:FD:FLUSH:GMS
+# jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=225.0.0.100;mcast_port=7777):PING(timeout=3000):AUTOCONF:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:STATE_TRANSFER:QUEUE
+jcs.auxiliary.LJG.attributes.EventQueueType=POOLED
+jcs.auxiliary.LJG.attributes.EventQueuePolName=lg_event_queue
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.jgroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.lg_event_queue.boundarySize=5000
+thread_pool.lg_event_queue.maximumPoolSize=75
+thread_pool.lg_event_queue.minimumPoolSize=10
+thread_pool.lg_event_queue.keepAliveTime=350000
+thread_pool.lg_event_queue.whenBlockedPolicy=DISCARDOLDEST
+thread_pool.lg_event_queue.startUpSize=4
diff --git a/commons-jcs-core/src/test/conf/cacheJG2.ccf b/commons-jcs-core/src/test/conf/cacheJG2.ccf
new file mode 100644
index 0000000..4e350cb
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheJG2.ccf
@@ -0,0 +1,81 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=LJG
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LJG
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafJG2
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# Lateral JavaGroups Distribution
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:QUEUE
+# jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=32):PING(timeout=3000;num_initial_members=6):FD(timeout=5000):VERIFY_SUSPECT(timeout=1500):pbcast.STABLE(desired_avg_gossip=10000):pbcast.NAKACK(gc_lag=10;retransmit_timeout=3000):UNICAST(timeout=5000;min_wait_time=2000):FRAG:pbcast.GMS(initial_mbrs_timeout=4000;join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=false)
+# UDP(mcast_addr=228.10.9.8;mcast_port=5678):PING:FD:FLUSH:GMS
+# UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:QUEUE
+# UDP(mcast_addr=225.0.0.100;mcast_port=7777):PING(timeout=3000):AUTOCONF:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:STATE_TRANSFER:QUEUE
+# UDP(mcast_addr=225.0.0.100;mcast_port=7777):PING(timeout=3000):pbcast.FD:pbcast.NAKACK:pbcast.STABLE:UNICAST:pbcast.GMS:FRAG
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.jgroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
diff --git a/commons-jcs-core/src/test/conf/cacheJG3.ccf b/commons-jcs-core/src/test/conf/cacheJG3.ccf
new file mode 100644
index 0000000..ba4fc1b
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheJG3.ccf
@@ -0,0 +1,91 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,LJG
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,LJG
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafJG1
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# Lateral JavaGroups Distribution
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=224.10.10.10;mcast_port=5555;ip_ttl=32):PING(timeout=3000;num_initial_members=6):FD(timeout=3000):VERIFY_SUSPECT(timeout=1500):pbcast.NAKACK(gc_lag=10;retransmit_timeout=600,1200,2400,4800):UNICAST(timeout=600,1200,2400,4800):pbcast.STABLE(desired_avg_gossip=10000):FRAG:pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false;print_local_addr=true)
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.lg_event_queue.boundarySize=5000
+thread_pool.lg_event_queue.maximumPoolSize=75
+thread_pool.lg_event_queue.minimumPoolSize=10
+thread_pool.lg_event_queue.keepAliveTime=350000
+thread_pool.lg_event_queue.whenBlockedPolicy=DISCARDOLDEST
+thread_pool.lg_event_queue.startUpSize=4
diff --git a/commons-jcs-core/src/test/conf/cacheLMD1.ccf b/commons-jcs-core/src/test/conf/cacheLMD1.ccf
new file mode 100644
index 0000000..4198030
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheLMD1.ccf
@@ -0,0 +1,112 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,LJG
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+# LEAD_PRICE_CACHE_NAME
+jcs.region.LeadPrice=DC,LJG
+jcs.region.LeadPrice.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.LeadPrice.cacheattributes.MaxObjects=1000
+# set it to zero, was 1000
+jcs.region.LeadPrice.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.LeadPrice.cacheattributes.UseMemoryShrinker=true
+jcs.region.LeadPrice.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.LeadPrice.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.LeadPrice.cacheattributes.MaxSpoolPerRun=100
+jcs.region.LeadPrice.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.LeadPrice.elementattributes.IsEternal=false
+jcs.region.LeadPrice.elementattributes.MaxLifeSeconds=600
+jcs.region.LeadPrice.elementattributes.IsSpool=true
+jcs.region.LeadPrice.elementattributes.IsLateral=true
+jcs.region.LeadPrice.elementattributes.IsRemote=true
+
+# HOTEL_OPTION_CACHE_NAME
+jcs.region.HotelOption=DC,LJG
+jcs.region.HotelOption.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.HotelOption.cacheattributes.MaxObjects=1000
+# set it to zero, was 1000
+jcs.region.HotelOption.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.HotelOption.cacheattributes.UseMemoryShrinker=true
+jcs.region.HotelOption.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.HotelOption.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.HotelOption.cacheattributes.MaxSpoolPerRun=100
+jcs.region.HotelOption.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.HotelOption.elementattributes.IsEternal=false
+jcs.region.HotelOption.elementattributes.MaxLifeSeconds=600
+jcs.region.HotelOption.elementattributes.IsSpool=true
+jcs.region.HotelOption.elementattributes.IsLateral=true
+jcs.region.HotelOption.elementattributes.IsRemote=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=log/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+
+# Lateral JavaGroups Distribution
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=224.0.0.100;mcast_port=751):PING(timeout=3000):FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+jcs.auxiliary.LJG.attributes.EventQueueType=POOLED
+jcs.auxiliary.LJG.attributes.EventQueuePolName=lg_event_queue
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.lg_event_queue.boundarySize=5000
+thread_pool.lg_event_queue.maximumPoolSize=75
+thread_pool.lg_event_queue.minimumPoolSize=10
+thread_pool.lg_event_queue.keepAliveTime=350000
+thread_pool.lg_event_queue.whenBlockedPolicy=DISCARDOLDEST
+thread_pool.lg_event_queue.startUpSize=4
diff --git a/commons-jcs-core/src/test/conf/cacheNA.ccf b/commons-jcs-core/src/test/conf/cacheNA.ccf
new file mode 100644
index 0000000..84a829c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheNA.ccf
@@ -0,0 +1,99 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafNA
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# REMOTE SERVER RS1
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+# First server is primary, the rest will be tried in order if the primary fails
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=25
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheNA2.ccf b/commons-jcs-core/src/test/conf/cacheNA2.ccf
new file mode 100644
index 0000000..cd587a6
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheNA2.ccf
@@ -0,0 +1,99 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafNA
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# REMOTE SERVER RS1
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+# First server is primary, the rest will be tried in order if the primary fails
+jcs.auxiliary.RC.attributes.FailoverServers=server:1101
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=25
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheNA3.ccf b/commons-jcs-core/src/test/conf/cacheNA3.ccf
new file mode 100644
index 0000000..e3f10e7
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheNA3.ccf
@@ -0,0 +1,125 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafNA
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# REMOTE SERVER RS1
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+# First server is primary, the rest will be tried in order if the primary fails
+jcs.auxiliary.RC.attributes.FailoverServers=server:1101
+jcs.auxiliary.RC.attributes.LocalPort=1200
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.attributes.EventQueueType=POOLED
+jcs.auxiliary.RC.attributes.EventQueuePolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=25
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+thread_pool.remote_cache_server.boundarySize=100000
+thread_pool.remote_cache_server.maximumPoolSize=150
+thread_pool.remote_cache_server.minimumPoolSize=15
+thread_pool.remote_cache_server.keepAliveTime=350000
+thread_pool.remote_cache_server.whenBlockedPolicy=RUN
+thread_pool.remote_cache_server.startUpSize=4
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=5000
+thread_pool.remote_cache_client.maximumPoolSize=75
+thread_pool.remote_cache_client.minimumPoolSize=10
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=DISCARDOLDEST
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/conf/cacheNB.ccf b/commons-jcs-core/src/test/conf/cacheNB.ccf
new file mode 100644
index 0000000..80e2a21
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheNB.ccf
@@ -0,0 +1,100 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafNB
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# REMOTE SERVER -- RS2
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+# First server is primary, the rest will be tried in order if the primary fails
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1102,localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1202
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=25
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheRC.ccf b/commons-jcs-core/src/test/conf/cacheRC.ccf
new file mode 100644
index 0000000..9ef2135
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRC.ccf
@@ -0,0 +1,246 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC,RC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=1000
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=40
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLifeSeconds=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Remote RMI cache without failover
+jcs.auxiliary.RGroup=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RGroup.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RGroup.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RGroup.attributes.RemoteHost=localhost
+jcs.auxiliary.RGroup.attributes.RemotePort=1102
+jcs.auxiliary.RGroup.attributes.GetOnly=true
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=localhost:1102
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads does not help.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled.
+# SINGLE is best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# Berkeley DB JE
+jcs.auxiliary.JE=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.JE.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.JE.attributes.DiskPath=target/test-sandbox/bdbje-disk-cache-conc
+# the minimum cache size is 1024
+jcs.auxiliary.indexedDiskCache.attributes.CacheSize=1024
+# jcs.auxiliary.indexedDiskCache.attributes.CachePercent=0
+
+# HSQL Disk Cache -- too slow as is
+jcs.auxiliary.HDC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HDC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HDC.attributes.DiskPath=@project_home_f at hsql
+
+# JISP Disk Cache -- save memory with disk key storage
+jcs.auxiliary.JDC=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheFactory
+jcs.auxiliary.JDC.attributes=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheAttributes
+jcs.auxiliary.JDC.attributes.DiskPath=@project_home_f at raf
+jcs.auxiliary.JDC.attributes.ClearOnStart=false
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=751):PING(timeout=3000):FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.javagroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP2.attributes.PutOnlyMode=true
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8182
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8181
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheRC1.ccf b/commons-jcs-core/src/test/conf/cacheRC1.ccf
new file mode 100644
index 0000000..09890e3
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRC1.ccf
@@ -0,0 +1,79 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=${user.dir}/raf1
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cacheRC2.ccf b/commons-jcs-core/src/test/conf/cacheRC2.ccf
new file mode 100644
index 0000000..935236c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRC2.ccf
@@ -0,0 +1,79 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=${user.dir}/raf2
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1102,localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1202
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cacheRCN1.ccf b/commons-jcs-core/src/test/conf/cacheRCN1.ccf
new file mode 100644
index 0000000..7406467
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRCN1.ccf
@@ -0,0 +1,79 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=${user.dir}/raf1
+
+# This remote client does not receive
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.attributes.Receive=false
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cacheRCN2.ccf b/commons-jcs-core/src/test/conf/cacheRCN2.ccf
new file mode 100644
index 0000000..9ff900c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRCN2.ccf
@@ -0,0 +1,79 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# standard disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=${user.dir}/raf2
+
+# This remote client does not receive
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1102,localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1202
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.attributes.Receive=false
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/conf/cacheRCSimple.ccf b/commons-jcs-core/src/test/conf/cacheRCSimple.ccf
new file mode 100644
index 0000000..a155345
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRCSimple.ccf
@@ -0,0 +1,218 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Remote RMI cache without failover
+jcs.auxiliary.RGroup=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RGroup.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RGroup.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RGroup.attributes.RemoteHost=localhost
+jcs.auxiliary.RGroup.attributes.RemotePort=1102
+jcs.auxiliary.RGroup.attributes.GetOnly=true
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=localhost:1102
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads does not help.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled.
+# SINGLE is best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# Berkeley DB JE
+jcs.auxiliary.JE=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.JE.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.JE.attributes.DiskPath=target/test-sandbox/bdbje-disk-cache-conc
+# the minimum cache size is 1024
+jcs.auxiliary.indexedDiskCache.attributes.CacheSize=1024
+# jcs.auxiliary.indexedDiskCache.attributes.CachePercent=0
+
+# HSQL Disk Cache -- too slow as is
+jcs.auxiliary.HDC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HDC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HDC.attributes.DiskPath=@project_home_f at hsql
+
+# JISP Disk Cache -- save memory with disk key storage
+jcs.auxiliary.JDC=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheFactory
+jcs.auxiliary.JDC.attributes=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheAttributes
+jcs.auxiliary.JDC.attributes.DiskPath=@project_home_f at raf
+jcs.auxiliary.JDC.attributes.ClearOnStart=false
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101
+# ,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=751):PING(timeout=3000):FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.javagroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP2.attributes.PutOnlyMode=true
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8182
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8181
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheRC_CEL.ccf b/commons-jcs-core/src/test/conf/cacheRC_CEL.ccf
new file mode 100644
index 0000000..9c06932
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRC_CEL.ccf
@@ -0,0 +1,145 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=DC,RC
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=1000
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=40
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLifeSeconds=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.cacheeventlogger=org.apache.commons.jcs.engine.logging.CacheEventLoggerDebugLogger
+jcs.auxiliary.RC.cacheeventlogger.attributes.logCategoryName=test.RCCEventLogCategory
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+# since it doesn't use a boundary, some of the options are unnecessary
+thread_pool.cache_event_queue.useBoundary=false
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+thread_pool.cache_event_queue.startUpSize=5
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
+
diff --git a/commons-jcs-core/src/test/conf/cacheRHTTP.ccf b/commons-jcs-core/src/test/conf/cacheRHTTP.ccf
new file mode 100644
index 0000000..9c2cff4
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheRHTTP.ccf
@@ -0,0 +1,40 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+## The Http Remote Cache Client
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.http.client.RemoteHttpCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.http.client.RemoteHttpCacheAttributes
+jcs.auxiliary.RC.attributes.url=http://localhost:8000/jcs-app/RemoteCache
+jcs.auxiliary.RC.attributes.maxConnectionsPerHost=500
diff --git a/commons-jcs-core/src/test/conf/cacheTCP1.ccf b/commons-jcs-core/src/test/conf/cacheTCP1.ccf
new file mode 100644
index 0000000..3644f6c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheTCP1.ccf
@@ -0,0 +1,66 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LTCP
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# TCP
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1118
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=9780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+jcs.auxiliary.LTCP.attributes.Receive=true
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=false
diff --git a/commons-jcs-core/src/test/conf/cacheTCP2.ccf b/commons-jcs-core/src/test/conf/cacheTCP2.ccf
new file mode 100644
index 0000000..c40c44e
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheTCP2.ccf
@@ -0,0 +1,80 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LTCP
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=LTCP
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=1000
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache2.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache2.elementattributes.IsLateral=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# TCP
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1119
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=9780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+jcs.auxiliary.LTCP.attributes.Receive=true
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=false
+
diff --git a/commons-jcs-core/src/test/conf/cacheTCP3.ccf b/commons-jcs-core/src/test/conf/cacheTCP3.ccf
new file mode 100644
index 0000000..3b727eb
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheTCP3.ccf
@@ -0,0 +1,81 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LTCP
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=LTCP
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=1000
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache2.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache2.elementattributes.IsLateral=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+# jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1120
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=9780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+# jcs.auxiliary.LTCP.attributes.Receive=true
+
diff --git a/commons-jcs-core/src/test/conf/cacheTCP4.ccf b/commons-jcs-core/src/test/conf/cacheTCP4.ccf
new file mode 100644
index 0000000..c748c23
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/cacheTCP4.ccf
@@ -0,0 +1,68 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LTCP
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+# jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1121
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=9780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+# jcs.auxiliary.LTCP.attributes.Receive=true
+
diff --git a/commons-jcs-core/src/test/conf/jcsutils.properties b/commons-jcs-core/src/test/conf/jcsutils.properties
new file mode 100644
index 0000000..38b469b
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/jcsutils.properties
@@ -0,0 +1,18 @@
+# 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.
+admin.userid=admin
+admin.password=system
diff --git a/commons-jcs-core/src/test/conf/je.properties b/commons-jcs-core/src/test/conf/je.properties
new file mode 100644
index 0000000..5e9aa1f
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/je.properties
@@ -0,0 +1,51 @@
+# 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.
+# Property file for unit test usage. Usually, all
+# unit tests should run w/out a je.properties file, so
+# the test can have total control over its environment.
+# It may be useful to use a property file when debugging.
+# This file should always be checked in with all properties
+# commented out.
+# $Id: je.properties 1591173 2014-04-30 01:45:52Z olamy $
+
+# je.env.forcedYield=false
+
+# java.util.logging.ConsoleHandler.on=true
+# java.util.logging.FileHandler.on=true
+# java.util.logging.level=INFO
+
+# je.env.runINCompressor=true
+# je.compressor.deadlockRetry=3
+# je.compressor.lockTimeout=5000
+
+# je.env.runEvictor=true
+# je.maxMemory defaults to 93% of jdb.maxMemory unless specified
+je.maxMemory=512
+# je.evictor.nodeScanPercentage=25
+# je.evictor.evictionBatchPercentage=25
+
+# je.env.runCheckpointer=true
+# je.checkpointer.deadlockRetry=3
+
+# je.verify.tree.dump=true
+# je.verify.inlist=true
+# je.verify.throw=false
+
+# je.env.runCleaner=true
+# je.cleaner.deadlockRetry=3
+# je.cleaner.lockTimeout=5000
+# je.cleaner.expunge=false
diff --git a/commons-jcs-core/src/test/conf/log4j.properties b/commons-jcs-core/src/test/conf/log4j.properties
new file mode 100644
index 0000000..90ea9c8
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/log4j.properties
@@ -0,0 +1,69 @@
+# 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.
+log4j.rootCategory=INFO, A1
+
+# A1 -- console
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+# Print the date in ISO 8601 format
+log4j.appender.A1.layout.ConversionPattern=%d %-5p [%c{1}] [%t] [%x] %m%n
+
+# RF --file appender
+log4j.appender.RF=org.apache.log4j.RollingFileAppender
+log4j.appender.RF.File=server.log
+log4j.appender.RF.layout=org.apache.log4j.PatternLayout
+log4j.appender.RF.layout.ConversionPattern=%d %-5p [%c{1}] [%t] [%x] %m%n
+
+# WF -- special file for warnings
+# log4j.appender.WF=org.apache.log4j.RollingFileAppender
+# log4j.appender.WF.File=I:/dev/jakarta-turbine-jcs/src/scripts/warnings.log
+# log4j.appender.WF.layout=org.apache.log4j.PatternLayout
+# log4j.appender.WF.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
+
+
+# ################################################################################
+
+log4j.category.test= DEBUG
+
+log4j.category.org.jgroups=WARN
+
+log4j.category.org.apache.commons.jcs=INFO
+log4j.category.org.apache.commons.jcs.engine=INFO
+# log4j.category.org.apache.commons.jcs.acess=WARN,WF
+# log4j.category.org.apache.commons.jcs.engine.control=WARN,WF
+
+# log4j.logger.org.apache.commons.jcs.engine.memory.shrinking=INFO
+# log4j.logger.org.apache.commons.jcs.auxiliary.disk=INFO
+log4j.logger.org.apache.commons.jcs.auxiliary.disk.indexed=INFO
+
+# log4j.category.org.apache.commons.jcs.config=WARN,A1
+
+# log4j.category.org.apache.commons.jcs.auxiliary=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.disk=WARN,WF
+# log4j.category.org.apache.commons.jcs.auxiliary.lateral=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.lateral.javagroups=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.lateral.xmlrpc=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.remote=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.remote.RemoteCacheFailoverRunner=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.remote.RemoteCacheListener=DEBUG
+# log4j.category.org.apache.commons.jcs.auxiliary.remote.RemoteCacheManager=INFO
+# log4j.category.org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServer=DEBUG
+# log4j.category.org.apache.commons.jcs.auxiliary.remote.server=INFO
+# log4j.category.org.apache.commons.jcs.utils=WARN,WF
+# log4j.category.org.apache.commons.jcs.utils.discovery=DEBUG
+
+log4j.category.org.apache.commons.jcs.utils=INFO
diff --git a/commons-jcs-core/src/test/conf/logger.properties b/commons-jcs-core/src/test/conf/logger.properties
new file mode 100644
index 0000000..8629cb9
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/logger.properties
@@ -0,0 +1,75 @@
+# 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.
+# The LoggerManager creates loggers for entries in this file.
+# This initializes certain logs at the set debugging levels( 0 - 4 )
+# A management tool will force reinitialization and a reread of this file at
+# runtime.  However the primary way to alter runtime loggin levels will be to
+# modify the level of the logger object through the tool.
+# An entry must have a .level entry to be initialized
+# .systemout is N by default, Y will turn it on
+# .maxfilesize -- number of bytes before archiving log
+# .numtocheck -- number of entries before checking to see if it is too big
+# The logroot value is used by default.  This can be overridden with
+# a specific entry
+
+logroot=@project_home@/logs
+
+# the sleepinterval value is how often the writing thread wakes up in ms.
+# Recommend set to 1000 for development servers (so it would write with 1 sec. delay
+# and 10000 for production servers (so it would write every 10 seconds.)
+
+# Min is 5 secs ie 5000
+sleepInterval=1000
+
+# The string buffer size before messages are flushed to disk.
+# Minimum is zero, which flushes every log message to disk asap.
+buffer_capacity=0
+
+access_cacheaccess.level=2
+access_cacheaccess.systemout=y
+access_cacheaccess.maxfilesize=100000
+access_cacheaccess.numtocheck=300
+
+control_cache.level=2
+control_cache.systemout=y
+control_cache.maxfilesize=100000
+control_cache.numtocheck=300
+
+engine_groupcache.level=2
+engine_groupcache.systemout=y
+engine_groupcache.maxfilesize=100000
+engine_groupcache.numtocheck=300
+
+control_cachemanager.level=2
+control_cachemanager.systemout=y
+control_cachemanager.maxfilesize=100000
+control_cachemanager.numtocheck=300
+
+memory_lateralcacheunicaster.level=0
+memory_lateralcacheunicaster.systemout=n
+memory_lateralcacheunicaster.maxfilesize=100000
+memory_lateralcacheunicaster.numtocheck=300
+
+remote_remotecachemanager.level=2
+remote_remotecachemanager.systemout=y
+remote_remotecachemanager.maxfilesize=100000
+remote_remotecachemanager.numtocheck=300
+
+group_remotegroupcacheserver.level=2
+group_remotegroupcacheserver.systemout=y
+group_remotegroupcacheserver.maxfilesize=100000
+group_remotegroupcacheserver.numtocheck=300
diff --git a/commons-jcs-core/src/test/conf/myjetty.xml b/commons-jcs-core/src/test/conf/myjetty.xml
new file mode 100644
index 0000000..0fc6a26
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/myjetty.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0"  encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure 1.0//EN"
+ "http://jetty.mortbay.com/configure_1_0.dtd">
+
+<!--
+This is a Jetty HTTP server configuration file.  This configuration
+uses the generic com.mortbay.Util.XmlConfiguration class to call
+the normal com.mortbay.HTTP.HttpServer configuration API from
+within an XML script.
+
+The format of this file is described in the configure.dtd file.
+
+The API that can be called by this file is described in the
+Javadoc for Jetty.
+
+The following concepts must be understood when configuring
+a server:
+
+Listener: is a network interface object that
+accepts HTTP requests for the server. SocketListeners accept
+normal requests, while JsseListeners accept SSL requests.
+The threading model of the server is controlled by the
+listener parameters.
+
+WebApplication: is a bundled collection of resources,
+servlets and configuration that can provide a unified
+WWW application.  It is part of the 2.2 servlet standard.
+The contents of the application are configured by the
+web.xml deployment descriptor within the application.
+The configuration of the application within Jetty requires
+on the context of the application to be set.
+
+Context: is a grouping of server resources that share
+the same URL path prefix, class path and resource base.
+A Web application is an example of a specific context.
+Generic contexts may have arbitrary request handlers
+added to them.  All contexts have a path specification
+(frequently the default "/") and an option virtual
+host alias.
+
+Handler:  Handlers are the objects that actually
+service the HTTP requests. Examples of Handlers include
+ServletHandler, ResourceHandler and NotFoundHandler.
+Handlers are contained within Contexts, which provide
+conveniance methods for the common handlers so
+that servlet and file serving may be configured for
+a context without explicit creation of a Handler.
+
+
+This file configures:
+ + A listener at port 8080 on all known interfaces
+ + The default web applicaton at /default/* context
+ + Dynamic servlet context at /servlet/*
+ + A context at / with serving files from ./docroot and
+   the dump servlet at /dump.
+
+-->
+
+<Configure class="com.mortbay.HTTP.HttpServer">
+  <Call name="addListener">
+    <Arg>
+      <New class="com.mortbay.HTTP.SocketListener">
+        <Set name="Port">9090</Set>
+        <Set name="MinThreads">5</Set>
+        <Set name="MaxThreads">255</Set>
+        <Set name="MaxIdleTimeMs">60000</Set>
+        <Set name="MaxReadTimeMs">60000</Set>
+      </New>
+    </Arg>
+  </Call>
+
+  <Call name="addWebApplication">
+    <Arg>/jcs/*</Arg>
+    <Arg><SystemProperty name="jetty.home" default="../../"/>webapps/jcs/</Arg>
+    <Arg></Arg>
+  </Call>
+
+  <Call name="addContext">
+    <Arg>/</Arg>
+    <Set name="ClassPath"><SystemProperty name="jetty.home" default="."/>/servlets/</Set>
+    <Set name="DynamicServletPathSpec">/servlet/*</Set>
+    <Set name="ResourceBase"><SystemProperty name="jetty.home" default="."/>/docroot/</Set>
+    <Set name="ServingResources">TRUE</Set>
+    <Call name="addServlet">
+      <Arg>Dump</Arg>
+      <Arg>/dump/*,/handler/Dump,/handler/Dump/*</Arg>
+      <Arg>com.mortbay.Servlet.Dump</Arg>
+    </Call>
+    <Call name="addServlet">
+      <Arg>JSP</Arg>
+      <Arg>*.jsp,*.jsP,*.jSp,*.jSP,*.Jsp,*.JsP,*.JSp,*.JSP</Arg>
+      <Arg>org.apache.jasper.servlet.JspServlet</Arg>
+    </Call>
+  </Call>
+
+  <Set name="LogSink">
+    <New class="com.mortbay.Util.WriterLogSink">
+      <Arg><SystemProperty name="jetty.log" default="../../logs"/>/yyyy_mm_dd.request.log</Arg>
+      <Set name="RetainDays">90</Set>
+      <Set name="Append">true</Set>
+    </New>
+  </Set>
+
+</Configure>
+
diff --git a/commons-jcs-core/src/test/conf/myjetty2.xml b/commons-jcs-core/src/test/conf/myjetty2.xml
new file mode 100644
index 0000000..72fb780
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/myjetty2.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0"  encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure 1.0//EN"
+ "http://jetty.mortbay.com/configure_1_0.dtd">
+
+<!--
+This is a Jetty HTTP server configuration file.  This configuration
+uses the generic com.mortbay.Util.XmlConfiguration class to call
+the normal com.mortbay.HTTP.HttpServer configuration API from
+within an XML script.
+
+The format of this file is described in the configure.dtd file.
+
+The API that can be called by this file is described in the
+Javadoc for Jetty.
+
+The following concepts must be understood when configuring
+a server:
+
+Listener: is a network interface object that
+accepts HTTP requests for the server. SocketListeners accept
+normal requests, while JsseListeners accept SSL requests.
+The threading model of the server is controlled by the
+listener parameters.
+
+WebApplication: is a bundled collection of resources,
+servlets and configuration that can provide a unified
+WWW application.  It is part of the 2.2 servlet standard.
+The contents of the application are configured by the
+web.xml deployment descriptor within the application.
+The configuration of the application within Jetty requires
+on the context of the application to be set.
+
+Context: is a grouping of server resources that share
+the same URL path prefix, class path and resource base.
+A Web application is an example of a specific context.
+Generic contexts may have arbitrary request handlers
+added to them.  All contexts have a path specification
+(frequently the default "/") and an option virtual
+host alias.
+
+Handler:  Handlers are the objects that actually
+service the HTTP requests. Examples of Handlers include
+ServletHandler, ResourceHandler and NotFoundHandler.
+Handlers are contained within Contexts, which provide
+conveniance methods for the common handlers so
+that servlet and file serving may be configured for
+a context without explicit creation of a Handler.
+
+
+This file configures:
+ + A listener at port 8080 on all known interfaces
+ + The default web applicaton at /default/* context
+ + Dynamic servlet context at /servlet/*
+ + A context at / with serving files from ./docroot and
+   the dump servlet at /dump.
+
+-->
+
+<Configure class="com.mortbay.HTTP.HttpServer">
+  <Call name="addListener">
+    <Arg>
+      <New class="com.mortbay.HTTP.SocketListener">
+        <Set name="Port">9091</Set>
+        <Set name="MinThreads">5</Set>
+        <Set name="MaxThreads">255</Set>
+        <Set name="MaxIdleTimeMs">60000</Set>
+        <Set name="MaxReadTimeMs">60000</Set>
+      </New>
+    </Arg>
+  </Call>
+
+  <Call name="addWebApplication">
+    <Arg>/jcs/*</Arg>
+    <Arg><SystemProperty name="jetty.home" default="../../"/>webapps/jcs/</Arg>
+    <Arg></Arg>
+  </Call>
+
+  <Call name="addContext">
+    <Arg>/</Arg>
+    <Set name="ClassPath"><SystemProperty name="jetty.home" default="."/>/servlets/</Set>
+    <Set name="DynamicServletPathSpec">/servlet/*</Set>
+    <Set name="ResourceBase"><SystemProperty name="jetty.home" default="."/>/docroot/</Set>
+    <Set name="ServingResources">TRUE</Set>
+    <Call name="addServlet">
+      <Arg>Dump</Arg>
+      <Arg>/dump/*,/handler/Dump,/handler/Dump/*</Arg>
+      <Arg>com.mortbay.Servlet.Dump</Arg>
+    </Call>
+    <Call name="addServlet">
+      <Arg>JSP</Arg>
+      <Arg>*.jsp,*.jsP,*.jSp,*.jSP,*.Jsp,*.JsP,*.JSp,*.JSP</Arg>
+      <Arg>org.apache.jasper.servlet.JspServlet</Arg>
+    </Call>
+  </Call>
+
+  <Set name="LogSink">
+    <New class="com.mortbay.Util.WriterLogSink">
+      <Arg><SystemProperty name="jetty.log" default="../../logs"/>/yyyy_mm_dd.request.log</Arg>
+      <Set name="RetainDays">90</Set>
+      <Set name="Append">true</Set>
+    </New>
+  </Set>
+
+</Configure>
+
diff --git a/commons-jcs-core/src/test/conf/remote.cache.ccf b/commons-jcs-core/src/test/conf/remote.cache.ccf
new file mode 100644
index 0000000..40e6261
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cache.ccf
@@ -0,0 +1,68 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1101
+remote.cache.service.port=1101
+remote.cluster.LocalClusterConsistency=true
+#remote.cacheeventlogger=org.apache.commons.jcs.engine.logging.CacheEventLoggerDebugLogger
+#remote.cacheeventlogger.attributes.logCategoryName=TestEventLogCategory
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200002
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# server to update for clustering -- remote.cache2.ccf(1102)  and remote.cache3.ccf(1103)
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1102
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf/remote
+
diff --git a/commons-jcs-core/src/test/conf/remote.cache2.ccf b/commons-jcs-core/src/test/conf/remote.cache2.ccf
new file mode 100644
index 0000000..803cdd6
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cache2.ccf
@@ -0,0 +1,70 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1102
+# call back port to local caches.
+remote.cache.service.port=1102
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200000
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# server to update for clustering -- remote.cache1.ccf(1101)  and remote.cache3.ccf(1103)
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1101
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+# disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf/remote-rc2
+
+
diff --git a/commons-jcs-core/src/test/conf/remote.cache3.ccf b/commons-jcs-core/src/test/conf/remote.cache3.ccf
new file mode 100644
index 0000000..116a208
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cache3.ccf
@@ -0,0 +1,71 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1103
+# call back port to local caches.
+remote.cache.service.port=1103
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# server to update for clustering -- remote.cache1.ccf(1101) and remote.cache2.ccf(1102)
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1101,localhost:1102
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+
+# disk cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf/remote
+
diff --git a/commons-jcs-core/src/test/conf/remote.cacheCEL.ccf b/commons-jcs-core/src/test/conf/remote.cacheCEL.ccf
new file mode 100644
index 0000000..a2bf52a
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cacheCEL.ccf
@@ -0,0 +1,68 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1101
+remote.cache.service.port=1101
+remote.cluster.LocalClusterConsistency=true
+remote.cacheeventlogger=org.apache.commons.jcs.engine.logging.CacheEventLoggerDebugLogger
+remote.cacheeventlogger.attributes.logCategoryName=test.RCSEventLogCategory
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200002
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# server to update for clustering -- remote.cache2.ccf(1102)  and remote.cache3.ccf(1103)
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1102
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf/remote
+
diff --git a/commons-jcs-core/src/test/conf/remote.cacheCEL_CSF.ccf b/commons-jcs-core/src/test/conf/remote.cacheCEL_CSF.ccf
new file mode 100644
index 0000000..892a435
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cacheCEL_CSF.ccf
@@ -0,0 +1,69 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1101
+jcs.remotecache.serverattributes.servicePort=1301
+jcs.remotecache.serverattributes.localClusterConsistency=true
+jcs.remotecache.customrmisocketfactory=org.apache.commons.jcs.auxiliary.remote.server.MockRMISocketFactory
+jcs.remotecache.cacheeventlogger=org.apache.commons.jcs.engine.logging.CacheEventLoggerDebugLogger
+jcs.remotecache.cacheeventlogger.attributes.logCategoryName=test.RCSEventLogCategory
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=200002
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# server to update for clustering -- remote.cache2.ccf(1102)  and remote.cache3.ccf(1103)
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1102
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=@project_home@/raf/remote
+
diff --git a/commons-jcs-core/src/test/conf/remote.cacheRS1.ccf b/commons-jcs-core/src/test/conf/remote.cacheRS1.ccf
new file mode 100644
index 0000000..d540726
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cacheRS1.ccf
@@ -0,0 +1,85 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  RS1 #################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1101
+# call back port to local caches.
+remote.cache.service.port=1101
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafRS1
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# RS2 SERVER to update for clustering
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1102
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
diff --git a/commons-jcs-core/src/test/conf/remote.cacheRS2.ccf b/commons-jcs-core/src/test/conf/remote.cacheRS2.ccf
new file mode 100644
index 0000000..7d2e99c
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.cacheRS2.ccf
@@ -0,0 +1,86 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  RS2 #################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1102
+# call back port to local caches.
+remote.cache.service.port=1102
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=250000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RCluster
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=250000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+jcs.region.testCache1.elementattributes.IsSpool=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=logs/rafRS2
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=100000
+jcs.auxiliary.DC.attributes.MaxKeySize=500000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=-1
+
+# RS1 SERVER to update for clustering
+jcs.auxiliary.RCluster=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster.attributes.ClusterServers=localhost:1101
+jcs.auxiliary.RCluster.attributes.GetOnly=false
+jcs.auxiliary.RCluster.attributes.LocalClusterConsistency=true
+
+
diff --git a/commons-jcs-core/src/test/conf/remote.tomcat.xml b/commons-jcs-core/src/test/conf/remote.tomcat.xml
new file mode 100644
index 0000000..3530ea0
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/remote.tomcat.xml
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+
+<Server>
+    <!-- Debug low-level events in XmlMapper startup
+    <xmlmapper:debug level="0" />
+    -->
+
+    <!--
+
+    Logging:
+
+         Logging in Tomcat is quite flexible; we can either have a log
+         file per module (example: ContextManager) or we can have one
+         for Servlets and one for Jasper, or we can just have one
+         tomcat.log for both Servlet and Jasper.  Right now there are
+         three standard log streams, "tc_log", "servlet_log", and
+         "JASPER_LOG".
+
+	 Path:
+
+	 The file to which to output this log, relative to
+	 TOMCAT_HOME.  If you omit a "path" value, then stderr or
+	 stdout will be used.
+
+	 Verbosity:
+
+	 Threshold for which types of messages are displayed in the
+	 log.  Levels are inclusive; that is, "WARNING" level displays
+	 any log message marked as warning, error, or fatal.  Default
+	 level is WARNING.
+
+	 verbosityLevel values can be:
+	    FATAL
+	    ERROR
+	    WARNING
+            INFORMATION
+            DEBUG
+
+	 Timestamps:
+
+	 By default, logs print a timestamp in the form "yyyy-MM-dd
+	 hh:mm:ss" in front of each message.  To disable timestamps
+	 completely, set 'timestamp="no"'. To use the raw
+	 msec-since-epoch, which is more efficient, set
+	 'timestampFormat="msec"'.  If you want a custom format, you
+	 can use 'timestampFormat="hh:mm:ss"' following the syntax of
+	 java.text.SimpleDateFormat (see Javadoc API).  For a
+	 production environment, we recommend turning timestamps off,
+	 or setting the format to "msec".
+
+	 Custom Output:
+
+	 "Custom" means "normal looking".  "Non-custom" means
+	 "surrounded with funny xml tags".  In preparation for
+	 possibly disposing of "custom" altogether, now the default is
+	 'custom="yes"' (i.e. no tags)
+
+	 Per-component Debugging:
+
+	 Some components accept a "debug" attribute.  This further
+	 enhances log output.  If you set the "debug" level for a
+	 component, it may output extra debugging information.
+    -->
+
+    <!-- if you don't want messages on screen, add the attribute
+            path="logs/tomcat.log"
+	 to the Logger element below
+    -->
+    <Logger name="tc_log"
+            verbosityLevel = "INFORMATION"
+    />
+
+    <Logger name="servlet_log"
+            path="logs/servlet.log"
+    />
+
+    <Logger name="JASPER_LOG"
+	    path="logs/jasper.log"
+            verbosityLevel = "INFORMATION" />
+
+    <!-- You can add a "home" attribute to represent the "base" for
+         all relative paths. If none is set, the TOMCAT_HOME property
+         will be used, and if not set "." will be used.
+         webapps/, work/ and logs/ will be relative to this ( unless
+         set explicitely to absolute paths ).
+
+         You can also specify a "randomClass" attribute, which determines
+         a subclass of java.util.Random will be used for generating session IDs.
+         By default this is "java.security.SecureRandom".
+         Specifying "java.util.Random" will speed up Tomcat startup,
+         but it will cause sessions to be less secure.
+
+         You can specify the "showDebugInfo" attribute to control whether
+         debugging information is displayed in Tomcat's default responses.
+         This debugging information includes:
+             1. Stack traces for exceptions
+             2. Request URI's that cause status codes >= 400
+         The default is "true", so you must specify "false" to prevent
+         the debug information from appearing.  Since the debugging
+         information reveals internal details about what Tomcat is serving,
+         set showDebugInfo="false" if you wish increased security.
+      -->
+    <ContextManager debug="0" workDir="work" showDebugInfo="true" >
+
+      <!-- ==================== Interceptors ==================== -->
+
+        <!--
+         ContextInterceptor className="org.apache.tomcat.context.LogEvents"
+        -->
+
+        <ContextInterceptor className="org.apache.tomcat.context.AutoSetup" />
+
+        <ContextInterceptor
+            className="org.apache.tomcat.context.WebXmlReader" />
+
+        <!-- Uncomment out if you have JDK1.2 and want to use policy
+        <ContextInterceptor
+            className="org.apache.tomcat.context.PolicyInterceptor" />
+        -->
+
+        <ContextInterceptor
+            className="org.apache.tomcat.context.LoaderInterceptor" />
+        <ContextInterceptor
+            className="org.apache.tomcat.context.DefaultCMSetter" />
+        <ContextInterceptor
+            className="org.apache.tomcat.context.WorkDirInterceptor" />
+
+	<!--  Uncomment if you are using JDK1.2 or higher.
+              Insures proper thread context class loader is in effect for servlet execution
+	<ContextInterceptor
+            className="org.apache.tomcat.request.Jdk12Interceptor" />
+	-->
+
+	<!-- Request processing -->
+        <!-- Session interceptor will extract the session id from cookies and
+             deal with URL rewriting ( by fixing the URL ).  If you wish to
+             suppress the use of cookies for session identifiers, change the
+             "noCookies" attribute to "true"
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SessionInterceptor"
+            noCookies="false" />
+
+        <!-- Find the container ( context and prefix/extension map )
+             for a request.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SimpleMapper1"
+            debug="0" />
+
+        <!-- Non-standard invoker, for backward compat. ( /servlet/* )
+             You can modify the prefix that is matched by adjusting the
+             "prefix" parameter below.  Be sure your modified pattern
+             starts and ends with a slash.
+
+             NOTE:  This prefix applies to *all* web applications that
+             are running in this instance of Tomcat.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.InvokerInterceptor"
+            debug="0" prefix="/servlet/" />
+
+        <!-- "default" handler - static files and dirs.  Set the
+             "suppress" property to "true" to suppress directory listings
+             when no welcome file is present.
+
+             NOTE:  This setting applies to *all* web applications that
+             are running in this instance of Tomcat.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.StaticInterceptor"
+            debug="0" suppress="false" />
+
+        <!-- Plug a session manager. You can plug in more advanced session
+             modules.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.session.StandardSessionInterceptor" />
+
+        <!-- Check if the request requires an authenticated role.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.AccessInterceptor"
+            debug="0" />
+
+        <!-- Check permissions using the simple xml file. You can
+             plug more advanced authentication modules.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SimpleRealm"
+            debug="0" />
+
+       <!-- UnComment the following and comment out the
+            above to get a JDBC realm.
+            Other options for driverName:
+              driverName="oracle.jdbc.driver.OracleDriver"
+              connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+              connectionName="scott"
+              connectionPassword="tiger"
+
+              driverName="com.mysql.jdbc.Driver"
+              connectionURL="jdbc:mysql://localhost/authority"
+              connectionName="test"
+              connectionPassword="test"
+
+            "connectionName" and "connectionPassword" are optional.
+        -->
+        <!--
+        <RequestInterceptor
+            className="org.apache.tomcat.request.JDBCRealm"
+            debug="99"
+	    driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+	    connectionURL="jdbc:odbc:TOMCAT"
+	    userTable="users"
+            userNameCol="user_name"
+            userCredCol="user_pass"
+	    userRoleTable="user_roles"
+            roleNameCol="role_name" />
+        -->
+
+        <!-- Loaded last since JSP's that load-on-startup use request handling -->
+        <ContextInterceptor
+            className="org.apache.tomcat.context.LoadOnStartupInterceptor" />
+
+      <!-- ==================== Connectors ==================== -->
+
+        <!-- Normal HTTP -->
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+                value="org.apache.tomcat.service.http.HttpConnectionHandler"/>
+            <Parameter name="port"
+                value="9090"/>
+        </Connector>
+
+        <!--
+            Uncomment this for SSL support.
+            You _need_ to set up a server certificate if you want this
+            to work, and you need JSSE.
+            1. Add JSSE jars to CLASSPATH
+            2. Edit java.home/jre/lib/security/java.security
+               Add:
+               security.provider.2=com.sun.net.ssl.internal.ssl.Provider
+            3. Do: keytool -genkey -alias tomcat -keyalg RSA
+               RSA is essential to work with Netscape and IIS.
+               Use "changeit" as password. ( or add keypass attribute )
+               You don't need to sign the certificate.
+
+            You can set parameter keystore and keypass if you want
+            to change the default ( user.home/.keystore with changeit )
+        -->
+        <!--
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+                value="org.apache.tomcat.service.http.HttpConnectionHandler"/>
+            <Parameter name="port"
+                value="8443"/>
+            <Parameter name="socketFactory"
+                value="org.apache.tomcat.net.SSLSocketFactory" />
+        </Connector>
+        -->
+
+        <!-- Apache AJP12 support. This is also used to shut down tomcat.
+          -->
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+       value="org.apache.tomcat.service.connector.Ajp12ConnectionHandler"/>
+            <Parameter name="port" value="8007"/>
+        </Connector>
+
+        <!-- ==================== Special webapps ==================== -->
+        <!-- You don't need this if you place your app in webapps/
+             and use defaults.
+             For security you'll also need to edit tomcat.policy
+
+             Defaults are: debug=0, reloadable=true, trusted=false
+             (trusted allows you to access tomcat internal objects
+             with FacadeManager ), crossContext=true (allows you to
+             access other contexts via ServletContext.getContext())
+
+             If security manager is enabled, you'll have read perms.
+             in the webapps dir and read/write in the workdir.
+         -->
+
+        <!--
+        <Context path="/examples"
+                 docBase="webapps/examples"
+                 crossContext="false"
+                 debug="0"
+                 reloadable="true" >
+        </Context>
+        -->
+
+        <Context path="../../"
+                 docBase="webapps/jcs"
+                 crossContext="false"
+                 debug="0"
+                 reloadable="true" >
+        </Context>
+
+
+        <!-- Admin context will use tomcat.core to add/remove/get info about
+             the webapplications and tomcat internals.
+             By default it is not trusted - i.e. it is not allowed access to
+             tomcat internals, only informations that are available to all
+             servlets are visible.
+
+             If you change this to true, make sure you set a password.
+          -->
+      <!--
+        <Context path="/admin"
+                 docBase="webapps/admin"
+                 crossContext="true"
+                 debug="0"
+                 reloadable="true"
+                 trusted="false" >
+        </Context>
+      -->
+
+        <!-- Virtual host example -
+             In "127.0.0.1" virtual host we'll reverse "/" and
+             "/examples"
+             (XXX need a better example )
+             (use  "http://127.0.0.1/examples" )
+        <Host name="127.0.0.1" >
+           <Context path=""
+                    docBase="webapps/examples" />
+           <Context path="/examples"
+                    docBase="webapps/ROOT" />
+        </Host>
+         -->
+
+    </ContextManager>
+</Server>
diff --git a/commons-jcs-core/src/test/conf/tomcat.xml b/commons-jcs-core/src/test/conf/tomcat.xml
new file mode 100644
index 0000000..3530ea0
--- /dev/null
+++ b/commons-jcs-core/src/test/conf/tomcat.xml
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+
+<Server>
+    <!-- Debug low-level events in XmlMapper startup
+    <xmlmapper:debug level="0" />
+    -->
+
+    <!--
+
+    Logging:
+
+         Logging in Tomcat is quite flexible; we can either have a log
+         file per module (example: ContextManager) or we can have one
+         for Servlets and one for Jasper, or we can just have one
+         tomcat.log for both Servlet and Jasper.  Right now there are
+         three standard log streams, "tc_log", "servlet_log", and
+         "JASPER_LOG".
+
+	 Path:
+
+	 The file to which to output this log, relative to
+	 TOMCAT_HOME.  If you omit a "path" value, then stderr or
+	 stdout will be used.
+
+	 Verbosity:
+
+	 Threshold for which types of messages are displayed in the
+	 log.  Levels are inclusive; that is, "WARNING" level displays
+	 any log message marked as warning, error, or fatal.  Default
+	 level is WARNING.
+
+	 verbosityLevel values can be:
+	    FATAL
+	    ERROR
+	    WARNING
+            INFORMATION
+            DEBUG
+
+	 Timestamps:
+
+	 By default, logs print a timestamp in the form "yyyy-MM-dd
+	 hh:mm:ss" in front of each message.  To disable timestamps
+	 completely, set 'timestamp="no"'. To use the raw
+	 msec-since-epoch, which is more efficient, set
+	 'timestampFormat="msec"'.  If you want a custom format, you
+	 can use 'timestampFormat="hh:mm:ss"' following the syntax of
+	 java.text.SimpleDateFormat (see Javadoc API).  For a
+	 production environment, we recommend turning timestamps off,
+	 or setting the format to "msec".
+
+	 Custom Output:
+
+	 "Custom" means "normal looking".  "Non-custom" means
+	 "surrounded with funny xml tags".  In preparation for
+	 possibly disposing of "custom" altogether, now the default is
+	 'custom="yes"' (i.e. no tags)
+
+	 Per-component Debugging:
+
+	 Some components accept a "debug" attribute.  This further
+	 enhances log output.  If you set the "debug" level for a
+	 component, it may output extra debugging information.
+    -->
+
+    <!-- if you don't want messages on screen, add the attribute
+            path="logs/tomcat.log"
+	 to the Logger element below
+    -->
+    <Logger name="tc_log"
+            verbosityLevel = "INFORMATION"
+    />
+
+    <Logger name="servlet_log"
+            path="logs/servlet.log"
+    />
+
+    <Logger name="JASPER_LOG"
+	    path="logs/jasper.log"
+            verbosityLevel = "INFORMATION" />
+
+    <!-- You can add a "home" attribute to represent the "base" for
+         all relative paths. If none is set, the TOMCAT_HOME property
+         will be used, and if not set "." will be used.
+         webapps/, work/ and logs/ will be relative to this ( unless
+         set explicitely to absolute paths ).
+
+         You can also specify a "randomClass" attribute, which determines
+         a subclass of java.util.Random will be used for generating session IDs.
+         By default this is "java.security.SecureRandom".
+         Specifying "java.util.Random" will speed up Tomcat startup,
+         but it will cause sessions to be less secure.
+
+         You can specify the "showDebugInfo" attribute to control whether
+         debugging information is displayed in Tomcat's default responses.
+         This debugging information includes:
+             1. Stack traces for exceptions
+             2. Request URI's that cause status codes >= 400
+         The default is "true", so you must specify "false" to prevent
+         the debug information from appearing.  Since the debugging
+         information reveals internal details about what Tomcat is serving,
+         set showDebugInfo="false" if you wish increased security.
+      -->
+    <ContextManager debug="0" workDir="work" showDebugInfo="true" >
+
+      <!-- ==================== Interceptors ==================== -->
+
+        <!--
+         ContextInterceptor className="org.apache.tomcat.context.LogEvents"
+        -->
+
+        <ContextInterceptor className="org.apache.tomcat.context.AutoSetup" />
+
+        <ContextInterceptor
+            className="org.apache.tomcat.context.WebXmlReader" />
+
+        <!-- Uncomment out if you have JDK1.2 and want to use policy
+        <ContextInterceptor
+            className="org.apache.tomcat.context.PolicyInterceptor" />
+        -->
+
+        <ContextInterceptor
+            className="org.apache.tomcat.context.LoaderInterceptor" />
+        <ContextInterceptor
+            className="org.apache.tomcat.context.DefaultCMSetter" />
+        <ContextInterceptor
+            className="org.apache.tomcat.context.WorkDirInterceptor" />
+
+	<!--  Uncomment if you are using JDK1.2 or higher.
+              Insures proper thread context class loader is in effect for servlet execution
+	<ContextInterceptor
+            className="org.apache.tomcat.request.Jdk12Interceptor" />
+	-->
+
+	<!-- Request processing -->
+        <!-- Session interceptor will extract the session id from cookies and
+             deal with URL rewriting ( by fixing the URL ).  If you wish to
+             suppress the use of cookies for session identifiers, change the
+             "noCookies" attribute to "true"
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SessionInterceptor"
+            noCookies="false" />
+
+        <!-- Find the container ( context and prefix/extension map )
+             for a request.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SimpleMapper1"
+            debug="0" />
+
+        <!-- Non-standard invoker, for backward compat. ( /servlet/* )
+             You can modify the prefix that is matched by adjusting the
+             "prefix" parameter below.  Be sure your modified pattern
+             starts and ends with a slash.
+
+             NOTE:  This prefix applies to *all* web applications that
+             are running in this instance of Tomcat.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.InvokerInterceptor"
+            debug="0" prefix="/servlet/" />
+
+        <!-- "default" handler - static files and dirs.  Set the
+             "suppress" property to "true" to suppress directory listings
+             when no welcome file is present.
+
+             NOTE:  This setting applies to *all* web applications that
+             are running in this instance of Tomcat.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.StaticInterceptor"
+            debug="0" suppress="false" />
+
+        <!-- Plug a session manager. You can plug in more advanced session
+             modules.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.session.StandardSessionInterceptor" />
+
+        <!-- Check if the request requires an authenticated role.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.AccessInterceptor"
+            debug="0" />
+
+        <!-- Check permissions using the simple xml file. You can
+             plug more advanced authentication modules.
+          -->
+        <RequestInterceptor
+            className="org.apache.tomcat.request.SimpleRealm"
+            debug="0" />
+
+       <!-- UnComment the following and comment out the
+            above to get a JDBC realm.
+            Other options for driverName:
+              driverName="oracle.jdbc.driver.OracleDriver"
+              connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+              connectionName="scott"
+              connectionPassword="tiger"
+
+              driverName="com.mysql.jdbc.Driver"
+              connectionURL="jdbc:mysql://localhost/authority"
+              connectionName="test"
+              connectionPassword="test"
+
+            "connectionName" and "connectionPassword" are optional.
+        -->
+        <!--
+        <RequestInterceptor
+            className="org.apache.tomcat.request.JDBCRealm"
+            debug="99"
+	    driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+	    connectionURL="jdbc:odbc:TOMCAT"
+	    userTable="users"
+            userNameCol="user_name"
+            userCredCol="user_pass"
+	    userRoleTable="user_roles"
+            roleNameCol="role_name" />
+        -->
+
+        <!-- Loaded last since JSP's that load-on-startup use request handling -->
+        <ContextInterceptor
+            className="org.apache.tomcat.context.LoadOnStartupInterceptor" />
+
+      <!-- ==================== Connectors ==================== -->
+
+        <!-- Normal HTTP -->
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+                value="org.apache.tomcat.service.http.HttpConnectionHandler"/>
+            <Parameter name="port"
+                value="9090"/>
+        </Connector>
+
+        <!--
+            Uncomment this for SSL support.
+            You _need_ to set up a server certificate if you want this
+            to work, and you need JSSE.
+            1. Add JSSE jars to CLASSPATH
+            2. Edit java.home/jre/lib/security/java.security
+               Add:
+               security.provider.2=com.sun.net.ssl.internal.ssl.Provider
+            3. Do: keytool -genkey -alias tomcat -keyalg RSA
+               RSA is essential to work with Netscape and IIS.
+               Use "changeit" as password. ( or add keypass attribute )
+               You don't need to sign the certificate.
+
+            You can set parameter keystore and keypass if you want
+            to change the default ( user.home/.keystore with changeit )
+        -->
+        <!--
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+                value="org.apache.tomcat.service.http.HttpConnectionHandler"/>
+            <Parameter name="port"
+                value="8443"/>
+            <Parameter name="socketFactory"
+                value="org.apache.tomcat.net.SSLSocketFactory" />
+        </Connector>
+        -->
+
+        <!-- Apache AJP12 support. This is also used to shut down tomcat.
+          -->
+        <Connector className="org.apache.tomcat.service.PoolTcpConnector">
+            <Parameter name="handler"
+       value="org.apache.tomcat.service.connector.Ajp12ConnectionHandler"/>
+            <Parameter name="port" value="8007"/>
+        </Connector>
+
+        <!-- ==================== Special webapps ==================== -->
+        <!-- You don't need this if you place your app in webapps/
+             and use defaults.
+             For security you'll also need to edit tomcat.policy
+
+             Defaults are: debug=0, reloadable=true, trusted=false
+             (trusted allows you to access tomcat internal objects
+             with FacadeManager ), crossContext=true (allows you to
+             access other contexts via ServletContext.getContext())
+
+             If security manager is enabled, you'll have read perms.
+             in the webapps dir and read/write in the workdir.
+         -->
+
+        <!--
+        <Context path="/examples"
+                 docBase="webapps/examples"
+                 crossContext="false"
+                 debug="0"
+                 reloadable="true" >
+        </Context>
+        -->
+
+        <Context path="../../"
+                 docBase="webapps/jcs"
+                 crossContext="false"
+                 debug="0"
+                 reloadable="true" >
+        </Context>
+
+
+        <!-- Admin context will use tomcat.core to add/remove/get info about
+             the webapplications and tomcat internals.
+             By default it is not trusted - i.e. it is not allowed access to
+             tomcat internals, only informations that are available to all
+             servlets are visible.
+
+             If you change this to true, make sure you set a password.
+          -->
+      <!--
+        <Context path="/admin"
+                 docBase="webapps/admin"
+                 crossContext="true"
+                 debug="0"
+                 reloadable="true"
+                 trusted="false" >
+        </Context>
+      -->
+
+        <!-- Virtual host example -
+             In "127.0.0.1" virtual host we'll reverse "/" and
+             "/examples"
+             (XXX need a better example )
+             (use  "http://127.0.0.1/examples" )
+        <Host name="127.0.0.1" >
+           <Context path=""
+                    docBase="webapps/examples" />
+           <Context path="/examples"
+                    docBase="webapps/ROOT" />
+        </Host>
+         -->
+
+    </ContextManager>
+</Server>
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/ConcurrentRemovalLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/ConcurrentRemovalLoadTest.java
new file mode 100644
index 0000000..b32ffab
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/ConcurrentRemovalLoadTest.java
@@ -0,0 +1,156 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Test which exercises the hierarchical removal when the cache is active.
+ */
+public class ConcurrentRemovalLoadTest
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     * @param testName
+     */
+    public ConcurrentRemovalLoadTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { RemovalTestUtil.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit. This verfies that we can remove hierarchically while the region
+     * is active.
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new RemovalTestUtil( "testRemoveCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runTestPutThenRemoveCategorical( 0, 200 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testPutCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runPutInRange( 300, 400 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testPutCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runPutInRange( 401, 600 );
+            }
+        } );
+
+        // stomp on previous put
+        suite.addTest( new RemovalTestUtil( "testPutCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runPutInRange( 401, 600 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testRemoveCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runTestPutThenRemoveCategorical( 601, 700 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testRemoveCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runTestPutThenRemoveCategorical( 701, 800 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testRemoveCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runTestPutThenRemoveCategorical( 901, 1000 );
+            }
+        } );
+
+        suite.addTest( new RemovalTestUtil( "testPutCache2" )
+        {
+            // verify that there are no errors with concurrent gets.
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                runGetInRange( 0, 1000, false );
+            }
+        } );
+        return suite;
+    }
+
+    /**
+     * Test setup
+     * <p>
+     * @throws Exception
+     */
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestRemoval.ccf" );
+        JCS.getInstance( "testCache1" );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSCacheElementRetrievalUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSCacheElementRetrievalUnitTest.java
new file mode 100644
index 0000000..5c300d8
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSCacheElementRetrievalUnitTest.java
@@ -0,0 +1,54 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+/**
+ * @author Aaron Smuts
+ *
+ */
+public class JCSCacheElementRetrievalUnitTest
+    extends TestCase
+{
+
+    /**
+     *
+     * @throws Exception
+     */
+    public void testSimpleElementRetrieval()
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        jcs.put( "test_key", "test_data" );
+
+        long now = System.currentTimeMillis();
+        ICacheElement<String, String> elem = jcs.getCacheElement( "test_key" );
+        assertEquals( "Name wasn't right", "testCache1", elem.getCacheName() );
+
+        long diff = now - elem.getElementAttributes().getCreateTime();
+        assertTrue( "Create time should have been at or after the call", diff >= 0 );
+
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSConcurrentCacheAccessUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSConcurrentCacheAccessUnitTest.java
new file mode 100644
index 0000000..f234dd8
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSConcurrentCacheAccessUnitTest.java
@@ -0,0 +1,164 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.GroupCacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Test Case for JCS-73, modeled after the Groovy code by Alexander Kleymenov
+ *
+ * @author Thomas Vandahl
+ *
+ */
+public class JCSConcurrentCacheAccessUnitTest
+    extends TestCase
+{
+    /**
+     * the cache instance
+     */
+    protected GroupCacheAccess<Integer, String> cache;
+
+    /**
+     * the group name
+     */
+    protected String group = "group";
+
+    /**
+     * the error count
+     */
+    protected AtomicInteger errcount;
+
+    @Override
+	protected void setUp() throws Exception
+	{
+        super.setUp();
+        JCS.setConfigFilename( "/TestJCS-73.ccf" );
+        cache = JCS.getGroupCacheInstance( "cache" );
+        errcount = new AtomicInteger(0);
+	}
+
+    @Override
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+        cache.clear();
+        cache.dispose();
+    }
+
+    /**
+     * Worker thread
+     */
+    protected class Worker extends Thread
+    {
+    	@Override
+		public void run()
+		{
+			String name = getName();
+
+			for (int idx = 0; idx < 10000; idx++)
+			{
+				if (idx > 0)
+				{
+					// get previously stored value
+		            String res = cache.getFromGroup(Integer.valueOf(idx-1), group);
+
+		            if (res == null)
+		            {
+		                // null value got inspite of the fact it was placed in cache!
+		                System.out.println("ERROR: for " + idx + " in " + name);
+		                errcount.incrementAndGet();
+
+		                // try to get the value again:
+		                int n = 5;
+		                while (n-- > 0)
+		                {
+		                    res = cache.getFromGroup(Integer.valueOf(idx-1), group);
+		                    if (res != null)
+		                    {
+		                        // the value finally appeared in cache
+		                    	System.out.println("ERROR FIXED for " + idx + ": " + res + " " + name);
+		                    	errcount.decrementAndGet();
+		                        break;
+		                    }
+
+		                    System.out.println("ERROR STILL PERSISTS for " + idx + " in " + name);
+		                    try
+		                    {
+								Thread.sleep(1000);
+							}
+		                    catch (InterruptedException e)
+							{
+								// continue
+							}
+		                }
+		            }
+
+		            Assert.assertEquals("Values do not match", String.valueOf(idx-1), res);
+				}
+
+				 // put value in the cache
+		        try
+		        {
+					cache.putInGroup(Integer.valueOf(idx), group, String.valueOf(idx));
+				}
+		        catch (CacheException e)
+		        {
+		        	// continue
+				}
+
+//		        if ((idx % 1000) == 0)
+//		        {
+//		        	System.out.println(name + " " + idx);
+//		        }
+			}
+
+		}
+    }
+
+	/**
+     *
+     * @throws Exception
+     */
+    public void testConcurrentAccess()
+        throws Exception
+    {
+    	Worker[] worker = new Worker[10];
+
+        for (int i = 0; i < 10; i++)
+        {
+        	worker[i] = new Worker();
+        	worker[i].start();
+        }
+
+        for (int i = 0; i < 10; i++)
+        {
+        	worker[i].join();
+        }
+
+        assertEquals("Error count should be 0",  0, errcount.intValue());
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSLightLoadUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSLightLoadUnitTest.java
new file mode 100644
index 0000000..7e3638d
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSLightLoadUnitTest.java
@@ -0,0 +1,102 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Runs a few thousand queries.
+ */
+public class JCSLightLoadUnitTest
+    extends TestCase
+{
+    /** number to use for the test */
+    private static int items = 20000;
+
+    /**
+     * Test setup
+     * @throws Exception
+     */
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestSimpleLoad.ccf" );
+        JCS.getInstance( "testCache1" );
+    }
+
+    /**
+     * @param testName
+     */
+    public JCSLightLoadUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Description of the Method
+     * @param args Description of the Parameter
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { JCSLightLoadUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        return new TestSuite( JCSLightLoadUnitTest.class );
+    }
+
+    /**
+     * A unit test for JUnit
+     * @throws Exception Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+        //        ICompositeCacheAttributes cattr = jcs.getCacheAttributes();
+        //        cattr.setMaxObjects( 20002 );
+        //        jcs.setCacheAttributes( cattr );
+
+        for ( int i = 1; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items; i > 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        // test removal
+        jcs.remove( "300:key" );
+        assertNull( jcs.get( "300:key" ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSRemovalSimpleConcurrentTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSRemovalSimpleConcurrentTest.java
new file mode 100644
index 0000000..92a3a56
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSRemovalSimpleConcurrentTest.java
@@ -0,0 +1,192 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Verify that basic removal functionality works.
+ */
+public class JCSRemovalSimpleConcurrentTest
+    extends TestCase
+{
+    /**
+     * @param testName
+     */
+    public JCSRemovalSimpleConcurrentTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Test setup
+     * <p>
+     * @throws Exception
+     */
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestRemoval.ccf" );
+        JCS.getInstance( "testCache1" );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * <p>
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { JCSRemovalSimpleConcurrentTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * Verify that 2 level deep hierchical removal works.
+     * <p>
+     * @throws Exception
+     */
+    public void testTwoDeepRemoval()
+        throws Exception
+    {
+        int count = 500;
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.put( "key:" + i + ":anotherpart", "data" + i );
+        }
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            String res = jcs.get( "key:" + i + ":anotherpart" );
+            assertNotNull( "[key:" + i + ":anotherpart] should not be null, " + jcs.getStats(), res );
+        }
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.remove( "key:" + i + ":" );
+            assertNull( jcs.getStats(), jcs.get( "key:" + i + ":anotherpart" ) );
+        }
+
+    }
+
+    /**
+     * Verify that 1 level deep hierchical removal works.
+     *
+     * @throws Exception
+     */
+    public void testSingleDepthRemoval()
+        throws Exception
+    {
+
+        int count = 500;
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.remove( i + ":" );
+            assertNull( jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Verify that clear removes everyting as it should.
+     * <p>
+     * @throws Exception
+     */
+    public void testClear()
+        throws Exception
+    {
+
+        int count = 500;
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+        jcs.clear();
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            if ( res != null )
+            {
+                assertNull( "[" + i + ":key] should be null after remvoeall" + jcs.getStats(), res );
+            }
+        }
+    }
+
+    /**
+     * Verify that we can clear repeatedly without error.
+     *
+     * @throws Exception
+     */
+    public void testClearRepeatedlyWithoutError()
+        throws Exception
+    {
+        int count = 500;
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        jcs.clear();
+
+        for ( int i = 0; i <= count; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        for ( int i = count; i >= 0; i-- )
+        {
+            jcs.put( i + ":key", "data" + i );
+            jcs.clear();
+            String res = jcs.get( i + ":key" );
+            if ( res != null )
+            {
+                assertNull( "[" + i + ":key] should be null after remvoeall" + jcs.getStats(), res );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSThrashTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSThrashTest.java
new file mode 100644
index 0000000..704b6f0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSThrashTest.java
@@ -0,0 +1,330 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is based on a test that was posted to the user's list:
+ * <p>
+ * http://www.opensubscriber.com/message/jcs-users@jakarta.apache.org/2435965.html
+ */
+public class JCSThrashTest
+    extends TestCase
+{
+    /** The logger. */
+    private static final Log LOG = LogFactory.getLog( JCSThrashTest.class.getName() );
+
+    /**
+     * the cache instance
+     */
+    protected CacheAccess<String, Serializable> jcs;
+
+    /**
+     * @param args
+     */
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( JCSThrashTest.class );
+    }
+
+    /**
+     * @param arg0
+     */
+    public JCSThrashTest( String arg0 )
+    {
+        super( arg0 );
+    }
+
+    /**
+     * Sets up the test
+     * @throws Exception
+     */
+    @Override
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        JCS.setConfigFilename( "/TestThrash.ccf" );
+        jcs = JCS.getInstance( "testcache" );
+    }
+
+    /**
+     * @throws Exception
+     */
+    @Override
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+        jcs.clear();
+        jcs.dispose();
+    }
+
+    /**
+     * Tests adding an entry.
+     * @throws Exception
+     */
+    public void testPut()
+        throws Exception
+    {
+        final String value = "value";
+        final String key = "key";
+
+        // Make sure the element is not found
+        assertEquals( 0, getListSize() );
+
+        assertNull( jcs.get( key ) );
+
+        jcs.put( key, value );
+
+        // Get the element
+        LOG.info( "jcs.getStats(): " + jcs.getStatistics() );
+        assertEquals( 1, getListSize() );
+        assertNotNull( jcs.get( key ) );
+        assertEquals( value, jcs.get( key ) );
+    }
+
+    /**
+     * Test elements can be removed from the store
+     * @throws Exception
+     */
+    public void testRemove()
+        throws Exception
+    {
+        jcs.put( "key1", "value1" );
+        assertEquals( 1, getListSize() );
+
+        jcs.remove( "key1" );
+        assertEquals( 0, getListSize() );
+
+        jcs.put( "key2", "value2" );
+        jcs.put( "key3", "value3" );
+        assertEquals( 2, getListSize() );
+
+        jcs.remove( "key2" );
+        assertEquals( 1, getListSize() );
+
+        // Try to remove an object that is not there in the store
+        jcs.remove( "key4" );
+        assertEquals( 1, getListSize() );
+    }
+
+    /**
+     * This does a bunch of work and then verifies that the memory has not grown by much. Most of
+     * the time the amount of memory used after the test is less.
+     * @throws Exception
+     */
+    public void testForMemoryLeaks()
+        throws Exception
+    {
+        long differenceMemoryCache = thrashCache();
+        LOG.info( "Memory Difference is: " + differenceMemoryCache );
+        assertTrue( differenceMemoryCache < 500000 );
+
+        //LOG.info( "Memory Used is: " + measureMemoryUse() );
+    }
+
+    /**
+     * @return time
+     * @throws Exception
+     */
+    protected long thrashCache()
+        throws Exception
+    {
+        long startingSize = measureMemoryUse();
+        LOG.info( "Memory Used is: " + startingSize );
+
+        final String value = "value";
+        final String key = "key";
+
+        // Add the entry
+        jcs.put( key, value );
+
+        // Create 15 threads that read the keys;
+        final List<Executable> executables = new ArrayList<Executable>();
+        for ( int i = 0; i < 15; i++ )
+        {
+            final JCSThrashTest.Executable executable = new JCSThrashTest.Executable()
+            {
+                @Override
+                public void execute()
+                    throws Exception
+                {
+                    for ( int j = 0; j < 500; j++ )
+                    {
+                        final String keyj = "key" + j;
+                        jcs.get( keyj );
+                    }
+                    jcs.get( "key" );
+                }
+            };
+            executables.add( executable );
+        }
+
+        // Create 15 threads that are insert 500 keys with large byte[] as
+        // values
+        for ( int i = 0; i < 15; i++ )
+        {
+            final JCSThrashTest.Executable executable = new JCSThrashTest.Executable()
+            {
+                @Override
+                public void execute()
+                    throws Exception
+                {
+
+                    // Add a bunch of entries
+                    for ( int j = 0; j < 500; j++ )
+                    {
+                        // Use a random length value
+                        final String keyj = "key" + j;
+                        byte[] valuej = new byte[10000];
+                        jcs.put( keyj, valuej );
+                    }
+                }
+            };
+            executables.add( executable );
+        }
+
+        runThreads( executables );
+        jcs.clear();
+
+        long finishingSize = measureMemoryUse();
+        LOG.info( "Memory Used is: " + finishingSize );
+        return finishingSize - startingSize;
+    }
+
+    /**
+     * Runs a set of threads, for a fixed amount of time.
+     * <p>
+     * @param executables
+     * @throws Exception
+     */
+    protected void runThreads( final List<Executable> executables )
+        throws Exception
+    {
+
+        final long endTime = System.currentTimeMillis() + 10000;
+        final Throwable[] errors = new Throwable[1];
+
+        // Spin up the threads
+        final Thread[] threads = new Thread[executables.size()];
+        for ( int i = 0; i < threads.length; i++ )
+        {
+            final JCSThrashTest.Executable executable = executables.get( i );
+            threads[i] = new Thread()
+            {
+                @Override
+                public void run()
+                {
+                    try
+                    {
+                        // Run the thread until the given end time
+                        while ( System.currentTimeMillis() < endTime )
+                        {
+                            executable.execute();
+                        }
+                    }
+                    catch ( Throwable t )
+                    {
+                        // Hang on to any errors
+                        errors[0] = t;
+                    }
+                }
+            };
+            threads[i].start();
+        }
+
+        // Wait for the threads to finish
+        for ( int i = 0; i < threads.length; i++ )
+        {
+            threads[i].join();
+        }
+
+        // Throw any error that happened
+        if ( errors[0] != null )
+        {
+            throw new Exception( "Test thread failed.", errors[0] );
+        }
+    }
+
+    /**
+     * Measure memory used by the VM.
+     * <p>
+     * @return bytes
+     * @throws InterruptedException
+     */
+    protected long measureMemoryUse()
+        throws InterruptedException
+    {
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+    }
+
+    /**
+     * A runnable, that can throw an exception.
+     */
+    protected interface Executable
+    {
+        /**
+         * Executes this object.
+         * @throws Exception
+         */
+        void execute()
+            throws Exception;
+    }
+
+    /**
+     * @return size
+     */
+    private int getListSize()
+    {
+        final String listSize = "List Size";
+        final String lruMemoryCache = "LRU Memory Cache";
+        String result = "0";
+        List<IStats> istats = jcs.getStatistics().getAuxiliaryCacheStats();
+        for ( IStats istat : istats )
+        {
+            List<IStatElement<?>> statElements = istat.getStatElements();
+            if ( lruMemoryCache.equals( istat.getTypeName() ) )
+            {
+                for ( IStatElement<?> statElement : statElements )
+                {
+                    if ( listSize.equals( statElement.getName() ) )
+                    {
+                        result = statElement.getData().toString();
+                    }
+                }
+            }
+        }
+        return Integer.parseInt( result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSUnitTest.java
new file mode 100644
index 0000000..e945da4
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSUnitTest.java
@@ -0,0 +1,115 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.jcs.access.CacheAccess;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Random;
+
+/**
+ * Simple test for the JCS class.
+ */
+public class JCSUnitTest
+    extends TestCase
+{
+    /** A random for key generation. */
+    Random random = new Random();
+
+    /**
+     * @param testName
+     */
+    public JCSUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return Test
+     */
+    public static Test suite()
+    {
+        return new TestSuite( JCSUnitTest.class );
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { JCSUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testJCS()
+        throws Exception
+    {
+        CacheAccess<String, LinkedList<HashMap<String, String>>> jcs = JCS.getInstance( "testCache1" );
+
+        LinkedList<HashMap<String, String>> list = buildList();
+
+        jcs.put( "some:key", list );
+
+        assertEquals( list, jcs.get( "some:key" ) );
+    }
+
+    /**
+     * @return builds a list
+     */
+    private LinkedList<HashMap<String, String>> buildList()
+    {
+        LinkedList<HashMap<String, String>> list = new LinkedList<HashMap<String,String>>();
+
+        for ( int i = 0; i < 100; i++ )
+        {
+            list.add( buildMap() );
+        }
+
+        return list;
+    }
+
+    /**
+     * @return a map
+     */
+    private HashMap<String, String> buildMap()
+    {
+        HashMap<String, String> map = new HashMap<String, String>();
+
+        byte[] keyBytes = new byte[32];
+        byte[] valBytes = new byte[128];
+
+        for ( int i = 0; i < 10; i++ )
+        {
+            random.nextBytes( keyBytes );
+            random.nextBytes( valBytes );
+
+            map.put( new String( keyBytes ), new String( valBytes ) );
+        }
+
+        return map;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSvsHashtablePerformanceTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSvsHashtablePerformanceTest.java
new file mode 100644
index 0000000..ee600ab
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/JCSvsHashtablePerformanceTest.java
@@ -0,0 +1,210 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Hashtable;
+
+/**
+ * This test ensures that basic memory operations are with a speficified order of magnitude of the
+ * java.util.Hashtable.
+ * <p>
+ * Currenlty JCS is un 2x a hashtable for gets, and under 1.2x for puts.
+ */
+public class JCSvsHashtablePerformanceTest
+    extends TestCase
+{
+    /** jcs / hashtable */
+    float ratioPut = 0;
+
+    /** jcs / hashtable */
+    float ratioGet = 0;
+
+    /** ration goal */
+    float target = 3.50f;
+
+    /** Times to run the test */
+    int loops = 20;
+
+    /** how many puts and gets to run */
+    int tries = 50000;
+
+    /**
+     * @param testName
+     */
+    public JCSvsHashtablePerformanceTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        return new TestSuite( JCSvsHashtablePerformanceTest.class );
+    }
+
+    /**
+     * A unit test for JUnit
+     * @throws Exception Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        Log log1 = LogFactory.getLog( LRUMemoryCache.class );
+        if ( log1.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+        Log log2 = LogFactory.getLog( JCS.class );
+        if ( log2.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+        doWork();
+        assertTrue( this.ratioPut < target );
+        assertTrue( this.ratioGet < target );
+    }
+
+    /**
+     *
+     */
+    public void doWork()
+    {
+
+        long start = 0;
+        long end = 0;
+        long time = 0;
+        float tPer = 0;
+
+        long putTotalJCS = 0;
+        long getTotalJCS = 0;
+        long putTotalHashtable = 0;
+        long getTotalHashtable = 0;
+
+        try
+        {
+
+            JCS.setConfigFilename( "/TestJCSvHashtablePerf.ccf" );
+            CacheAccess<String, String> cache = JCS.getInstance( "testCache1" );
+
+            for ( int j = 0; j < loops; j++ )
+            {
+
+                String name = "JCS      ";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                // /////////////////////////////////////////////////////////////
+                name = "Hashtable";
+                Hashtable<String, String> cache2 = new Hashtable<String, String>();
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                System.out.println( "\n" );
+            }
+
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace( System.out );
+            System.out.println( e );
+        }
+
+        long putAvJCS = putTotalJCS / loops;
+        long getAvJCS = getTotalJCS / loops;
+        long putAvHashtable = putTotalHashtable / loops;
+        long getAvHashtable = getTotalHashtable / loops;
+
+        System.out.println( "Finished " + loops + " loops of " + tries + " gets and puts" );
+
+        System.out.println( "\n" );
+        System.out.println( "Put average for JCS       = " + putAvJCS );
+        System.out.println( "Put average for Hashtable = " + putAvHashtable );
+        ratioPut = Float.intBitsToFloat( (int) putAvJCS ) / Float.intBitsToFloat( (int) putAvHashtable );
+        System.out.println( "JCS puts took " + ratioPut + " times the Hashtable, the goal is <" + target + "x" );
+
+        System.out.println( "\n" );
+        System.out.println( "Get average for JCS       = " + getAvJCS );
+        System.out.println( "Get average for Hashtable = " + getAvHashtable );
+        ratioGet = Float.intBitsToFloat( (int) getAvJCS ) / Float.intBitsToFloat( (int) getAvHashtable );
+        System.out.println( "JCS gets took " + ratioGet + " times the Hashtable, the goal is <" + target + "x" );
+
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        JCSvsHashtablePerformanceTest test = new JCSvsHashtablePerformanceTest( "command" );
+        test.doWork();
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/RemovalTestUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/RemovalTestUtil.java
new file mode 100644
index 0000000..5da5639
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/RemovalTestUtil.java
@@ -0,0 +1,130 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Simple methods to be run by active test suites that test removal.
+ *
+ */
+public class RemovalTestUtil
+    extends TestCase
+{
+
+    /**
+     * Constructor for the TestSimpleLoad object
+     *
+     * @param testName
+     *            Description of the Parameter
+     */
+    public RemovalTestUtil( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Adds elements in the range specified and then removes them using the
+     * categorical or substring removal method.
+     *
+     * @param start
+     * @param end
+     *
+     * @throws Exception
+     *                Description of the Exception
+     */
+    public void runTestPutThenRemoveCategorical( int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = end; i >= start; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.remove( i + ":" );
+            assertNull( jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Put items in the cache in this key range. Can be used to verify that
+     * concurrent operations are not effected by things like hierchical removal.
+     *
+     * @param start
+     *            int
+     * @param end
+     *            int
+     * @throws Exception
+     */
+    public void runPutInRange( int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = end; i >= start; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+    }
+
+    /**
+     * Just get from start to end.
+     *
+     * @param start
+     *            int
+     * @param end
+     *            int
+     * @param check
+     *            boolean -- check to see if the items are in the cache.
+     * @throws Exception
+     */
+    public void runGetInRange( int start, int end, boolean check )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        // don't care if they are found
+        for ( int i = end; i >= start; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            if ( check )
+            {
+                assertNotNull( "[" + i + ":key] should not be null", res );
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestLogConfigurationUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestLogConfigurationUtil.java
new file mode 100644
index 0000000..83d5ae1
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestLogConfigurationUtil.java
@@ -0,0 +1,46 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.WriterAppender;
+
+import java.io.StringWriter;
+
+/** Utility for testing log messages. */
+public class TestLogConfigurationUtil
+{
+    /**
+     * Configures a logger for the given name. This allows us to check the log output.
+     * <p>
+     * @param stringWriter string writer
+     * @param loggerName logger name
+     */
+    public static void configureLogger( StringWriter stringWriter, String loggerName )
+    {
+        Logger logger = Logger.getLogger( loggerName );
+        WriterAppender appender = new WriterAppender( new PatternLayout(), stringWriter );
+
+        logger.addAppender( appender );
+        logger.setLevel( Level.DEBUG );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestTCPLateralCache.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestTCPLateralCache.java
new file mode 100644
index 0000000..dd39d77
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/TestTCPLateralCache.java
@@ -0,0 +1,155 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Test which exercises the indexed disk cache. This one uses three different
+ * regions for thre threads.
+ *
+ * @version $Id: TestTCPLateralCache.java 1593844 2014-05-11 19:53:32Z rmannibucau $
+ */
+public class TestTCPLateralCache
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory
+     * cache regions.
+     */
+    private static int items = 200;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public TestTCPLateralCache( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { TestTCPLateralCache.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new TestTCPLateralCache( "testTcpRegion1_no_receiver" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "testTcpRegion1" );
+            }
+        } );
+
+        //        suite.addTest( new TestTCPLateralCache( "testIndexedDiskCache2" )
+        //        {
+        //            public void runTest() throws Exception
+        //            {
+        //                this.runTestForRegion( "indexedRegion2" );
+        //            }
+        //        } );
+        //
+        //        suite.addTest( new TestTCPLateralCache( "testIndexedDiskCache3" )
+        //        {
+        //            public void runTest() throws Exception
+        //            {
+        //                this.runTestForRegion( "indexedRegion3" );
+        //            }
+        //        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestTCPLateralCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Remove all the items
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/ZeroSizeCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/ZeroSizeCacheUnitTest.java
new file mode 100644
index 0000000..fc6d6be
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/ZeroSizeCacheUnitTest.java
@@ -0,0 +1,90 @@
+package org.apache.commons.jcs;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class ZeroSizeCacheUnitTest
+    extends TestCase
+{
+    /** number to get each loop */
+    private static int items = 20000;
+
+    /**
+     * Test setup
+     * <p>
+     * @throws Exception
+     */
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestZeroSizeCache.ccf" );
+        JCS.getInstance( "testCache1" );
+    }
+
+    /**
+     * Verify that a 0 size cache does not result in errors. You should be able
+     * to disable a region this way.
+     * @throws Exception
+     *
+     */
+    public void testPutGetRemove()
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        // all the gets should be null
+        for ( int i = items; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNull( "[" + i + ":key] should be null", res );
+        }
+
+        // test removal, should be no exceptions
+        jcs.remove( "300:key" );
+
+        // allow the shrinker to run
+        Thread.sleep( 500 );
+
+        // do it again.
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items; i >= 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            assertNull( "[" + i + ":key] should be null", res );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/CacheAccessUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/CacheAccessUnitTest.java
new file mode 100644
index 0000000..dbee697
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/CacheAccessUnitTest.java
@@ -0,0 +1,402 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.ObjectExistsException;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests the methods of the cache access class from which the class JCS extends.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class CacheAccessUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we get an object exists exception if the item is in the cache.
+     * @throws Exception
+     */
+    public void testPutSafe()
+        throws Exception
+    {
+        CacheAccess<String, String> access = JCS.getInstance( "test" );
+        assertNotNull( "We should have an access class", access );
+
+        String key = "mykey";
+        String value = "myvalue";
+
+        access.put( key, value );
+
+        String returnedValue1 = access.get( key );
+        assertEquals( "Wrong value returned.", value, returnedValue1 );
+
+        try
+        {
+            access.putSafe( key, "someothervalue" );
+            fail( "We should have received an eception since this key is alredy in the cache." );
+        }
+        catch ( CacheException e )
+        {
+            assertTrue( "Wrong type of exception.", e instanceof ObjectExistsException );
+            assertTrue( "Should have the key in the error message.", e.getMessage().indexOf( "[" + key + "]" ) != -1 );
+        }
+
+        String returnedValue2 = access.get( key );
+        assertEquals( "Wrong value returned.  Shoudl still be the original.", value, returnedValue2 );
+    }
+
+    /**
+     * Try to put a null key and verify that we get an exception.
+     * @throws Exception
+     */
+    public void testPutNullKey()
+        throws Exception
+    {
+        CacheAccess<String, String> access = JCS.getInstance( "test" );
+        assertNotNull( "We should have an access class", access );
+
+        String key = null;
+        String value = "myvalue";
+
+        try
+        {
+            access.put( key, value );
+            fail( "Should not have been able to put a null key." );
+        }
+        catch ( CacheException e )
+        {
+            assertTrue( "Should have the work null in the error message.", e.getMessage().indexOf( "null" ) != -1 );
+        }
+    }
+
+    /**
+     * Try to put a null value and verify that we get an exception.
+     * @throws Exception
+     */
+    public void testPutNullValue()
+        throws Exception
+    {
+        CacheAccess<String, String> access = JCS.getInstance( "test" );
+        assertNotNull( "We should have an access class", access );
+
+        String key = "myKey";
+        String value = null;
+
+        try
+        {
+            access.put( key, value );
+            fail( "Should not have been able to put a null object." );
+        }
+        catch ( CacheException e )
+        {
+            assertTrue( "Should have the work null in the error message.", e.getMessage().indexOf( "null" ) != -1 );
+        }
+    }
+
+    /**
+     * Verify that elements that go in the region after this call take the new attributes.
+     * @throws Exception
+     */
+    public void testSetDefaultElementAttributes()
+        throws Exception
+    {
+        CacheAccess<String, String> access = JCS.getInstance( "test" );
+        assertNotNull( "We should have an access class", access );
+
+        long maxLife = 9876;
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(maxLife);
+
+        access.setDefaultElementAttributes( attr );
+
+        assertEquals( "Wrong element attributes.", attr.getMaxLife(), access.getDefaultElementAttributes()
+            .getMaxLife() );
+
+        String key = "mykey";
+        String value = "myvalue";
+
+        access.put( key, value );
+
+        ICacheElement<String, String> element = access.getCacheElement( key );
+
+        assertEquals( "Wrong max life.  Should have the new value.", maxLife, element.getElementAttributes()
+            .getMaxLife() );
+    }
+
+    /**
+     * Verify that getCacheElements returns the elements requested based on the key.
+     * @throws Exception
+     */
+    public void testGetCacheElements()
+        throws Exception
+    {
+        //SETUP
+        CacheAccess<String, String> access = JCS.getInstance( "test" );
+        assertNotNull( "We should have an access class", access );
+
+        String keyOne = "mykeyone";
+        String keyTwo = "mykeytwo";
+        String keyThree = "mykeythree";
+        String valueOne = "myvalueone";
+        String valueTwo = "myvaluetwo";
+        String valueThree = "myvaluethree";
+
+        access.put( keyOne, valueOne );
+        access.put( keyTwo, valueTwo );
+        access.put( keyThree, valueThree );
+
+        Set<String> input = new HashSet<String>();
+        input.add( keyOne );
+        input.add( keyTwo );
+
+        //DO WORK
+        Map<String, ICacheElement<String, String>> result = access.getCacheElements( input );
+
+        //VERIFY
+        assertEquals( "map size", 2, result.size() );
+        ICacheElement<String, String> elementOne = result.get( keyOne );
+        assertEquals( "value one", keyOne, elementOne.getKey() );
+        assertEquals( "value one", valueOne, elementOne.getVal() );
+        ICacheElement<String, String> elementTwo = result.get( keyTwo );
+        assertEquals( "value two", keyTwo, elementTwo.getKey() );
+        assertEquals( "value two", valueTwo, elementTwo.getVal() );
+    }
+
+    /**
+     * Verify that we can get a region using the define region method.
+     * @throws Exception
+     */
+    public void testRegionDefiniton()
+        throws Exception
+    {
+        CacheAccess<String, String> access = JCS.defineRegion( "test" );
+        assertNotNull( "We should have an access class", access );
+    }
+
+    /**
+     * Verify that we can get a region using the define region method with cache attributes.
+     * @throws Exception
+     */
+    public void testRegionDefinitonWithAttributes()
+        throws Exception
+    {
+        ICompositeCacheAttributes ca = new CompositeCacheAttributes();
+
+        long maxIdleTime = 8765;
+        ca.setMaxMemoryIdleTimeSeconds( maxIdleTime );
+
+        CacheAccess<String, String> access = JCS.defineRegion( "testRegionDefinitonWithAttributes", ca );
+        assertNotNull( "We should have an access class", access );
+
+        ICompositeCacheAttributes ca2 = access.getCacheAttributes();
+        assertEquals( "Wrong idle time setting.", ca.getMaxMemoryIdleTimeSeconds(), ca2.getMaxMemoryIdleTimeSeconds() );
+    }
+
+    /**
+     * Verify that we can get a region using the define region method with cache attributes and
+     * elemetn attributes.
+     * @throws Exception
+     */
+    public void testRegionDefinitonWithBothAttributes()
+        throws Exception
+    {
+        ICompositeCacheAttributes ca = new CompositeCacheAttributes();
+
+        long maxIdleTime = 8765;
+        ca.setMaxMemoryIdleTimeSeconds( maxIdleTime );
+
+        long maxLife = 9876;
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(maxLife);
+
+        CacheAccess<String, String> access = JCS.defineRegion( "testRegionDefinitonWithAttributes", ca, attr );
+        assertNotNull( "We should have an access class", access );
+
+        ICompositeCacheAttributes ca2 = access.getCacheAttributes();
+        assertEquals( "Wrong idle time setting.", ca.getMaxMemoryIdleTimeSeconds(), ca2.getMaxMemoryIdleTimeSeconds() );
+    }
+
+    /**
+     * Verify we can get some matching elements..
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatching_Normal()
+        throws Exception
+    {
+        // SETUP
+        int maxMemorySize = 1000;
+        String keyprefix1 = "MyPrefix1";
+        String keyprefix2 = "MyPrefix2";
+        String memoryCacheClassName = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMemoryCacheName( memoryCacheClassName );
+        cattr.setMaxObjects( maxMemorySize );
+
+        long maxLife = 9876;
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(maxLife);
+
+        CacheAccess<String, Integer> access = JCS.defineRegion( "testGetMatching_Normal", cattr, attr );
+
+        // DO WORK
+        int numToInsertPrefix1 = 10;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            access.put( keyprefix1 + String.valueOf( i ), Integer.valueOf( i ) );
+        }
+
+        int numToInsertPrefix2 = 50;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix2; i++ )
+        {
+            access.put( keyprefix2 + String.valueOf( i ), Integer.valueOf( i ) );
+        }
+
+        Map<String, Integer> result1 = access.getMatching( keyprefix1 + ".+" );
+        Map<String, Integer> result2 = access.getMatching( keyprefix2 + "\\S+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1:", numToInsertPrefix1, result1.size() );
+        assertEquals( "Wrong number returned 2:", numToInsertPrefix2, result2.size() );
+        //System.out.println( result1 );
+
+        // verify that the elements are unwrapped
+        for (Map.Entry<String, Integer> entry : result1.entrySet())
+        {
+            Object value = entry.getValue();
+            assertFalse( "Should not be a cache element.", value instanceof ICacheElement );
+        }
+    }
+
+    /**
+     * Verify we can get some matching elements..
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatchingElements_Normal()
+        throws Exception
+    {
+        // SETUP
+        int maxMemorySize = 1000;
+        String keyprefix1 = "MyPrefix1";
+        String keyprefix2 = "MyPrefix2";
+        String memoryCacheClassName = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMemoryCacheName( memoryCacheClassName );
+        cattr.setMaxObjects( maxMemorySize );
+
+        long maxLife = 9876;
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(maxLife);
+
+        CacheAccess<String, Integer> access = JCS.defineRegion( "testGetMatching_Normal", cattr, attr );
+
+        // DO WORK
+        int numToInsertPrefix1 = 10;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            access.put( keyprefix1 + String.valueOf( i ), Integer.valueOf( i ) );
+        }
+
+        int numToInsertPrefix2 = 50;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix2; i++ )
+        {
+            access.put( keyprefix2 + String.valueOf( i ), Integer.valueOf( i ) );
+        }
+
+        Map<String, ICacheElement<String, Integer>> result1 = access.getMatchingCacheElements( keyprefix1 + "\\S+" );
+        Map<String, ICacheElement<String, Integer>> result2 = access.getMatchingCacheElements( keyprefix2 + ".+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1:", numToInsertPrefix1, result1.size() );
+        assertEquals( "Wrong number returned 2:", numToInsertPrefix2, result2.size() );
+        //System.out.println( result1 );
+
+        // verify that the elements are wrapped
+        for (Map.Entry<String, ICacheElement<String, Integer>> entry : result1.entrySet())
+        {
+            Object value = entry.getValue();
+            assertTrue( "Should be a cache element.", value instanceof ICacheElement );
+        }
+    }
+
+    /**
+     * Verify we can use the group cache.
+     * <p>
+     * @throws Exception
+     */
+    public void testGroupCache()
+        throws Exception
+    {
+        GroupCacheAccess<String, Integer> access = JCS.getGroupCacheInstance( "testGroup" );
+        String groupName1 = "testgroup1";
+        String groupName2 = "testgroup2";
+
+        Set<String> keys1 = access.getGroupKeys( groupName1 );
+        assertNotNull(keys1);
+        assertEquals(0, keys1.size());
+
+        Set<String> keys2 = access.getGroupKeys( groupName2 );
+        assertNotNull(keys2);
+        assertEquals(0, keys2.size());
+
+        // DO WORK
+        int numToInsertGroup1 = 10;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertGroup1; i++ )
+        {
+            access.putInGroup(String.valueOf( i ), groupName1, Integer.valueOf( i ) );
+        }
+
+        int numToInsertGroup2 = 50;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertGroup2; i++ )
+        {
+            access.putInGroup(String.valueOf( i ), groupName2, Integer.valueOf( i + 1 ) );
+        }
+
+        keys1 = access.getGroupKeys( groupName1 ); // Test for JCS-102
+        assertNotNull(keys1);
+        assertEquals("Wrong number returned 1:", 10, keys1.size());
+
+        keys2 = access.getGroupKeys( groupName2 );
+        assertNotNull(keys2);
+        assertEquals("Wrong number returned 2:", 50, keys2.size());
+
+        assertEquals(Integer.valueOf(5), access.getFromGroup("5", groupName1));
+        assertEquals(Integer.valueOf(6), access.getFromGroup("5", groupName2));
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/SystemPropertyUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/SystemPropertyUnitTest.java
new file mode 100644
index 0000000..a301efd
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/SystemPropertyUnitTest.java
@@ -0,0 +1,88 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+/**
+ * This test is for the system property usage in configuration values.
+ *
+ * @author Aaron Smuts
+ *
+ */
+ at FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SystemPropertyUnitTest
+    extends TestCase
+{
+
+    /**
+     * Verify that we use a system property for a ${FOO} string in a value.
+     *
+     * @throws Exception
+     *
+     */
+    public void test1SystemPropertyInValueDelimiter()
+        throws Exception
+    {
+
+        int maxMemory = 1234;
+        System.getProperties().setProperty( "MY_SYSTEM_PROPERTY_DISK_DIR", "system_set" );
+        System.getProperties().setProperty( "MY_SYSTEM_PROPERTY_MAX_SIZE", String.valueOf( maxMemory ) );
+
+        JCS.setConfigFilename( "/TestSystemProperties.ccf" );
+
+        CacheAccess<String, String> cache = JCS.getInstance( "test1" );
+        assertEquals( "We should have used the system property for the memory size", maxMemory, cache
+            .getCacheAttributes().getMaxObjects() );
+
+        System.clearProperty("MY_SYSTEM_PROPERTY_DISK_DIR");
+        System.clearProperty("MY_SYSTEM_PROPERTY_MAX_SIZE");
+    }
+
+    /**
+     * Verify that we use a system property for a ${FOO} string in a value. We
+     * define a propety in the cache.ccf file, but we do not have it as a system
+     * property. The default value should be used, if one exists.
+     *
+     * @throws Exception
+     *
+     */
+    public void test2SystemPropertyMissingInValueDelimeter()
+        throws Exception
+    {
+        System.getProperties().setProperty( "MY_SYSTEM_PROPERTY_DISK_DIR", "system_set" );
+
+        CompositeCacheManager mgr = CompositeCacheManager.getUnconfiguredInstance();
+        mgr.configure( "/TestSystemProperties.ccf" );
+
+        CacheAccess<String, String> cache = JCS.getInstance( "missing" );
+        // TODO check against the actual default def
+        assertEquals( "We should have used the default property for the memory size", 100, cache.getCacheAttributes()
+            .getMaxObjects() );
+
+        System.clearProperty("MY_SYSTEM_PROPERTY_DISK_DIR");
+
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/TestCacheAccess.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/TestCacheAccess.java
new file mode 100644
index 0000000..66c5295
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/access/TestCacheAccess.java
@@ -0,0 +1,965 @@
+package org.apache.commons.jcs.access;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.control.event.ElementEventHandlerMockImpl;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+/**
+ * Allows the user to run common cache commands from the command line for a test cache. This also
+ * provide basic methods for use in unit tests.
+ */
+public class TestCacheAccess
+{
+    /** log instance */
+    private static final Log log = LogFactory.getLog( TestCacheAccess.class );
+
+    /** cache instance to use in testing */
+    private CacheAccess<String, String> cache_control = null;
+
+    /** cache instance to use in testing */
+    private GroupCacheAccess<String, String> group_cache_control = null;
+
+    /** do we use system.out.println to print out debug data? */
+    private static boolean isSysOut = false;
+
+    /** Construct and initialize the cachecontrol based on the config file. */
+    public TestCacheAccess()
+    {
+        this( "testCache1" );
+    }
+
+    /**
+     * @param regionName the name of the region.
+     */
+    public TestCacheAccess( String regionName )
+    {
+        try
+        {
+            cache_control = JCS.getInstance( regionName );
+            group_cache_control = JCS.getGroupCacheInstance( regionName );
+        }
+        catch ( Exception e )
+        {
+            log.error( "Problem getting cache instance", e );
+            p( e.toString() );
+        }
+    }
+
+    /**
+     * This is the main loop called by the main method.
+     */
+    public void runLoop()
+    {
+        try
+        {
+            // process user input till done
+            boolean notDone = true;
+            String message = null;
+            // wait to dispose
+            BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
+
+            help();
+
+            while ( notDone )
+            {
+                p( "enter command:" );
+
+                message = br.readLine();
+
+                if ( message == null || message.startsWith( "help" ) )
+                {
+                    help();
+                }
+                else if ( message.startsWith( "gc" ) )
+                {
+                    System.gc();
+                }
+                else if ( message.startsWith( "getAttributeNames" ) )
+                {
+                    long n_start = System.currentTimeMillis();
+                    String groupName = null;
+                    StringTokenizer toke = new StringTokenizer( message );
+                    int tcnt = 0;
+                    while ( toke.hasMoreElements() )
+                    {
+                        tcnt++;
+                        String t = (String) toke.nextElement();
+                        if ( tcnt == 2 )
+                        {
+                            groupName = t.trim();
+                        }
+                    }
+                    getAttributeNames( groupName );
+                    long n_end = System.currentTimeMillis();
+                    p( "---got attrNames for " + groupName + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+                }
+                else if ( message.startsWith( "shutDown" ) )
+                {
+                    CompositeCacheManager.getInstance().shutDown();
+                    //cache_control.dispose();
+                    notDone = false;
+                    //System.exit( -1 );
+                    return;
+                }
+                /////////////////////////////////////////////////////////////////////
+                // get multiple from a region
+                else if ( message.startsWith( "getm" ) )
+                {
+                    processGetMultiple( message );
+                }
+                else if ( message.startsWith( "getg" ) )
+                {
+                    processGetGroup( message );
+                }
+                else if ( message.startsWith( "getag" ) )
+                {
+                    processGetAutoGroup( message );
+                }
+                else if ( message.startsWith( "getMatching" ) )
+                {
+                    processGetMatching( message );
+                }
+                else if ( message.startsWith( "get" ) )
+                {
+                    processGet( message );
+                }
+                else if ( message.startsWith( "putg" ) )
+                {
+                    processPutGroup( message );
+                }
+                // put automatically
+                else if ( message.startsWith( "putag" ) )
+                {
+                    processPutAutoGroup( message );
+                }
+                else if ( message.startsWith( "putm" ) )
+                {
+                    String numS = message.substring( message.indexOf( " " ) + 1, message.length() );
+                    if ( numS == null )
+                    {
+                        p( "usage: putm numbertoput" );
+                    }
+                    else
+                    {
+                        int num = Integer.parseInt( numS.trim() );
+                        putMultiple( num );
+                    }
+                }
+                else if ( message.startsWith( "pute" ) )
+                {
+                    String numS = message.substring( message.indexOf( " " ) + 1, message.length() );
+                    if ( numS == null )
+                    {
+                        p( "usage: putme numbertoput" );
+                    }
+                    else
+                    {
+                        int num = Integer.parseInt( numS.trim() );
+                        long n_start = System.currentTimeMillis();
+                        for ( int n = 0; n < num; n++ )
+                        {
+                            IElementAttributes attrp = cache_control.getDefaultElementAttributes();
+                            ElementEventHandlerMockImpl hand = new ElementEventHandlerMockImpl();
+                            attrp.addElementEventHandler( hand );
+                            cache_control.put( "key" + n, "data" + n + " put from ta = junk", attrp );
+                        }
+                        long n_end = System.currentTimeMillis();
+                        p( "---put " + num + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+                    }
+                }
+                else if ( message.startsWith( "put" ) )
+                {
+                    processPut( message );
+                }
+                else if ( message.startsWith( "removem" ) )
+                {
+                    String numS = message.substring( message.indexOf( " " ) + 1, message.length() );
+                    if ( numS == null )
+                    {
+                        p( "usage: removem numbertoremove" );
+                    }
+                    else
+                    {
+                        int num = Integer.parseInt( numS.trim() );
+                        removeMultiple( num );
+                    }
+                }
+                else if ( message.startsWith( "removeall" ) )
+                {
+                    cache_control.clear();
+                    p( "removed all" );
+                }
+                else if ( message.startsWith( "remove" ) )
+                {
+                    String key = message.substring( message.indexOf( " " ) + 1, message.length() );
+                    cache_control.remove( key );
+                    p( "removed " + key );
+                }
+                else if ( message.startsWith( "deattr" ) )
+                {
+                    IElementAttributes ae = cache_control.getDefaultElementAttributes();
+                    p( "Default IElementAttributes " + ae );
+                }
+                else if ( message.startsWith( "cloneattr" ) )
+                {
+                    String numS = message.substring( message.indexOf( " " ) + 1, message.length() );
+                    if ( numS == null )
+                    {
+                        p( "usage: put numbertoput" );
+                    }
+                    else
+                    {
+                        int num = Integer.parseInt( numS.trim() );
+                        IElementAttributes attrp = new ElementAttributes();
+                        long n_start = System.currentTimeMillis();
+                        for ( int n = 0; n < num; n++ )
+                        {
+                            attrp.copy();
+                        }
+                        long n_end = System.currentTimeMillis();
+                        p( "---cloned attr " + num + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+                    }
+                }
+                else if ( message.startsWith( "switch" ) )
+                {
+                    String name = message.substring( message.indexOf( " " ) + 1, message.length() );
+
+                    setRegion( name );
+                    p( "switched to cache = " + name );
+                    p( cache_control.toString() );
+                }
+                else if ( message.startsWith( "stats" ) )
+                {
+                    p( cache_control.getStats() );
+                }
+                else if ( message.startsWith( "gc" ) )
+                {
+                    System.gc();
+                    p( "Called system.gc()" );
+                }
+                else if ( message.startsWith( "random" ) )
+                {
+                    if ( message.startsWith( "random" ) )
+                    {
+                        processRandom( message );
+                    }
+                }
+            }
+        }
+        catch ( CacheException e )
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+        }
+        catch (IOException e)
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processGetMultiple( String message )
+    {
+        int num = 0;
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                try
+                {
+                    num = Integer.parseInt( t.trim() );
+                }
+                catch ( NumberFormatException nfe )
+                {
+                    p( t + "not a number" );
+                }
+            }
+            else if ( tcnt == 3 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        if ( tcnt < 2 )
+        {
+            p( "usage: get numbertoget show values[true|false]" );
+        }
+        else
+        {
+            getMultiple( num, show );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processGetGroup( String message )
+    {
+        String key = null;
+        String group = null;
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                key = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                group = t.trim();
+            }
+            else if ( tcnt == 4 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        if ( tcnt < 2 )
+        {
+            p( "usage: get key show values[true|false]" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            try
+            {
+                Object obj = group_cache_control.getFromGroup( key, group );
+                if ( show && obj != null )
+                {
+                    p( obj.toString() );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( e );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---got " + key + " from group " + group + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processGetAutoGroup( String message )
+    {
+        // get auto from group
+
+        int num = 0;
+        String group = null;
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                num = Integer.parseInt( t.trim() );
+            }
+            else if ( tcnt == 3 )
+            {
+                group = t.trim();
+            }
+            else if ( tcnt == 4 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        if ( tcnt < 2 )
+        {
+            p( "usage: get key show values[true|false]" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            try
+            {
+                for ( int a = 0; a < num; a++ )
+                {
+                    Object obj = group_cache_control.getFromGroup( "keygr" + a, group );
+                    if ( show && obj != null )
+                    {
+                        p( obj.toString() );
+                    }
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( e );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---got " + num + " from group " + group + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     * @throws CacheException
+     */
+    private void processPutGroup( String message )
+        throws CacheException
+    {
+        String group = null;
+        String key = null;
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                key = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                group = t.trim();
+            }
+        }
+
+        if ( tcnt < 3 )
+        {
+            p( "usage: putg key group" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            group_cache_control.putInGroup( key, group, "data from putg ----asdfasfas-asfasfas-asfas in group " + group );
+            long n_end = System.currentTimeMillis();
+            p( "---put " + key + " in group " + group + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     * @throws CacheException
+     */
+    private void processPutAutoGroup( String message )
+        throws CacheException
+    {
+        String group = null;
+        int num = 0;
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                num = Integer.parseInt( t.trim() );
+            }
+            else if ( tcnt == 3 )
+            {
+                group = t.trim();
+            }
+        }
+
+        if ( tcnt < 3 )
+        {
+            p( "usage: putag num group" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            for ( int a = 0; a < num; a++ )
+            {
+                group_cache_control.putInGroup( "keygr" + a, group, "data " + a
+                    + " from putag ----asdfasfas-asfasfas-asfas in group " + group );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---put " + num + " in group " + group + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     * @throws CacheException
+     */
+    private void processPut( String message )
+        throws CacheException
+    {
+        String key = null;
+        String val = null;
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                key = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                val = t.trim();
+            }
+        }
+
+        if ( tcnt < 3 )
+        {
+            p( "usage: put key val" );
+        }
+        else
+        {
+
+            long n_start = System.currentTimeMillis();
+            cache_control.put( key, val );
+            long n_end = System.currentTimeMillis();
+            p( "---put " + key + " | " + val + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processRandom( String message )
+    {
+        String rangeS = "";
+        String numOpsS = "";
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                rangeS = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                numOpsS = t.trim();
+            }
+            else if ( tcnt == 4 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        String numS = message.substring( message.indexOf( " " ) + 1, message.length() );
+
+        int range = 0;
+        int numOps = 0;
+        try
+        {
+            range = Integer.parseInt( rangeS.trim() );
+            numOps = Integer.parseInt( numOpsS.trim() );
+        }
+        catch ( Exception e )
+        {
+            p( "usage: random range numOps show" );
+            p( "ex.  random 100 1000 false" );
+        }
+        if ( numS == null )
+        {
+            p( "usage: random range numOps show" );
+            p( "ex.  random 100 1000 false" );
+        }
+        else
+        {
+            random( range, numOps, show );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processGet( String message )
+    {
+        // plain old get
+
+        String key = null;
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                key = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        if ( tcnt < 2 )
+        {
+            p( "usage: get key show values[true|false]" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            try
+            {
+                Object obj = cache_control.get( key );
+                if ( show && obj != null )
+                {
+                    p( obj.toString() );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( e );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---got " + key + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * @param message
+     */
+    private void processGetMatching( String message )
+    {
+        // plain old get
+
+        String pattern = null;
+        boolean show = true;
+
+        StringTokenizer toke = new StringTokenizer( message );
+        int tcnt = 0;
+        while ( toke.hasMoreElements() )
+        {
+            tcnt++;
+            String t = (String) toke.nextElement();
+            if ( tcnt == 2 )
+            {
+                pattern = t.trim();
+            }
+            else if ( tcnt == 3 )
+            {
+                show = Boolean.valueOf( t ).booleanValue();
+            }
+        }
+
+        if ( tcnt < 2 )
+        {
+            p( "usage: getMatching key show values[true|false]" );
+        }
+        else
+        {
+            long n_start = System.currentTimeMillis();
+            try
+            {
+                Map<String, String> results = cache_control.getMatching( pattern );
+                if ( show && results != null )
+                {
+                    p( results.toString() );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( e );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---gotMatching [" + pattern + "] in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+    }
+
+    /**
+     * Test harness.
+     * @param args The command line arguments
+     */
+    public static void main( String[] args )
+    {
+        isSysOut = true;
+        String ccfFileName = args[0];
+        if ( ccfFileName != null )
+        {
+            JCS.setConfigFilename( ccfFileName );
+        }
+        TestCacheAccess tca = new TestCacheAccess( "testCache1" );
+        tca.runLoop();
+    }
+
+    // end main
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Gets multiple items from the cache with keys of the form key1, key2, key3 up to key[num].
+     * @param num int
+     */
+    public void getMultiple( int num )
+    {
+        getMultiple( num, false );
+    }
+
+    /**
+     * @param num
+     * @param show
+     */
+    public void getMultiple( int num, boolean show )
+    {
+        long n_start = System.currentTimeMillis();
+        for ( int n = 0; n < num; n++ )
+        {
+            try
+            {
+                Object obj = cache_control.get( "key" + n );
+                if ( show && obj != null )
+                {
+                    p( obj.toString() );
+                }
+            }
+            catch ( Exception e )
+            {
+                log.error( e );
+            }
+        }
+        long n_end = System.currentTimeMillis();
+        p( "---got " + num + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+    }
+
+    /**
+     * Puts multiple items into the cache.
+     * @param num int
+     */
+    public void putMultiple( int num )
+    {
+        try
+        {
+            long n_start = System.currentTimeMillis();
+            for ( int n = 0; n < num; n++ )
+            {
+                cache_control.put( "key" + n, "data" + n + " put from ta = junk" );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---put " + num + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+        }
+    }
+
+    /**
+     * Removes multiple items from the cache.
+     * @param num int
+     */
+    public void removeMultiple( int num )
+    {
+        try
+        {
+            long n_start = System.currentTimeMillis();
+            for ( int n = 0; n < num; n++ )
+            {
+                cache_control.remove( "key" + n );
+            }
+            long n_end = System.currentTimeMillis();
+            p( "---removed " + num + " in " + String.valueOf( n_end - n_start ) + " millis ---" );
+        }
+        catch ( Exception e )
+        {
+            log.error( e );
+        }
+    }
+
+    /**
+     * The random method performs numOps number of operations. The operations will be a mix of puts,
+     * gets, and removes. The key range will be from 0 to range.
+     * @param range int The end of the key range.
+     * @param numOps int The number of operations to perform
+     */
+    public void random( int range, int numOps )
+    {
+        random( range, numOps, false );
+    }
+
+    /**
+     * @param range
+     * @param numOps
+     * @param show
+     */
+    public void random( int range, int numOps, boolean show )
+    {
+        try
+        {
+            for ( int i = 1; i < numOps; i++ )
+            {
+                Random ran = new Random( i );
+                int n = ran.nextInt( 4 );
+                int kn = ran.nextInt( range );
+                String key = "key" + kn;
+                if ( n == 1 )
+                {
+                    cache_control.put( key, "data" + i + " junk asdfffffffadfasdfasf " + kn + ":" + n );
+                    if ( show )
+                    {
+                        p( "put " + key );
+                    }
+                }
+                else if ( n == 2 )
+                {
+                    cache_control.remove( key );
+                    if ( show )
+                    {
+                        p( "removed " + key );
+                    }
+                }
+                else
+                {
+                    // slightly greater chance of get
+                    Object obj = cache_control.get( key );
+                    if ( show && obj != null )
+                    {
+                        p( obj.toString() );
+                    }
+                }
+
+                if ( i % 10000 == 0 )
+                {
+                    p( cache_control.getStats() );
+                }
+
+            }
+            p( "Finished random cycle of " + numOps );
+        }
+        catch ( Exception e )
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+        }
+    }
+
+    /**
+     * Sets the region to be used by test methods.
+     * @param name String -- Name of region
+     */
+    public void setRegion( String name )
+    {
+        try
+        {
+            cache_control = JCS.getInstance( name );
+        }
+        catch ( Exception e )
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+        }
+
+    }
+
+    /////////////////////////////////////////////////////////////////////////////
+    /**
+     * The tester will print to the console if isSysOut is true, else it will log. It is false by
+     * default. When run via the main method, isSysOut will be set to true
+     * @param s String to print or log
+     */
+    public static void p( String s )
+    {
+        if ( isSysOut )
+        {
+            System.out.println( s );
+        }
+        else
+        {
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( s );
+            }
+        }
+    }
+
+    /**
+     * Displays usage information for command line testing.
+     */
+    public static void help()
+    {
+        p( "\n\n\n\n" );
+        p( "type 'shutDown' to shutdown the cache" );
+        p( "type 'getm num show[false|true]' to get num automatically from a region" );
+        p( "type 'putm num' to put num automatically to a region" );
+        p( "type 'removeall' to remove all items in a region" );
+        p( "type 'remove key' to remove" );
+        p( "type 'removem num' to remove a number automatically" );
+        p( "type 'getMatching pattern show' to getMatching" );
+        p( "type 'get key show' to get" );
+        p( "type 'getg key group show' to get" );
+        p( "type 'getag num group show' to get automatically from a group" );
+        p( "type 'getAttributeNames group' to get a list og the group elements" );
+        p( "type 'putg key group val' to put" );
+        p( "type 'putag num group' to put automatically from a group" );
+        p( "type 'put key val' to put" );
+        p( "type 'stats' to get stats" );
+        p( "type 'deattr' to get the default element attributes" );
+        p( "type 'cloneattr num' to clone attr" );
+        p( "type 'random range numOps' to put, get, and remove randomly" );
+        p( "type 'switch name' to switch to this region name" );
+        p( "type 'gc' to call System.gc()" );
+        p( "type 'help' for commands" );
+
+    }
+
+    /**
+     * Gets the attributeNames attribute of the TestCacheAccess class
+     * @param groupName
+     */
+    public void getAttributeNames( String groupName )
+    {
+        Iterator<String> iter = group_cache_control.getGroupKeys( groupName ).iterator();
+
+        while ( iter.hasNext() )
+        {
+            p( "=" + iter.next() );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/AdminBeanUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/AdminBeanUnitTest.java
new file mode 100644
index 0000000..7c84a23
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/AdminBeanUnitTest.java
@@ -0,0 +1,154 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Test the admin bean that is used by the JCSAdmin.jsp
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class AdminBeanUnitTest
+    extends TestCase
+{
+
+    /**
+     * Create a test region and then verify that we get it from the list.
+     *
+     * @throws Exception
+     *
+     */
+    public void testGetRegionInfo()
+        throws Exception
+    {
+        String regionName = "myRegion";
+        CacheAccess<String, String> cache = JCS.getInstance( regionName );
+
+        cache.put( "key", "value" );
+
+        JCSAdminBean admin = new JCSAdminBean();
+
+        CacheRegionInfo[] regions = admin.buildCacheInfo();
+
+        boolean foundRegion = false;
+
+        for (CacheRegionInfo info : regions)
+        {
+
+            if ( info.getCacheName().equals( regionName ) )
+            {
+                foundRegion = true;
+
+                assertTrue( "Byte count should be greater than 5.", info.getByteCount() > 5 );
+
+                assertNotNull( "Should have stats.", info.getCacheStatistics() );
+            }
+        }
+
+        assertTrue( "Should have found the region we just created.", foundRegion );
+    }
+
+    /**
+     * Put a value in a region and verify that it shows up.
+     *
+     * @throws Exception
+     */
+    public void testGetElementForRegionInfo()
+        throws Exception
+    {
+        String regionName = "myRegion";
+        CacheAccess<String, String> cache = JCS.getInstance( regionName );
+
+        // clear the region
+        cache.clear();
+
+        String key = "myKey";
+        cache.put( key, "value" );
+
+        JCSAdminBean admin = new JCSAdminBean();
+
+        CacheElementInfo[] elements = admin.buildElementInfo( regionName );
+
+        assertEquals( "Wrong number of elements in the region.", 1, elements.length );
+
+        CacheElementInfo elementInfo = elements[0];
+        assertEquals( "Wrong key." + elementInfo, key, elementInfo.getKey() );
+    }
+
+    /**
+     * Remove an item via the remove method.
+     *
+     * @throws Exception
+     */
+    public void testRemove()
+        throws Exception
+    {
+        JCSAdminBean admin = new JCSAdminBean();
+
+        String regionName = "myRegion";
+        CacheAccess<String, String> cache = JCS.getInstance( regionName );
+
+        // clear the region
+        cache.clear();
+        admin.clearRegion( regionName );
+
+        String key = "myKey";
+        cache.put( key, "value" );
+
+        CacheElementInfo[] elements = admin.buildElementInfo( regionName );
+
+        assertEquals( "Wrong number of elements in the region.", 1, elements.length );
+
+        CacheElementInfo elementInfo = elements[0];
+
+        assertEquals( "Wrong key.", key, elementInfo.getKey() );
+
+        admin.removeItem( regionName, key );
+
+        CacheElementInfo[] elements2 = admin.buildElementInfo( regionName );
+        assertEquals( "Wrong number of elements in the region after remove.", 0, elements2.length );
+    }
+
+    /**
+     * Add an item to a region. Call clear all and verify that it doesn't exist.
+     *
+     * @throws Exception
+     */
+    public void testClearAll()
+        throws Exception
+    {
+        JCSAdminBean admin = new JCSAdminBean();
+
+        String regionName = "myRegion";
+        CacheAccess<String, String> cache = JCS.getInstance( regionName );
+
+        String key = "myKey";
+        cache.put( key, "value" );
+
+        admin.clearAllRegions();
+
+        CacheElementInfo[] elements2 = admin.buildElementInfo( regionName );
+        assertEquals( "Wrong number of elements in the region after remove.", 0, elements2.length );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/CountingStreamUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/CountingStreamUnitTest.java
new file mode 100644
index 0000000..129575b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/CountingStreamUnitTest.java
@@ -0,0 +1,77 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the counting only output stream.
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class CountingStreamUnitTest
+    extends TestCase
+{
+
+    /**
+     * Write a single byte and verify the count.
+     *
+     * @throws Exception
+     */
+    public void testSingleByte() throws Exception
+    {
+        CountingOnlyOutputStream out = new CountingOnlyOutputStream();
+        out.write( 1 );
+        assertEquals( "Wrong number of bytes written.", 1, out.getCount() );
+        out.write( 1 );
+        assertEquals( "Wrong number of bytes written.", 2, out.getCount() );
+        out.close();
+    }
+
+    /**
+     * This should count the size of the array.
+     *
+     * @throws Exception
+     */
+    public void testByteArray() throws Exception
+    {
+        CountingOnlyOutputStream out = new CountingOnlyOutputStream();
+        byte[] array = new byte[]{1,2,3,4,5};
+        out.write( array );
+        assertEquals( "Wrong number of bytes written.", array.length, out.getCount() );
+        out.close();
+    }
+
+    /**
+     * This should count the len -- the third arg
+     *
+     * @throws Exception
+     */
+    public void testByteArrayLenCount() throws Exception
+    {
+        CountingOnlyOutputStream out = new CountingOnlyOutputStream();
+        byte[] array = new byte[]{1,2,3,4,5};
+        int len = 3;
+        out.write( array, 0, len );
+        assertEquals( "Wrong number of bytes written.", len, out.getCount() );
+        out.close();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/TestJMX.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/TestJMX.java
new file mode 100644
index 0000000..5a7b3ba
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/admin/TestJMX.java
@@ -0,0 +1,38 @@
+package org.apache.commons.jcs.admin;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Helper class to test the JMX registration
+ */
+public class TestJMX
+{
+	public static void main(String[] args) throws Exception
+	{
+		CacheAccess<String, String> cache = JCS.getInstance("test");
+
+		cache.put("key", "value");
+        System.out.println("Waiting...");
+        Thread.sleep(Long.MAX_VALUE);
+	}
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfiguratorUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfiguratorUnitTest.java
new file mode 100644
index 0000000..685e3e6
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/AuxiliaryCacheConfiguratorUnitTest.java
@@ -0,0 +1,129 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.engine.logging.MockCacheEventLogger;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.util.Properties;
+
+/** Unit tests for the auxiliary cache configurator. */
+public class AuxiliaryCacheConfiguratorUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we don't get an error.
+     */
+    public void testParseCacheEventLogger_Null()
+    {
+        // SETUP
+        Properties props = new Properties();
+
+        // DO WORK
+        MockCacheEventLogger result = (MockCacheEventLogger) AuxiliaryCacheConfigurator.parseCacheEventLogger( props,
+                                                                                                               "junk" );
+
+        // VERIFY
+        assertNull( "Should not have a logger.", result );
+    }
+
+    /**
+     * Verify that we don't get an error.
+     */
+    public void testParseCacheEventLogger_NullName()
+    {
+        // SETUP
+        Properties props = new Properties();
+
+        // DO WORK
+        MockCacheEventLogger result = (MockCacheEventLogger) AuxiliaryCacheConfigurator.parseCacheEventLogger( props,
+                                                                                                               null );
+
+        // VERIFY
+        assertNull( "Should not have a logger.", result );
+    }
+
+    /**
+     * Verify that we can parse the event logger.
+     */
+    public void testParseCacheEventLogger_Normal()
+    {
+        // SETUP
+        String auxPrefix = "jcs.auxiliary." + "MYAux";
+        String testPropertyValue = "This is the value";
+        String className = MockCacheEventLogger.class.getName();
+
+        Properties props = new Properties();
+        props.put( auxPrefix + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX, className );
+        props.put( auxPrefix + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX
+            + AuxiliaryCacheConfigurator.ATTRIBUTE_PREFIX + ".testProperty", testPropertyValue );
+
+        // DO WORK
+        MockCacheEventLogger result = (MockCacheEventLogger) AuxiliaryCacheConfigurator
+            .parseCacheEventLogger( props, auxPrefix );
+
+        // VERIFY
+        assertNotNull( "Should have a logger.", result );
+        assertEquals( "Property should be set.", testPropertyValue, result.getTestProperty() );
+    }
+
+    /**
+     * Verify that we can parse the ElementSerializer.
+     */
+    public void testParseElementSerializer_Normal()
+    {
+        // SETUP
+        String auxPrefix = "jcs.auxiliary." + "MYAux";
+        String testPropertyValue = "This is the value";
+        String className = MockElementSerializer.class.getName();
+
+        Properties props = new Properties();
+        props.put( auxPrefix + AuxiliaryCacheConfigurator.SERIALIZER_PREFIX, className );
+        props.put( auxPrefix + AuxiliaryCacheConfigurator.SERIALIZER_PREFIX
+            + AuxiliaryCacheConfigurator.ATTRIBUTE_PREFIX + ".testProperty", testPropertyValue );
+
+        // DO WORK
+        MockElementSerializer result = (MockElementSerializer) AuxiliaryCacheConfigurator
+            .parseElementSerializer( props, auxPrefix );
+
+        // VERIFY
+        assertNotNull( "Should have a Serializer.", result );
+        assertEquals( "Property should be set.", testPropertyValue, result.getTestProperty() );
+    }
+
+    /**
+     * Verify that we can parse the ElementSerializer.
+     */
+    public void testParseElementSerializer_Null()
+    {
+        // SETUP
+        Properties props = new Properties();
+
+        // DO WORK
+        IElementSerializer result = AuxiliaryCacheConfigurator
+            .parseElementSerializer( props, "junk" );
+
+        // VERIFY
+        assertTrue( "Should have the default Serializer.", result instanceof StandardSerializer );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCache.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCache.java
new file mode 100644
index 0000000..0fa7ecd
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCache.java
@@ -0,0 +1,218 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Mock auxiliary for unit tests.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MockAuxiliaryCache<K, V>
+    extends AbstractAuxiliaryCache<K, V>
+{
+    /** Don't change */
+    private static final long serialVersionUID = 1L;
+
+    /** Can setup the cache type */
+    public CacheType cacheType = CacheType.DISK_CACHE;
+
+    /** Can setup status */
+    public CacheStatus status = CacheStatus.ALIVE;
+
+    /** Times getMatching was Called */
+    public int getMatchingCallCount = 0;
+
+    /**
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /**
+     * @param pattern
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching(String pattern)
+        throws IOException
+    {
+        getMatchingCallCount++;
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<String, String> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+    {
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * @param key
+     * @return boolean
+     * @throws IOException
+     */
+    @Override
+    public boolean remove( K key )
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * @return int
+     */
+    @Override
+    public int getSize()
+    {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * @return int
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return status;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public String getCacheName()
+    {
+        return null;
+    }
+
+    /**
+     * Return the keys in this cache.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getKeySet()
+     */
+    @Override
+    public Set<K> getKeySet() throws IOException
+    {
+        return null;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        return null;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public String getStats()
+    {
+        return null;
+    }
+
+    /**
+     * @return cacheType
+     */
+    @Override
+    public CacheType getCacheType()
+    {
+        return cacheType;
+    }
+
+    /**
+     * @return Returns the AuxiliaryCacheAttributes.
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return null;
+    }
+
+    /** @return null */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return null;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheAttributes.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheAttributes.java
new file mode 100644
index 0000000..74b52ee
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheAttributes.java
@@ -0,0 +1,40 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+/** For testing. */
+public class MockAuxiliaryCacheAttributes
+    extends AbstractAuxiliaryCacheAttributes
+{
+    /** Don't change. */
+    private static final long serialVersionUID = 1091238902450504108L;
+
+    /**
+     * Doesn't really copy
+     * <p>
+     * @return this
+     */
+    @Override
+    public AuxiliaryCacheAttributes copy()
+    {
+        return this;
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheFactory.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheFactory.java
new file mode 100644
index 0000000..2979950
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockAuxiliaryCacheFactory.java
@@ -0,0 +1,70 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** For testing */
+public class MockAuxiliaryCacheFactory
+    implements AuxiliaryCacheFactory
+{
+    /** the name of the aux */
+    public String name = "MockAuxiliaryCacheFactory";
+
+    /**
+     * Creates a mock aux.
+     * <p>
+     * @param attr
+     * @param cacheMgr
+     * @param cacheEventLogger
+     * @param elementSerializer
+     * @return AuxiliaryCache
+     */
+    @Override
+    public <K, V> AuxiliaryCache<K, V>
+        createCache( AuxiliaryCacheAttributes attr, ICompositeCacheManager cacheMgr,
+           ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
+    {
+        MockAuxiliaryCache<K, V> auxCache = new MockAuxiliaryCache<K, V>();
+        auxCache.setCacheEventLogger( cacheEventLogger );
+        auxCache.setElementSerializer( elementSerializer );
+        return auxCache;
+    }
+
+    /**
+     * @return String
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * @param s
+     */
+    @Override
+    public void setName( String s )
+    {
+        this.name = s;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockCacheEventLogger.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockCacheEventLogger.java
new file mode 100644
index 0000000..562e33a
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/MockCacheEventLogger.java
@@ -0,0 +1,98 @@
+package org.apache.commons.jcs.auxiliary;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.logging.CacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * For testing auxiliary event logging. Improve later so we can test the details. This is very
+ * crude.
+ */
+public class MockCacheEventLogger
+    implements ICacheEventLogger
+{
+    /** times called */
+    public int applicationEventCalls = 0;
+
+    /** times called */
+    public int startICacheEventCalls = 0;
+
+    /** times called */
+    public int endICacheEventCalls = 0;
+
+    /** times called */
+    public int errorEventCalls = 0;
+
+    /** list of messages */
+    public List<String> errorMessages = new ArrayList<String>();
+
+    /**
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    @Override
+    public void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        applicationEventCalls++;
+    }
+
+    /**
+     * @param event
+     */
+    @Override
+    public <T> void logICacheEvent( ICacheEvent<T> event )
+    {
+        endICacheEventCalls++;
+    }
+
+    /**
+     * @param source
+     * @param eventName
+     * @param errorMessage
+     */
+    @Override
+    public void logError( String source, String eventName, String errorMessage )
+    {
+        errorEventCalls++;
+        errorMessages.add( errorMessage );
+    }
+
+    /**
+     * @param source
+     * @param region
+     * @param eventName
+     * @param optionalDetails
+     * @param key
+     * @return ICacheEvent
+     */
+    @Override
+    public <T> ICacheEvent<T> createICacheEvent( String source, String region,
+            String eventName, String optionalDetails, T key )
+    {
+        startICacheEventCalls++;
+        return new CacheEvent<T>();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheUnitTest.java
new file mode 100644
index 0000000..f23116b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/AbstractDiskCacheUnitTest.java
@@ -0,0 +1,304 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.TestLogConfigurationUtil;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.behavior.IDiskCacheAttributes;
+import org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/** Tests for the abstract disk cache. It's largely tested by actual instances. */
+public class AbstractDiskCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that update and get work.
+     * <p>
+     * @throws IOException
+     */
+    public void testUpdateGet_allowed()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testUpdateGet_allowed";
+        IDiskCacheAttributes diskCacheAttributes = new IndexedDiskCacheAttributes();
+        diskCacheAttributes.setCacheName( cacheName );
+
+        AbstractDiskCacheTestInstance<String, String> diskCache = new AbstractDiskCacheTestInstance<String, String>( diskCacheAttributes );
+
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+
+        diskCache.update( cacheElement );
+
+        // DO WORK
+        ICacheElement<String, String> result = diskCache.get( key );
+
+        // VERIFY
+        //System.out.println( diskCache.getStats() );
+        assertNotNull( "Item should be in the map.", result );
+    }
+
+    /**
+     * Verify that alive is set to false..
+     * <p>
+     * @throws IOException
+     */
+    public void testDispose()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testDispose";
+        IDiskCacheAttributes diskCacheAttributes = new IndexedDiskCacheAttributes();
+        diskCacheAttributes.setCacheName( cacheName );
+
+        AbstractDiskCacheTestInstance<String, String> diskCache = new AbstractDiskCacheTestInstance<String, String>( diskCacheAttributes );
+
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+
+        diskCache.update( cacheElement );
+
+        // DO WORK
+        diskCache.dispose();
+
+        // VERIFY
+        assertFalse( "disk cache should not be alive.", diskCache.alive );
+        assertEquals( "Status should be disposed", CacheStatus.DISPOSED, diskCache.getStatus() );
+    }
+
+    /**
+     * Verify that removeAll is prohibited.
+     * <p>
+     * @throws IOException
+     */
+    public void testRemoveAll_notAllowed()
+        throws IOException
+    {
+        // SETUP
+        StringWriter stringWriter = new StringWriter();
+        TestLogConfigurationUtil.configureLogger( stringWriter, AbstractDiskCache.class.getName() );
+
+        IDiskCacheAttributes diskCacheAttributes = new IndexedDiskCacheAttributes();
+        diskCacheAttributes.setAllowRemoveAll( false );
+
+        AbstractDiskCacheTestInstance<String, String> diskCache = new AbstractDiskCacheTestInstance<String, String>( diskCacheAttributes );
+
+        String cacheName = "testRemoveAll_notAllowed";
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+
+        diskCache.update( cacheElement );
+
+        // DO WORK
+        diskCache.removeAll();
+        String result = stringWriter.toString();
+
+        // VERIFY
+        assertTrue( "Should say not allowed.", result.indexOf( "set to false" ) != -1 );
+        assertNotNull( "Item should be in the map.", diskCache.get( key ) );
+    }
+
+    /**
+     * Verify that removeAll is allowed.
+     * <p>
+     * @throws IOException
+     */
+    public void testRemoveAll_allowed()
+        throws IOException
+    {
+        // SETUP
+        IDiskCacheAttributes diskCacheAttributes = new IndexedDiskCacheAttributes();
+        diskCacheAttributes.setAllowRemoveAll( true );
+
+        AbstractDiskCacheTestInstance<String, String> diskCache = new AbstractDiskCacheTestInstance<String, String>( diskCacheAttributes );
+
+        String cacheName = "testRemoveAll_allowed";
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+
+        diskCache.update( cacheElement );
+
+        // DO WORK
+        diskCache.removeAll();
+
+        // VERIFY
+        assertNull( "Item should not be in the map.", diskCache.get( key ) );
+    }
+
+    /** Concrete, testable instance. */
+    protected static class AbstractDiskCacheTestInstance<K extends Serializable, V extends Serializable>
+        extends AbstractDiskCache<K, V>
+    {
+        /** Internal map */
+        protected Map<K, ICacheElement<K, V>> map = new HashMap<K, ICacheElement<K, V>>();
+
+        /** used by the abstract aux class */
+        protected IDiskCacheAttributes diskCacheAttributes;
+
+        /**
+         * Creates the disk cache.
+         * <p>
+         * @param attr
+         */
+        public AbstractDiskCacheTestInstance( IDiskCacheAttributes attr )
+        {
+            super( attr );
+            diskCacheAttributes = attr;
+            super.alive = true;
+        }
+
+        /** Nothing. */
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * The location on disk
+         * <p>
+         * @return "memory"
+         */
+        @Override
+        protected String getDiskLocation()
+        {
+            return "memory";
+        }
+
+        /**
+         * Return the keys in this cache.
+         * <p>
+         * @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#getKeySet()
+         */
+        @Override
+        public Set<K> getKeySet() throws IOException
+        {
+            return new HashSet<K>(map.keySet());
+        }
+
+        /**
+         * @return map.size()
+         */
+        @Override
+        public int getSize()
+        {
+            return map.size();
+        }
+
+        /**
+         * @throws IOException
+         */
+        @Override
+        protected void processDispose()
+            throws IOException
+        {
+            //System.out.println( "processDispose" );
+        }
+
+        /**
+         * @param key
+         * @return ICacheElement
+         * @throws IOException
+         */
+        @Override
+        protected ICacheElement<K, V> processGet( K key )
+            throws IOException
+        {
+            //System.out.println( "processGet: " + key );
+            return map.get( key );
+        }
+
+        /**
+         * @param pattern
+         * @return Collections.EMPTY_MAP
+         * @throws IOException
+         */
+        @Override
+        protected Map<K, ICacheElement<K, V>> processGetMatching( String pattern )
+            throws IOException
+        {
+            return Collections.emptyMap();
+        }
+
+        /**
+         * @param key
+         * @return false
+         * @throws IOException
+         */
+        @Override
+        protected boolean processRemove( K key )
+            throws IOException
+        {
+            return map.remove( key ) != null;
+        }
+
+        /**
+         * @throws IOException
+         */
+        @Override
+        protected void processRemoveAll()
+            throws IOException
+        {
+            //System.out.println( "processRemoveAll" );
+            map.clear();
+        }
+
+        /**
+         * @param cacheElement
+         * @throws IOException
+         */
+        @Override
+        protected void processUpdate( ICacheElement<K, V> cacheElement )
+            throws IOException
+        {
+            //System.out.println( "processUpdate: " + cacheElement );
+            map.put( cacheElement.getKey(), cacheElement );
+        }
+
+        /**
+         * @return null
+         */
+        @Override
+        public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+        {
+            return diskCacheAttributes;
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/DiskTestObject.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/DiskTestObject.java
new file mode 100644
index 0000000..9b0cdca
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/DiskTestObject.java
@@ -0,0 +1,52 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import java.io.Serializable;
+
+/**
+ * Resembles a cached image.
+ */
+public class DiskTestObject
+    implements Serializable
+{
+    /** don't change */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Key
+     */
+    public Integer id;
+
+    /**
+     * Byte size
+     */
+    public byte[] imageBytes;
+
+    /**
+     * @param id
+     * @param imageBytes
+     */
+    public DiskTestObject( Integer id, byte[] imageBytes )
+    {
+        this.id = id;
+        this.imageBytes = imageBytes;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCSUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCSUnitTest.java
new file mode 100644
index 0000000..67d8622
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/LRUMapJCSUnitTest.java
@@ -0,0 +1,76 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.TestLogConfigurationUtil;
+
+import java.io.StringWriter;
+
+/** Unit tests for the LRUMapJCS implementation. */
+public class LRUMapJCSUnitTest
+    extends TestCase
+{
+    /** Verify that we default to unlimited */
+    public void testDefault()
+    {
+        // SETUP
+
+        // DO WORK
+        LRUMapJCS<String, String> map = new LRUMapJCS<String, String>();
+
+        // VERIFY
+        assertEquals( "Should be unlimted", -1, map.getMaxObjects() );
+    }
+
+    /** Verify that we default to unlimited */
+    public void testLimited()
+    {
+        // SETUP
+        int expected = 100;
+
+        // DO WORK
+        LRUMapJCS<String, String> map = new LRUMapJCS<String, String>( expected );
+
+        // VERIFY
+        assertEquals( "Should be expected", expected, map.getMaxObjects() );
+    }
+
+    /** Verify that the log message. */
+    public void testProcessRemovedLRU()
+    {
+        // SETUP
+        StringWriter stringWriter = new StringWriter();
+        TestLogConfigurationUtil.configureLogger( stringWriter, LRUMapJCS.class.getName() );
+
+        LRUMapJCS<String, String> map = new LRUMapJCS<String, String>();
+
+        String key = "myKey";
+        String value = "myValue";
+
+        // DO WORK
+        map.processRemovedLRU( key, value );
+        String result = stringWriter.toString();
+
+        // VERIFY
+        assertTrue( "Debug log should contain the key,", result.indexOf( key ) != -1 );
+        assertTrue( "Debug log should contain the value,", result.indexOf( value ) != -1 );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElementUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElementUnitTest.java
new file mode 100644
index 0000000..55f41d8
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/PurgatoryElementUnitTest.java
@@ -0,0 +1,90 @@
+package org.apache.commons.jcs.auxiliary.disk;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+/** Simple unit tests for the Purgatory Element. */
+public class PurgatoryElementUnitTest
+    extends TestCase
+{
+    /** Verify basic data */
+    public void testSpoolable_normal()
+    {
+        // SETUP
+        String cacheName = "myCacheName";
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+        PurgatoryElement<String, String> purgatoryElement = new PurgatoryElement<String, String>( cacheElement );
+        purgatoryElement.setSpoolable( false );
+
+        // DO WORK
+        boolean result = purgatoryElement.isSpoolable();
+
+        // VERIFY
+        assertFalse( "Should not be spoolable.", result );
+    }
+
+    /** Verify basic data */
+    public void testElementAttributes_normal()
+    {
+        // SETUP
+        String cacheName = "myCacheName";
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value );
+        PurgatoryElement<String, String> purgatoryElement = new PurgatoryElement<String, String>( cacheElement );
+        purgatoryElement.setElementAttributes( elementAttributes );
+
+        // DO WORK
+        IElementAttributes result = cacheElement.getElementAttributes();
+
+        // VERIFY
+        assertEquals( "Should have set the attributes on the element", elementAttributes, result );
+    }
+
+    /** Verify basic data */
+    public void testToString_normal()
+    {
+        // SETUP
+        String cacheName = "myCacheName";
+        String key = "myKey";
+        String value = "myValue";
+        IElementAttributes elementAttributes = new ElementAttributes();
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( cacheName, key, value, elementAttributes );
+        PurgatoryElement<String, String> purgatoryElement = new PurgatoryElement<String, String>( cacheElement );
+
+        // DO WORK
+        String result = purgatoryElement.toString();
+
+        // VERIFY
+        assertTrue( "Should have the cacheName.", result.indexOf( cacheName ) != -1 );
+        assertTrue( "Should have the key.", result.indexOf( key ) != -1 );
+        assertTrue( "Should have the value.", result.indexOf( value ) != -1 );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheConcurrentUnitTest.java
new file mode 100644
index 0000000..05969cf
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheConcurrentUnitTest.java
@@ -0,0 +1,258 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the block disk cache. This one uses three different
+ * regions for three threads.
+ */
+public class BlockDiskCacheConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory
+     * cache regions.
+     */
+    private static int items = 200;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     * @throws Exception
+     */
+    public BlockDiskCacheConcurrentUnitTest( String testName )
+        throws Exception
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { BlockDiskCacheConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     * @throws Exception
+     */
+    public static Test suite()
+        throws Exception
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        JCS.setConfigFilename( "/TestBlockDiskCache.ccf" );
+        JCS.getInstance( "indexedRegion1" ).clear();
+        JCS.getInstance( "indexedRegion2" ).clear();
+        JCS.getInstance( "indexedRegion3" ).clear();
+
+        suite.addTest( new BlockDiskCacheConcurrentUnitTest( "testBlockDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion1" );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheConcurrentUnitTest( "testBlockDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion2" );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheConcurrentUnitTest( "testBlockDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion3" );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheConcurrentUnitTest( "testBlockDiskCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegionInRange( "indexedRegion3", 300, 600 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestBlockDiskCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+        // another thread may have inserted since
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key" + "\n stats " + jcs.getStats(), jcs
+                .get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     * @param start
+     * @param end
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegionInRange( String region, int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+        for ( int i = start; i <= end; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = start; i <= end; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = start; i <= end; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        // Verify removal
+        // another thread may have inserted since
+        for ( int i = start; i <= end; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key " + "\n stats " + jcs.getStats(), jcs.get( i
+                + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
new file mode 100644
index 0000000..49f3582
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
@@ -0,0 +1,129 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the keyStore.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskCacheKeyStoreUnitTest
+    extends TestCase
+{
+    /** Directory name */
+    private final String rootDirName = "target/test-sandbox/block";
+
+    /**
+     * Put a bunch of keys inthe key store and verify that they are present.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutKeys()
+        throws Exception
+    {
+        // SETUP
+        String regionName = "testPutKeys";
+        int maxKeys = 1000;
+        int bytesPerBlock = 2000;
+
+        BlockDiskCacheAttributes attributes = new BlockDiskCacheAttributes();
+        attributes.setCacheName( regionName );
+        attributes.setDiskPath( rootDirName );
+        attributes.setMaxKeySize( maxKeys );
+        attributes.setBlockSizeBytes( bytesPerBlock );
+
+        BlockDiskCache<String, String> blockDiskCache = new BlockDiskCache<String, String>( attributes );
+
+        BlockDiskKeyStore<String> keyStore = new BlockDiskKeyStore<String>( attributes, blockDiskCache );
+
+        // DO WORK
+        int numElements = 100;
+        for ( int i = 0; i < numElements; i++ )
+        {
+            keyStore.put( String.valueOf( i ), new int[i] );
+        }
+//        System.out.println( "testPutKeys " + keyStore );
+
+        // VERIFY
+        assertEquals( "Wrong number of keys", numElements, keyStore.size() );
+        for ( int i = 0; i < numElements; i++ )
+        {
+            int[] result = keyStore.get( String.valueOf( i ) );
+            assertEquals( "Wrong array returned.", i, result.length );
+        }
+    }
+
+    /**
+     * Verify that we can load keys that we saved. Add a bunch. Save them. Clear the memory keyhash.
+     * Load the keys. Verify.
+     * <p>
+     * @throws Exception
+     */
+    public void testSaveLoadKeys()
+        throws Exception
+    {
+        // SETUP
+        String regionName = "testSaveLoadKeys";
+        int maxKeys = 10000;
+        int bytesPerBlock = 2000;
+
+        BlockDiskCacheAttributes attributes = new BlockDiskCacheAttributes();
+        attributes.setCacheName( regionName );
+        attributes.setDiskPath( rootDirName );
+        attributes.setMaxKeySize( maxKeys );
+        attributes.setBlockSizeBytes( bytesPerBlock );
+
+        BlockDiskKeyStore<String> keyStore = new BlockDiskKeyStore<String>( attributes, null );
+
+        // DO WORK
+        int numElements = 1000;
+        //Random random = new Random( 89 );
+        for ( int i = 0; i < numElements; i++ )
+        {
+            int blocks = i;//random.nextInt( 10 );
+            keyStore.put( String.valueOf( i ), new int[blocks] );
+            keyStore.put( String.valueOf( i ), new int[i] );
+        }
+//        System.out.println( "testSaveLoadKeys " + keyStore );
+
+        // VERIFY
+        assertEquals( "Wrong number of keys", numElements, keyStore.size() );
+
+        // DO WORK
+        keyStore.saveKeys();
+        keyStore.clearMemoryMap();
+
+        // VERIFY
+        assertEquals( "Wrong number of keys after clearing memory", 0, keyStore.size() );
+
+        // DO WORK
+        keyStore.loadKeys();
+
+        // VERIFY
+        assertEquals( "Wrong number of keys after loading", numElements, keyStore.size() );
+        for ( int i = 0; i < numElements; i++ )
+        {
+            int[] result = keyStore.get( String.valueOf( i ) );
+            assertEquals( "Wrong array returned.", i, result.length );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManagerUnitTest.java
new file mode 100644
index 0000000..a22f2da
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheManagerUnitTest.java
@@ -0,0 +1,55 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** Unit tests for the manager */
+public class BlockDiskCacheManagerUnitTest
+    extends TestCase
+{
+    /** Verify that the disk cache has the event logger */
+    public void testGetCache_normal()
+    {
+        // SETUP
+        String cacheName = "testGetCache_normal";
+        BlockDiskCacheAttributes defaultCacheAttributes = new BlockDiskCacheAttributes();
+        defaultCacheAttributes.setDiskPath( "target/BlockDiskCacheManagerUnitTest" );
+
+        ICacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        IElementSerializer elementSerializer = new MockElementSerializer();
+
+        BlockDiskCacheManager manager = BlockDiskCacheManager.getInstance( defaultCacheAttributes, cacheEventLogger,
+                                                                           elementSerializer );
+
+        // DO WORK
+        AuxiliaryCache<String, String> auxcache = manager.getCache(cacheName);
+        BlockDiskCache<String, String> cache = (BlockDiskCache<String, String>) auxcache;
+
+        // VERIFY
+        assertEquals( "wrong cacheEventLogger", cacheEventLogger, cache.getCacheEventLogger() );
+        assertEquals( "wrong elementSerializer", elementSerializer, cache.getElementSerializer() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheRandomConcurrentTestUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheRandomConcurrentTestUtil.java
new file mode 100644
index 0000000..266ac28
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheRandomConcurrentTestUtil.java
@@ -0,0 +1,83 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.TestCacheAccess;
+
+/**
+ * This is used by other tests to generate a random load on the disk cache.
+ */
+public class BlockDiskCacheRandomConcurrentTestUtil
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public BlockDiskCacheRandomConcurrentTestUtil( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Randomly adds items to cache, gets them, and removes them. The range
+     * count is more than the size of the memory cache, so items should spool to
+     * disk.
+     * <p>
+     * @param region
+     *            Name of the region to access
+     * @param range
+     * @param numOps
+     * @param testNum
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region, int range, int numOps, int testNum )
+        throws Exception
+    {
+        // run a rondom operation test to detect deadlocks
+        TestCacheAccess tca = new TestCacheAccess( "/TestBlockDiskCacheCon.ccf" );
+        tca.setRegion( region );
+        tca.random( range, numOps );
+
+        // make sure a simple put then get works
+        // this may fail if the other tests are flooding the disk cache
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        String key = "testKey" + testNum;
+        String data = "testData" + testNum;
+        jcs.put( key, data );
+        String value = jcs.get( key );
+        assertEquals( data, value );
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestBlockDiskCacheCon.ccf" );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSameRegionConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSameRegionConcurrentUnitTest.java
new file mode 100644
index 0000000..ffca43e
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSameRegionConcurrentUnitTest.java
@@ -0,0 +1,172 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the block disk cache. Runs three threads against the same region.
+ */
+public class BlockDiskCacheSameRegionConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     * <p>
+     * @param testName
+     */
+    public BlockDiskCacheSameRegionConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * <p>
+     * @param args
+     * @throws InterruptedException
+     */
+    public static void main( String args[] ) throws InterruptedException
+    {
+        String[] testCaseName = { BlockDiskCacheSameRegionConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+
+        // Give test threads some time to finish
+        Thread.sleep(2000);
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new BlockDiskCacheSameRegionConcurrentUnitTest( "testBlockDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "blockRegion4", 0, 200 );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheSameRegionConcurrentUnitTest( "testBlockDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "blockRegion4", 1000, 1200 );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheSameRegionConcurrentUnitTest( "testBlockDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "blockRegion4", 2000, 2200 );
+            }
+        } );
+
+        suite.addTest( new BlockDiskCacheSameRegionConcurrentUnitTest( "testBlockDiskCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "blockRegion4", 2200, 5200 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup.  Sets the config name and clears the region.
+     * <p>
+     * @throws Exception
+     */
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestBlockDiskCacheCon.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * @param region Name of the region to access
+     * @param start
+     * @param end
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region, int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i + "-" + region );
+        }
+
+        // Test that all items are in cache
+
+        for ( int i = start; i <= end; i++ )
+        {
+            String key = i + ":key";
+            String value = jcs.get( key );
+
+            assertEquals( "Wrong value for key [" + key + "]", region + " data " + i + "-" + region, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = start; i <= end; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = start; i <= end; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i + "-" + region, element.getVal() );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSteadyLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSteadyLoadTest.java
new file mode 100644
index 0000000..a821f75
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheSteadyLoadTest.java
@@ -0,0 +1,161 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.auxiliary.disk.DiskTestObject;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+
+/**
+ * This allows you to put thousands of large objects into the disk cache and to force removes to
+ * trigger optimizations along the way.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskCacheSteadyLoadTest
+    extends TestCase
+{
+    /** String for separating log entries. */
+    private static final String LOG_DIVIDER = "---------------------------";
+
+    /** the runtime. */
+    private static Runtime rt = Runtime.getRuntime();
+
+    /** The decimal format to use int he logs. */
+    private static DecimalFormat format = new DecimalFormat( "#,###" );
+
+    /**
+     * Insert 2000 wait 1 second, repeat. Average 1000 / sec.
+     * <p>
+     * @throws Exception
+     */
+    public void testRunSteadyLoadTest()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestBlockDiskCacheSteadyLoad.ccf" );
+
+        logMemoryUsage();
+
+        int numPerRun = 250;
+        long pauseBetweenRuns = 1000;
+        int runCount = 0;
+        int runs = 1000;
+        int upperKB = 50;
+
+        CacheAccess<String, DiskTestObject> jcs = JCS.getInstance( ( numPerRun / 2 ) + "aSecond" );
+
+        ElapsedTimer timer = new ElapsedTimer();
+        int numToGet = numPerRun * ( runs / 10 );
+        for ( int i = 0; i < numToGet; i++ )
+        {
+            jcs.get( String.valueOf( i ) );
+        }
+//        System.out.println( LOG_DIVIDER );
+//        System.out.println( "After getting " + numToGet );
+//        System.out.println( "Elapsed " + timer.getElapsedTimeString() );
+        logMemoryUsage();
+
+        jcs.clear();
+        Thread.sleep( 3000 );
+//        System.out.println( LOG_DIVIDER );
+//        System.out.println( "Start putting" );
+
+        long totalSize = 0;
+        int totalPut = 0;
+
+        Random random = new Random( 89 );
+        while ( runCount < runs )
+        {
+            runCount++;
+            for ( int i = 0; i < numPerRun; i++ )
+            {
+                // 1/2 upper to upperKB-4 KB
+                int kiloBytes = Math.max( upperKB / 2, random.nextInt( upperKB ) );
+                int bytes = ( kiloBytes ) * 1024;
+                totalSize += bytes;
+                totalPut++;
+                DiskTestObject object = new DiskTestObject( Integer.valueOf( i ), new byte[bytes] );
+                jcs.put( String.valueOf( totalPut ), object );
+            }
+
+            // get half of those inserted the previous run
+            if ( runCount > 1 )
+            {
+                for ( int j = ( ( totalPut - numPerRun ) - ( numPerRun / 2 ) ); j < ( totalPut - numPerRun ); j++ )
+                {
+                    jcs.get( String.valueOf( j ) );
+                }
+            }
+
+            // remove half of those inserted the previous run
+            if ( runCount > 1 )
+            {
+                for ( int j = ( ( totalPut - numPerRun ) - ( numPerRun / 2 ) ); j < ( totalPut - numPerRun ); j++ )
+                {
+                    jcs.remove( String.valueOf( j ) );
+                }
+            }
+
+
+            Thread.sleep( pauseBetweenRuns );
+            if ( runCount % 100 == 0 )
+            {
+//                System.out.println( LOG_DIVIDER );
+//                System.out.println( "Elapsed " + timer.getElapsedTimeString() );
+//                System.out.println( "Run count: " + runCount + " Average size: " + ( totalSize / totalPut ) + "\n"
+//                    + jcs.getStats() );
+                logMemoryUsage();
+            }
+        }
+
+        Thread.sleep( 3000 );
+//        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+
+        Thread.sleep( 10000 );
+//        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+//        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+    }
+
+    /**
+     * Logs the memory usage.
+     */
+    private static void logMemoryUsage()
+    {
+        long byte2MB = 1024 * 1024;
+        long total = rt.totalMemory() / byte2MB;
+        long free = rt.freeMemory() / byte2MB;
+        long used = total - free;
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Memory:" + " Used:" + format.format( used ) + "MB" + " Free:" + format.format( free )
+            + "MB" + " Total:" + format.format( total ) + "MB" );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTest.java
new file mode 100644
index 0000000..a9f466b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTest.java
@@ -0,0 +1,340 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Map;
+
+/** Unit tests for the Block Disk Cache */
+public class BlockDiskCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Test the basic get matching.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGetMatching_SmallWait()
+        throws Exception
+    {
+        // SETUP
+        int items = 200;
+
+        String cacheName = "testPutGetMatching_SmallWait";
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, String> diskCache = new BlockDiskCache<String, String>( cattr );
+
+        // DO WORK
+        for ( int i = 0; i <= items; i++ )
+        {
+            diskCache.update( new CacheElement<String, String>( cacheName, i + ":key", cacheName + " data " + i ) );
+        }
+        Thread.sleep( 500 );
+
+        Map<String, ICacheElement<String, String>> matchingResults = diskCache.getMatching( "1.8.+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned", 10, matchingResults.size() );
+        //System.out.println( "matchingResults.keySet() " + matchingResults.keySet() );
+        //System.out.println( "\nAFTER TEST \n" + diskCache.getStats() );
+    }
+
+    /**
+     * Test the basic get matching. With no wait this will all come from purgatory.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGetMatching_NoWait()
+        throws Exception
+    {
+        // SETUP
+        int items = 200;
+
+        String cacheName = "testPutGetMatching_NoWait";
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, String> diskCache = new BlockDiskCache<String, String>( cattr );
+
+        // DO WORK
+        for ( int i = 0; i <= items; i++ )
+        {
+            diskCache.update( new CacheElement<String, String>( cacheName, i + ":key", cacheName + " data " + i ) );
+        }
+
+        Map<String, ICacheElement<String, String>> matchingResults = diskCache.getMatching( "1.8.+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned", 10, matchingResults.size() );
+        //System.out.println( "matchingResults.keySet() " + matchingResults.keySet() );
+        //System.out.println( "\nAFTER TEST \n" + diskCache.getStats() );
+    }
+
+    /**
+     * Verify that the block disk cache can handle a big string.
+     * <p>
+     * @throws Exception
+     */
+    public void testChunk_BigString()
+        throws Exception
+    {
+        String string = "This is my big string ABCDEFGH";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( "|" + i + ":" + sb.toString() ); // big string
+        }
+        string = sb.toString();
+
+        StandardSerializer elementSerializer = new StandardSerializer();
+        byte[] data = elementSerializer.serialize( string );
+
+        File file = new File( "target/test-sandbox/BlockDiskCacheUnitTest/testChunk_BigString.data" );
+
+        BlockDisk blockDisk = new BlockDisk( file, 200, elementSerializer );
+
+        int numBlocksNeeded = blockDisk.calculateTheNumberOfBlocksNeeded( data );
+//        System.out.println( numBlocksNeeded );
+
+        // get the individual sub arrays.
+        byte[][] chunks = blockDisk.getBlockChunks( data, numBlocksNeeded );
+
+        byte[] resultData = new byte[0];
+
+        for ( short i = 0; i < chunks.length; i++ )
+        {
+            byte[] chunk = chunks[i];
+            byte[] newTotal = new byte[data.length + chunk.length];
+            // copy data into the new array
+            System.arraycopy( data, 0, newTotal, 0, data.length );
+            // copy the chunk into the new array
+            System.arraycopy( chunk, 0, newTotal, data.length, chunk.length );
+            // swap the new and old.
+            resultData = newTotal;
+        }
+
+        Serializable result = elementSerializer.deSerialize( resultData, null );
+        // System.out.println( result );
+        assertEquals( "wrong string after retrieval", string, result );
+    }
+
+    /**
+     * Verify that the block disk cache can handle a big string.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGet_BigString()
+        throws Exception
+    {
+        String string = "This is my big string ABCDEFGH";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( " " + i + sb.toString() ); // big string
+        }
+        string = sb.toString();
+
+        String cacheName = "testPutGet_BigString";
+
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setBlockSizeBytes( 200 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, String> diskCache = new BlockDiskCache<String, String>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, String>( cacheName, "x", string ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, String> afterElement = diskCache.get( "x" );
+        assertNotNull( afterElement );
+        // System.out.println( "afterElement = " + afterElement );
+        String after = afterElement.getVal();
+
+        assertNotNull( after );
+        assertEquals( "wrong string after retrieval", string, after );
+    }
+
+    /**
+     * Verify that the block disk cache can handle utf encoded strings.
+     * <p>
+     * @throws Exception
+     */
+    public void testUTF8String()
+        throws Exception
+    {
+        String string = "IÒtÎrn‚tiÙn‡lizÊti¯n";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( sb.toString() ); // big string
+        }
+        string = sb.toString();
+
+//        System.out.println( "The string contains " + string.length() + " characters" );
+
+        String cacheName = "testUTF8String";
+
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setBlockSizeBytes( 200 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, String> diskCache = new BlockDiskCache<String, String>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, String>( cacheName, "x", string ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, String> afterElement = diskCache.get( "x" );
+        assertNotNull( afterElement );
+        // System.out.println( "afterElement = " + afterElement );
+        String after = afterElement.getVal();
+
+        assertNotNull( after );
+        assertEquals( "wrong string after retrieval", string, after );
+    }
+
+    /**
+     * Verify that the block disk cache can handle utf encoded strings.
+     * <p>
+     * @throws Exception
+     */
+    public void testUTF8ByteArray()
+        throws Exception
+    {
+        String string = "IÒtÎrn‚tiÙn‡lizÊti¯n";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( sb.toString() ); // big string
+        }
+        string = sb.toString();
+        //System.out.println( "The string contains " + string.length() + " characters" );
+        String UTF8 = "UTF-8";
+        byte[] bytes = string.getBytes( UTF8 );
+
+        String cacheName = "testUTF8ByteArray";
+
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setBlockSizeBytes( 200 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, byte[]> diskCache = new BlockDiskCache<String, byte[]>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, byte[]>( cacheName, "x", bytes ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, byte[]> afterElement = diskCache.get( "x" );
+        assertNotNull( afterElement );
+        //System.out.println( "afterElement = " + afterElement );
+        byte[] after = afterElement.getVal();
+
+        assertNotNull( after );
+        assertEquals( "wrong bytes after retrieval", bytes.length, after.length );
+        //assertEquals( "wrong bytes after retrieval", bytes, after );
+        //assertEquals( "wrong bytes after retrieval", string, new String( after, UTF8 ) );
+
+    }
+
+    /**
+     * Verify that the block disk cache can handle utf encoded strings.
+     * <p>
+     * @throws Exception
+     */
+    public void testUTF8StringAndBytes()
+        throws Exception
+    {
+        X before = new X();
+        String string = "IÒtÎrn‚tiÙn‡lizÊti¯n";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( sb.toString() ); // big string
+        }
+        string = sb.toString();
+        //System.out.println( "The string contains " + string.length() + " characters" );
+        String UTF8 = "UTF-8";
+        before.string = string;
+        before.bytes = string.getBytes( UTF8 );
+
+        String cacheName = "testUTF8StringAndBytes";
+
+        BlockDiskCacheAttributes cattr = new BlockDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setBlockSizeBytes( 500 );
+        cattr.setDiskPath( "target/test-sandbox/BlockDiskCacheUnitTest" );
+        BlockDiskCache<String, X> diskCache = new BlockDiskCache<String, X>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, X>( cacheName, "x", before ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, X> afterElement = diskCache.get( "x" );
+        // System.out.println( "afterElement = " + afterElement );
+        X after = ( afterElement.getVal() );
+
+        assertNotNull( after );
+        assertEquals( "wrong string after retrieval", string, after.string );
+        assertEquals( "wrong bytes after retrieval", string, new String( after.bytes, UTF8 ) );
+
+    }
+
+    /** Holder for a string and byte array. */
+    static class X
+        implements Serializable
+    {
+        /** ignore */
+        private static final long serialVersionUID = 1L;
+
+        /** Test string */
+        String string;
+
+        /*** test byte array. */
+        byte[] bytes;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
new file mode 100644
index 0000000..3cab3eb
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
@@ -0,0 +1,89 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Simple tests for the element descriptor
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskElementDescriptorUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the memory used per element is reasonable.
+     * <p>
+     * TODO figure out a more precise expectation.
+     * <p>
+     * @throws Exception
+     */
+    public void testMemorySize()
+        throws Exception
+    {
+        // SETUP
+        long memoryBefore = measureMemoryUse();
+//        System.out.println( "Before: " + memoryBefore );
+
+        int numElements = 25000;
+        @SuppressWarnings("unchecked")
+        BlockDiskElementDescriptor<Integer>[] elements = new BlockDiskElementDescriptor[numElements];
+
+        long memoryStart = measureMemoryUse();
+//        System.out.println( "Start: " + memoryStart );
+
+        // DO WORK
+        for ( int i = 0; i < numElements; i++ )
+        {
+            BlockDiskElementDescriptor<Integer> descriptor = new BlockDiskElementDescriptor<Integer>();
+            descriptor.setKey( Integer.valueOf( i ) );
+            descriptor.setBlocks( new int[] { 1, 2 } );
+            elements[i] = descriptor;
+        }
+
+        // VERIFY
+        long memoryEnd = measureMemoryUse();
+//        System.out.println( "End: " + memoryEnd );
+
+        long diff = memoryEnd - memoryStart;
+//        System.out.println( "diff: " + diff );
+
+        long perDiff = diff / numElements;
+//        System.out.println( "per diff: " + perDiff );
+
+        // about 20 bytes each
+        assertTrue( "Too much was used: " + perDiff + " >= 75", perDiff < 75 );
+    }
+
+    /**
+     * Measure memory used by the VM.
+     * @return long
+     * @throws InterruptedException
+     */
+    protected long measureMemoryUse()
+        throws InterruptedException
+    {
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
new file mode 100644
index 0000000..c568145
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
@@ -0,0 +1,363 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.io.File;
+import java.util.Random;
+
+/**
+ * Test for the disk access layer of the Block Disk Cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskUnitTest
+    extends TestCase
+{
+    /** data file. */
+    private File rafDir;
+
+    /**
+     * Creates the base directory
+     */
+    public BlockDiskUnitTest()
+    {
+        String rootDirName = "target/test-sandbox/block";
+        this.rafDir = new File( rootDirName );
+        this.rafDir.mkdirs();
+    }
+
+    /**
+     * Test writing a null object within a single block size.
+     * <p>
+     * @throws Exception
+     */
+    public void testWrite_NullBlockElement()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWrite_NullBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int[] blocks = disk.write( null );
+
+        // VERIFY
+        assertEquals( "Wrong number of blocks recorded.", 1, disk.getNumberOfBlocks() );
+        assertEquals( "Wrong number of blocks returned.", 1, blocks.length );
+        assertEquals( "Wrong block returned.", 0, blocks[0] );
+    }
+
+    /**
+     * Test writing an element within a single block size.
+     * <p>
+     * @throws Exception
+     */
+    public void testWrite_SingleBlockElement()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWrite_SingleBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int bytes = 1 * 1024;
+        int[] blocks = disk.write( new byte[bytes] );
+
+        // VERIFY
+        assertEquals( "Wrong number of blocks recorded.", 1, disk.getNumberOfBlocks() );
+        assertEquals( "Wrong number of blocks returned.", 1, blocks.length );
+        assertEquals( "Wrong block returned.", 0, blocks[0] );
+    }
+
+    /**
+     * Test writing and reading an element within a single block size.
+     * <p>
+     * @throws Exception
+     */
+    public void testWriteAndRead_SingleBlockElement()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteAndRead_SingleBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int bytes = 1 * 1024;
+        int[] blocks = disk.write( new byte[bytes] );
+
+        byte[] result = (byte[]) disk.read( blocks );
+
+        // VERIFY
+        assertEquals( "Wrong item retured.", new byte[bytes].length, result.length );
+    }
+
+    /**
+     * Test writing two elements that each fit within a single block size.
+     * <p>
+     * @throws Exception
+     */
+    public void testWrite_TwoSingleBlockElements()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWrite_TwoSingleBlockElements";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int bytes = 1 * 1024;
+        int[] blocks1 = disk.write( new byte[bytes] );
+        int[] blocks2 = disk.write( new byte[bytes] );
+
+        // VERIFY
+        assertEquals( "Wrong number of blocks recorded.", 2, disk.getNumberOfBlocks() );
+        assertEquals( "Wrong number of blocks returned.", 1, blocks1.length );
+        assertEquals( "Wrong block returned.", 0, blocks1[0] );
+        assertEquals( "Wrong number of blocks returned.", 1, blocks2.length );
+        assertEquals( "Wrong block returned.", 1, blocks2[0] );
+    }
+
+    /**
+     * Verify that it says we need two blocks if the total size will fit.
+     * <p>
+     * @throws Exception
+     */
+    public void testCalculateBlocksNeededDouble()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testCalculateBlocksNeededDouble";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int result = disk.calculateTheNumberOfBlocksNeeded( new byte[disk.getBlockSizeBytes() * 2
+            - ( 2 * BlockDisk.HEADER_SIZE_BYTES )] );
+
+        // Verify
+        assertEquals( "Wrong number of blocks", 2, result );
+    }
+
+    /**
+     * Test writing an element that takes two blocks.
+     * <p>
+     * @throws Exception
+     */
+    public void testWrite_DoubleBlockElement()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteDoubleBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        // byte arrays encur 27 bytes of serialization overhead.
+        int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), 2 );
+        int[] blocks = disk.write( new byte[bytes] );
+
+        // VERIFY
+        assertEquals( "Wrong number of blocks recorded.", 2, disk.getNumberOfBlocks() );
+        assertEquals( "Wrong number of blocks returned.", 2, blocks.length );
+        assertEquals( "Wrong block returned.", 0, blocks[0] );
+    }
+
+    /**
+     * Test writing an element that takes 128 blocks.  There was a byte in a for loop that limited the number to 127.  I fixed this.
+     * <p>
+     * @throws Exception
+     */
+    public void testWrite_128BlockElement()
+        throws Exception
+    {
+        // SETUP
+        int numBlocks = 128;
+
+        String fileName = "testWrite_128BlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        // byte arrays encur 27 bytes of serialization overhead.
+        int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocks );
+        int[] blocks = disk.write( new byte[bytes] );
+
+        // VERIFY
+        assertEquals( "Wrong number of blocks recorded.", numBlocks, disk.getNumberOfBlocks() );
+        assertEquals( "Wrong number of blocks returned.", numBlocks, blocks.length );
+        assertEquals( "Wrong block returned.", 0, blocks[0] );
+    }
+
+    /**
+     * Test writing and reading elements that do not fit within a single block.
+     * <p>
+     * @throws Exception
+     */
+    public void testWriteAndReadMultipleMultiBlockElement()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteAndReadSingleBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        BlockDisk disk = new BlockDisk( file, new StandardSerializer() );
+
+        // DO WORK
+        int numBlocksPerElement = 4;
+        int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocksPerElement );
+
+        int numElements = 100;
+        for ( int i = 0; i < numElements; i++ )
+        {
+            int[] blocks = disk.write( new byte[bytes] );
+            byte[] result = (byte[]) disk.read( blocks );
+
+            // VERIFY
+            assertEquals( "Wrong item retured.", new byte[bytes].length, result.length );
+            assertEquals( "Wrong number of blocks returned.", numBlocksPerElement, blocks.length );
+        }
+    }
+
+    /**
+     * Test writing and reading elements that do not fit within a single block.
+     * <p>
+     * @throws Exception
+     */
+    public void testWriteAndReadMultipleMultiBlockElement_setSize()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteAndReadSingleBlockElement";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        int blockSizeBytes = 1024;
+        BlockDisk disk = new BlockDisk( file, blockSizeBytes );
+
+        // DO WORK
+        int numBlocksPerElement = 4;
+        int bytes = getBytesForBlocksOfByteArrays( disk.getBlockSizeBytes(), numBlocksPerElement );
+
+        int numElements = 100;
+        Random r = new Random(System.currentTimeMillis());
+        final byte[] src = new byte[bytes];
+        for ( int i = 0; i < numElements; i++ )
+        {
+            r.nextBytes(src);  // Ensure we don't just write zeros out
+            int[] blocks = disk.write( src );
+            byte[] result = (byte[]) disk.read( blocks );
+
+            // VERIFY
+            assertEquals( "Wrong item length retured.", src.length, result.length );
+            assertEquals( "Wrong number of blocks returned.", numBlocksPerElement, blocks.length );
+
+            // We check the array contents, too, to ensure we read back what we wrote out
+            for (int j = 0 ; j < src.length ; j++) {
+                assertEquals( "Mismatch at offset " + j + " in attempt # " + (i + 1), src[j], result[j] );
+            }
+        }
+        assertEquals( "Wrong number of elements. "+disk, numBlocksPerElement * numElements, disk.getNumberOfBlocks() );
+    }
+
+    /**
+     * Used to get the size for byte arrays that will take up the number of blocks specified.
+     * <p>
+     * @param blockSize
+     * @param numBlocks
+     * @return num bytes.
+     */
+    private int getBytesForBlocksOfByteArrays( int blockSize, int numBlocks )
+    {
+        // byte arrays encur some bytes of serialization overhead.
+        return blockSize * numBlocks - ( numBlocks * BlockDisk.HEADER_SIZE_BYTES ) - ( numBlocks * 14 );
+    }
+
+    /**
+     * Verify that the block disk can handle a big string.
+     * <p>
+     * @throws Exception
+     */
+    public void testWriteAndRead_BigString()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteAndRead_BigString";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        int blockSizeBytes = 4096;//1024;
+        BlockDisk disk = new BlockDisk( file, blockSizeBytes, new StandardSerializer() );
+
+        String string = "This is my big string ABCDEFGH";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 8; i++ )
+        {
+            sb.append( " " + i + sb.toString() ); // big string
+        }
+        string = sb.toString();
+
+        // DO WORK
+        int[] blocks = disk.write( string );
+        String result = (String) disk.read( blocks );
+
+        // VERIFY
+//        System.out.println( string );
+//        System.out.println( result );
+//        System.out.println( disk );
+        assertEquals( "Wrong item retured.", string, result );
+    }
+
+    /**
+     * Verify that the block disk can handle a big string.
+     * <p>
+     * @throws Exception
+     */
+    public void testWriteAndRead_BigString2()
+        throws Exception
+    {
+        // SETUP
+        String fileName = "testWriteAndRead_BigString";
+        File file = new File( rafDir, fileName + ".data" );
+        file.delete();
+        int blockSizeBytes = 47;//4096;//1024;
+        BlockDisk disk = new BlockDisk( file, blockSizeBytes, new StandardSerializer() );
+
+        String string = "abcdefghijklmnopqrstuvwxyz1234567890";
+        string += string;
+        string += string;
+
+        // DO WORK
+        int[] blocks = disk.write( string );
+        String result = (String) disk.read( blocks );
+
+        // VERIFY
+        assertEquals( "Wrong item retured.", string, result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/HugeQuantityBlockDiskCacheLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/HugeQuantityBlockDiskCacheLoadTest.java
new file mode 100644
index 0000000..e96b0ac
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/HugeQuantityBlockDiskCacheLoadTest.java
@@ -0,0 +1,135 @@
+package org.apache.commons.jcs.auxiliary.disk.block;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+/**
+ * Put a few hundred thousand entries in the block disk cache. <p.
+ * @author Aaron Smuts
+ */
+public class HugeQuantityBlockDiskCacheLoadTest
+    extends TestCase
+{
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestBlockDiskCacheHuge.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @throws Exception If an error occurs
+     */
+    public void testLargeNumberOfItems()
+        throws Exception
+    {
+        int items = 300000;
+        String region = "testCache1";
+
+        System.out.println( "--------------------------" );
+        long initialMemory = measureMemoryUse();
+        System.out.println( "Before getting JCS: " + initialMemory );
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        jcs.clear();
+
+        try
+        {
+            ElapsedTimer timer = new ElapsedTimer();
+            System.out.println( "Start: " + measureMemoryUse() );
+
+            // Add items to cache
+            for ( int i = 0; i <= items; i++ )
+            {
+                jcs.put( i + ":key", region + " data " + i );
+            }
+
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            System.out.println( "After put: " + measureMemoryUse() );
+
+            Thread.sleep( 5000 );
+
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            System.out.println( "After wait: " + measureMemoryUse() );
+
+            for ( int i = 0; i < 10; i++ )
+            {
+                SleepUtil.sleepAtLeast( 3000 );
+                System.out.println( "--------------------------" );
+                System.out.println( "After sleep. " + timer.getElapsedTimeString() + " memory used = "
+                    + measureMemoryUse() );
+                System.out.println( jcs.getStats() );
+            }
+
+            // Test that all items are in cache
+            System.out.println( "--------------------------" );
+            System.out.println( "Retrieving all." );
+            for ( int i = 0; i <= items; i++ )
+            {
+                //System.out.print(  "\033[s" );
+                String value = jcs.get( i + ":key" );
+                if ( i % 1000 == 0 )
+                {
+                    //System.out.print(  "\033[r" );
+                    System.out.println( i + " " );
+                }
+                assertEquals( "Wrong value returned.", region + " data " + i, value );
+            }
+            long aftetGet = measureMemoryUse();
+            System.out.println( "After get: " + aftetGet + " diff = " + ( aftetGet - initialMemory ) );
+
+        }
+        finally
+        {
+            // dump the stats to the report
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            long endMemory = measureMemoryUse();
+            System.out.println( "End: " + endMemory + " diff = " + ( endMemory - initialMemory ) );
+        }
+    }
+
+    /**
+     * Measure memory used by the VM.
+     * @return long
+     * @throws InterruptedException
+     */
+    protected long measureMemoryUse()
+        throws InterruptedException
+    {
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/DiskTestObjectUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/DiskTestObjectUtil.java
new file mode 100644
index 0000000..eb65040
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/DiskTestObjectUtil.java
@@ -0,0 +1,144 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.disk.DiskTestObject;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Random;
+
+/**
+ * Utility for dealing with test objects.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class DiskTestObjectUtil
+{
+    /**
+     * Total from the start to the endPostion.
+     * <p>
+     * @param testObjects
+     * @param endPosition
+     * @return size
+     * @throws IOException
+     */
+    public static long totalSize( DiskTestObject[] testObjects, int endPosition )
+        throws IOException
+    {
+        StandardSerializer serializer = new StandardSerializer();
+        long total = 0;
+        for ( int i = 0; i < endPosition; i++ )
+        {
+            int tileSize = serializer.serialize( testObjects[i] ).length + IndexedDisk.HEADER_SIZE_BYTES;
+            total += tileSize;
+        }
+        return total;
+    }
+
+    /**
+     * Total from the start to the endPostion.
+     * <p>
+     * @param elements
+     * @param endPosition
+     * @return size
+     * @throws IOException
+     */
+    public static <K extends Serializable, V extends Serializable> long totalSize( ICacheElement<K, V>[] elements, int endPosition )
+        throws IOException
+    {
+        return totalSize( elements, 0, endPosition );
+    }
+
+    /**
+     * Total from the start to the endPostion.
+     * <p>
+     * @param elements
+     * @param startPosition
+     * @param endPosition
+     * @return size
+     * @throws IOException
+     */
+    public static <K extends Serializable, V extends Serializable> long totalSize( ICacheElement<K, V>[] elements, int startPosition, int endPosition )
+        throws IOException
+    {
+        StandardSerializer serializer = new StandardSerializer();
+        long total = 0;
+        for ( int i = startPosition; i < endPosition; i++ )
+        {
+            int tileSize = serializer.serialize( elements[i] ).length + IndexedDisk.HEADER_SIZE_BYTES;
+            total += tileSize;
+        }
+        return total;
+    }
+
+    /**
+     * Creates an array of ICacheElements with DiskTestObjects with payloads the byte size.
+     * <p>
+     * @param numToCreate
+     * @param bytes
+     * @param cacheName
+     * @return ICacheElement[]
+     */
+    public static ICacheElement<Integer, DiskTestObject>[] createCacheElementsWithTestObjects( int numToCreate, int bytes, String cacheName )
+    {
+        @SuppressWarnings("unchecked")
+        ICacheElement<Integer, DiskTestObject>[] elements = new ICacheElement[numToCreate];
+        for ( int i = 0; i < numToCreate; i++ )
+        {
+            // 24 KB
+            int size = bytes * 1024;
+            DiskTestObject tile = new DiskTestObject( Integer.valueOf( i ), new byte[size] );
+
+            ICacheElement<Integer, DiskTestObject> element = new CacheElement<Integer, DiskTestObject>( cacheName, tile.id, tile );
+            elements[i] = element;
+        }
+        return elements;
+    }
+
+    /**
+     * Creates an array of ICacheElements with DiskTestObjects with payloads the byte size.
+     * <p>
+     * @param numToCreate
+     * @param cacheName
+     * @return ICacheElement[]
+     */
+    public static ICacheElement<Integer, DiskTestObject>[] createCacheElementsWithTestObjectsOfVariableSizes( int numToCreate, String cacheName )
+    {
+        @SuppressWarnings("unchecked")
+        ICacheElement<Integer, DiskTestObject>[] elements = new ICacheElement[numToCreate];
+        Random random = new Random( 89 );
+        for ( int i = 0; i < numToCreate; i++ )
+        {
+            int bytes = random.nextInt( 20 );
+            // 4-24 KB
+            int size = ( bytes + 4 ) * 1024;
+            DiskTestObject tile = new DiskTestObject( Integer.valueOf( i ), new byte[size] );
+
+            ICacheElement<Integer, DiskTestObject> element = new CacheElement<Integer, DiskTestObject>( cacheName, tile.id, tile );
+            elements[i] = element;
+        }
+        return elements;
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/HugeQuantityIndDiskCacheLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/HugeQuantityIndDiskCacheLoadTest.java
new file mode 100644
index 0000000..fc082cb
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/HugeQuantityIndDiskCacheLoadTest.java
@@ -0,0 +1,124 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/**
+ * Put a few hundred thousand entries in the disk cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class HugeQuantityIndDiskCacheLoadTest
+    extends TestCase
+{
+    /** Test setup.  */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheHuge.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @throws Exception If an error occurs
+     */
+    public void testLargeNumberOfItems()
+        throws Exception
+    {
+        int items = 300000;
+        String region = "testCache1";
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        try
+        {
+            System.out.println( "Start: " + measureMemoryUse() );
+
+            // Add items to cache
+
+            for ( int i = 0; i <= items; i++ )
+            {
+                jcs.put( i + ":key", region + " data " + i );
+            }
+
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            System.out.println( "After put: " + measureMemoryUse() );
+
+            Thread.sleep( 5000 );
+
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            System.out.println( "After wait: " + measureMemoryUse() );
+
+            // Test that all items are in cache
+
+            for ( int i = 0; i <= items; i++ )
+            {
+                String value = jcs.get( i + ":key" );
+
+                assertEquals( region + " data " + i, value );
+            }
+
+            System.out.println( "After get: " + measureMemoryUse() );
+
+            // // Remove all the items
+            // for ( int i = 0; i <= items; i++ )
+            // {
+            // jcs.remove( i + ":key" );
+            // }
+            //
+            // // Verify removal
+            // for ( int i = 0; i <= items; i++ )
+            // {
+            // assertNull( "Removed key should be null: " + i + ":key" + "\n
+            // stats " + jcs.getStats(), jcs.get( i + ":key" ) );
+            // }
+
+        }
+        finally
+        {
+            // dump the stats to the report
+            System.out.println( jcs.getStats() );
+            System.out.println( "--------------------------" );
+            System.out.println( "End: " + measureMemoryUse() );
+        }
+    }
+
+    /**
+     * Measure memory used by the VM.
+     * <p>
+     * @return memory used
+     * @throws InterruptedException
+     */
+    protected long measureMemoryUse()
+        throws InterruptedException
+    {
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexDiskCacheUnitTest.java
new file mode 100644
index 0000000..032fcb5
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexDiskCacheUnitTest.java
@@ -0,0 +1,1012 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.auxiliary.disk.DiskTestObject;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for common functionality.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class IndexDiskCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Simply verify that we can put items in the disk cache and retrieve them.
+     * @throws IOException
+     */
+    public void testSimplePutAndGet()
+        throws IOException
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testSimplePutAndGet" );
+        cattr.setMaxKeySize( 1000 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        disk.processRemoveAll();
+
+        int cnt = 999;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testSimplePutAndGet", "key:" + i, "data:" + i );
+            element.setElementAttributes( eAttr );
+            disk.processUpdate( element );
+        }
+
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = disk.processGet( "key:" + i );
+            assertNotNull( "Should have received an element.", element );
+            assertEquals( "Element is wrong.", "data:" + i, element.getVal() );
+        }
+
+        // Test that getMultiple returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i < cnt; i++ )
+        {
+            keys.add( "key:" + i );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = disk.getMultiple( keys );
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( "key:" + i );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value key:" + i, "data:" + i, element.getVal() );
+        }
+        //System.out.println( disk.getStats() );
+    }
+
+    /**
+     * Add some items to the disk cache and then remove them one by one.
+     * @throws IOException
+     */
+    public void testRemoveItems()
+        throws IOException
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemoveItems" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        disk.processRemoveAll();
+
+        int cnt = 25;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRemoveItems", "key:" + i, "data:" + i );
+            element.setElementAttributes( eAttr );
+            disk.processUpdate( element );
+        }
+
+        // remove each
+        for ( int i = 0; i < cnt; i++ )
+        {
+            disk.remove( "key:" + i );
+            ICacheElement<String, String> element = disk.processGet( "key:" + i );
+            assertNull( "Should not have received an element.", element );
+        }
+    }
+
+    /**
+     * Verify that we don't override the largest item.
+     * <p>
+     * @throws IOException
+     */
+    public void testRecycleBin()
+        throws IOException
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemoveItems" );
+        cattr.setMaxRecycleBinSize( 2 );
+        cattr.setOptimizeAtRemoveCount( 7 );
+        cattr.setMaxKeySize( 5 );
+        cattr.setMaxPurgatorySize( 0 );
+        cattr.setDiskPath( "target/test-sandbox/BreakIndexTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        String[] test = { "a", "bb", "ccc", "dddd", "eeeee", "ffffff", "ggggggg", "hhhhhhhhh", "iiiiiiiiii" };
+        String[] expect = { null, "bb", "ccc", null, null, "ffffff", null, "hhhhhhhhh", "iiiiiiiiii" };
+
+        //System.out.println( "------------------------- testRecycleBin " );
+
+        for ( int i = 0; i < 6; i++ )
+        {
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRecycleBin", "key:" + test[i], test[i] );
+            //System.out.println( "About to add " + "key:" + test[i] + " i = " + i );
+            disk.processUpdate( element );
+        }
+
+        for ( int i = 3; i < 5; i++ )
+        {
+            //System.out.println( "About to remove " + "key:" + test[i] + " i = " + i );
+            disk.remove( "key:" + test[i] );
+        }
+
+        // there was a bug where 7 would try to be put in the empty slot left by 4's removal, but it
+        // will not fit.
+        for ( int i = 7; i < 9; i++ )
+        {
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRecycleBin", "key:" + test[i], test[i] );
+            //System.out.println( "About to add " + "key:" + test[i] + " i = " + i );
+            disk.processUpdate( element );
+        }
+
+        try
+        {
+            for ( int i = 0; i < 9; i++ )
+            {
+                ICacheElement<String, String> element = disk.get( "key:" + test[i] );
+                if ( element != null )
+                {
+                    //System.out.println( "element = " + element.getVal() );
+                }
+                else
+                {
+                    //System.out.println( "null --" + "key:" + test[i] );
+                }
+
+                String expectedValue = expect[i];
+                if ( expectedValue == null )
+                {
+                    assertNull( "Expected a null element", element );
+                }
+                else
+                {
+                    assertNotNull( "The element for key [" + "key:" + test[i] + "] should not be null. i = " + i,
+                                   element );
+                    assertEquals( "Elements contents do not match expected", element.getVal(), expectedValue );
+                }
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+            fail( "Should not get an exception: " + e.toString() );
+        }
+
+        disk.removeAll();
+    }
+
+    /**
+     * Verify that the overlap check returns true when there are no overlaps.
+     */
+    public void testCheckForDedOverlaps_noOverlap()
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testCheckForDedOverlaps_noOverlap" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        int numDescriptors = 5;
+        int pos = 0;
+        IndexedDiskElementDescriptor[] sortedDescriptors = new IndexedDiskElementDescriptor[numDescriptors];
+        for ( int i = 0; i < numDescriptors; i++ )
+        {
+            IndexedDiskElementDescriptor descriptor = new IndexedDiskElementDescriptor( pos, i * 2 );
+            pos = pos + ( i * 2 ) + IndexedDisk.HEADER_SIZE_BYTES;
+            sortedDescriptors[i] = descriptor;
+        }
+
+        // DO WORK
+        boolean result = disk.checkForDedOverlaps( sortedDescriptors );
+
+        // VERIFY
+        assertTrue( "There should be no overlap. it should be ok", result );
+    }
+
+    /**
+     * Verify that the overlap check returns false when there are overlaps.
+     */
+    public void testCheckForDedOverlaps_overlaps()
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testCheckForDedOverlaps_overlaps" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        int numDescriptors = 5;
+        int pos = 0;
+        IndexedDiskElementDescriptor[] sortedDescriptors = new IndexedDiskElementDescriptor[numDescriptors];
+        for ( int i = 0; i < numDescriptors; i++ )
+        {
+            IndexedDiskElementDescriptor descriptor = new IndexedDiskElementDescriptor( pos, i * 2 );
+            // don't add the header + IndexedDisk.RECORD_HEADER;
+            pos = pos + ( i * 2 );
+            sortedDescriptors[i] = descriptor;
+        }
+
+        // DO WORK
+        boolean result = disk.checkForDedOverlaps( sortedDescriptors );
+
+        // VERIFY
+        assertFalse( "There should be overlaps. it should be not ok", result );
+    }
+
+    /**
+     * Verify that the file size is as expected.
+     * <p>
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void testFileSize()
+        throws IOException, InterruptedException
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testFileSize" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        IndexedDiskCache<Integer, DiskTestObject> disk = new IndexedDiskCache<Integer, DiskTestObject>( cattr );
+
+        int numberToInsert = 20;
+        int bytes = 24;
+        ICacheElement<Integer, DiskTestObject>[] elements = DiskTestObjectUtil.createCacheElementsWithTestObjects( numberToInsert, bytes, cattr
+            .getCacheName() );
+
+        for ( int i = 0; i < elements.length; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        Thread.yield();
+        Thread.sleep( 100 );
+        Thread.yield();
+
+        long expectedSize = DiskTestObjectUtil.totalSize( elements, numberToInsert );
+        long resultSize = disk.getDataFileSize();
+
+        //System.out.println( "testFileSize stats " + disk.getStats() );
+
+        assertEquals( "Wrong file size", expectedSize, resultSize );
+    }
+
+    /**
+     * Verify that items are added to the recycle bin on removal.
+     * <p>
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void testRecyleBinSize()
+        throws IOException, InterruptedException
+    {
+        // SETUP
+        int numberToInsert = 20;
+
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRecyleBinSize" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        cattr.setMaxRecycleBinSize( numberToInsert );
+        cattr.setOptimizeAtRemoveCount( numberToInsert );
+        cattr.setMaxKeySize( numberToInsert * 2 );
+        cattr.setMaxPurgatorySize( numberToInsert );
+        IndexedDiskCache<Integer, DiskTestObject> disk = new IndexedDiskCache<Integer, DiskTestObject>( cattr );
+
+        int bytes = 24;
+        ICacheElement<Integer, DiskTestObject>[] elements = DiskTestObjectUtil.createCacheElementsWithTestObjects( numberToInsert, bytes, cattr
+            .getCacheName() );
+
+        for ( int i = 0; i < elements.length; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        Thread.yield();
+        Thread.sleep( 100 );
+        Thread.yield();
+
+        // remove half
+        int numberToRemove = elements.length / 2;
+        for ( int i = 0; i < numberToRemove; i++ )
+        {
+            disk.processRemove( elements[i].getKey() );
+        }
+
+        // verify that the recycle bin has the correct amount.
+        assertEquals( "The recycle bin should have the number removed.", numberToRemove, disk.getRecyleBinSize() );
+    }
+
+    /**
+     * Verify that items of the same size use recycle bin spots. Setup the recycle bin by removing
+     * some items. Add some of the same size. Verify that the recycle count is the number added.
+     * <p>
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void testRecyleBinUsage()
+        throws IOException, InterruptedException
+    {
+        // SETUP
+        int numberToInsert = 20;
+
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRecyleBinUsage" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        cattr.setMaxRecycleBinSize( numberToInsert );
+        cattr.setOptimizeAtRemoveCount( numberToInsert );
+        cattr.setMaxKeySize( numberToInsert * 2 );
+        cattr.setMaxPurgatorySize( numberToInsert );
+        IndexedDiskCache<Integer, DiskTestObject> disk = new IndexedDiskCache<Integer, DiskTestObject>( cattr );
+
+        // we will reuse these
+        int bytes = 24;
+        ICacheElement<Integer, DiskTestObject>[] elements = DiskTestObjectUtil.createCacheElementsWithTestObjects( numberToInsert, bytes, cattr
+            .getCacheName() );
+
+        // Add some to the disk
+        for ( int i = 0; i < elements.length; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        Thread.yield();
+        Thread.sleep( 100 );
+        Thread.yield();
+
+        // remove half of those added
+        int numberToRemove = elements.length / 2;
+        for ( int i = 0; i < numberToRemove; i++ )
+        {
+            disk.processRemove( elements[i].getKey() );
+        }
+
+        // verify that the recycle bin has the correct amount.
+        assertEquals( "The recycle bin should have the number removed.", numberToRemove, disk.getRecyleBinSize() );
+
+        // add half as many as we removed. These should all use spots in the recycle bin.
+        int numberToAdd = numberToRemove / 2;
+        for ( int i = 0; i < numberToAdd; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        // verify that we used the correct number of spots
+        assertEquals( "The recycle bin should have the number removed." + disk.getStats(), numberToAdd, disk
+            .getRecyleCount() );
+    }
+
+    /**
+     * Verify that the data size is as expected after a remove and after a put that should use the
+     * spots.
+     * <p>
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void testBytesFreeSize()
+        throws IOException, InterruptedException
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testBytesFreeSize" );
+        cattr.setDiskPath( "target/test-sandbox/UnitTest" );
+        IndexedDiskCache<Integer, DiskTestObject> disk = new IndexedDiskCache<Integer, DiskTestObject>( cattr );
+
+        int numberToInsert = 20;
+        int bytes = 24;
+        ICacheElement<Integer, DiskTestObject>[] elements = DiskTestObjectUtil.createCacheElementsWithTestObjects( numberToInsert, bytes, cattr
+            .getCacheName() );
+
+        for ( int i = 0; i < elements.length; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        Thread.yield();
+        Thread.sleep( 100 );
+        Thread.yield();
+
+        // remove half of those added
+        int numberToRemove = elements.length / 2;
+        for ( int i = 0; i < numberToRemove; i++ )
+        {
+            disk.processRemove( elements[i].getKey() );
+        }
+
+        long expectedSize = DiskTestObjectUtil.totalSize( elements, numberToRemove );
+        long resultSize = disk.getBytesFree();
+
+        //System.out.println( "testBytesFreeSize stats " + disk.getStats() );
+
+        assertEquals( "Wrong bytes free size" + disk.getStats(), expectedSize, resultSize );
+
+        // add half as many as we removed. These should all use spots in the recycle bin.
+        int numberToAdd = numberToRemove / 2;
+        for ( int i = 0; i < numberToAdd; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        long expectedSize2 = DiskTestObjectUtil.totalSize( elements, numberToAdd );
+        long resultSize2 = disk.getBytesFree();
+        assertEquals( "Wrong bytes free size" + disk.getStats(), expectedSize2, resultSize2 );
+    }
+
+    /**
+     * Add some items to the disk cache and then remove them one by one.
+     * <p>
+     * @throws IOException
+     */
+    public void testRemove_PartialKey()
+        throws IOException
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemove_PartialKey" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        disk.processRemoveAll();
+
+        int cnt = 25;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRemove_PartialKey", i + ":key", "data:" + i );
+            element.setElementAttributes( eAttr );
+            disk.processUpdate( element );
+        }
+
+        // verif each
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = disk.processGet( i + ":key" );
+            assertNotNull( "Shoulds have received an element.", element );
+        }
+
+        // remove each
+        for ( int i = 0; i < cnt; i++ )
+        {
+            disk.remove( i + ":" );
+            ICacheElement<String, String> element = disk.processGet( i + ":key" );
+            assertNull( "Should not have received an element.", element );
+        }
+        // https://issues.apache.org/jira/browse/JCS-67
+        assertEquals( "Recylenbin should not have more elements than we removed. Check for JCS-67", cnt, disk
+            .getRecyleBinSize() );
+    }
+
+    /**
+     * Verify that group members are removed if we call remove with a group.
+     * @throws IOException
+     */
+    public void testRemove_Group()
+        throws IOException
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemove_Group" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<GroupAttrName<String>, String> disk = new IndexedDiskCache<GroupAttrName<String>, String>( cattr );
+
+        disk.processRemoveAll();
+
+        String cacheName = "testRemove_Group_Region";
+        String groupName = "testRemove_Group";
+
+        int cnt = 25;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            GroupAttrName<String> groupAttrName = getGroupAttrName( cacheName, groupName, i + ":key" );
+            CacheElement<GroupAttrName<String>, String> element = new CacheElement<GroupAttrName<String>, String>( cacheName, groupAttrName, "data:" + i );
+
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            element.setElementAttributes( eAttr );
+
+            disk.processUpdate( element );
+        }
+
+        // verify each
+        for ( int i = 0; i < cnt; i++ )
+        {
+            GroupAttrName<String> groupAttrName = getGroupAttrName( cacheName, groupName, i + ":key" );
+            ICacheElement<GroupAttrName<String>, String> element = disk.processGet( groupAttrName );
+            assertNotNull( "Should have received an element.", element );
+        }
+
+        // DO WORK
+        // remove the group
+        disk.remove( getGroupAttrName( cacheName, groupName, null ) );
+
+        for ( int i = 0; i < cnt; i++ )
+        {
+            GroupAttrName<String> groupAttrName = getGroupAttrName( cacheName, groupName, i + ":key" );
+            ICacheElement<GroupAttrName<String>, String> element = disk.processGet( groupAttrName );
+
+            // VERIFY
+            assertNull( "Should not have received an element.", element );
+        }
+
+    }
+
+    /**
+     * Internal method used for group functionality.
+     * <p>
+     * @param cacheName
+     * @param group
+     * @param name
+     * @return GroupAttrName
+     */
+    private GroupAttrName<String> getGroupAttrName( String cacheName, String group, String name )
+    {
+        GroupId gid = new GroupId( cacheName, group );
+        return new GroupAttrName<String>( gid, name );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_EventLogging_simple()
+        throws Exception
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testUpdate_EventLogging_simple" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTestCEL" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+        diskCache.processRemoveAll();
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        diskCache.setCacheEventLogger( cacheEventLogger );
+
+        ICacheElement<String, String> item = new CacheElement<String, String>( "region", "key", "value" );
+
+        // DO WORK
+        diskCache.update( item );
+
+        SleepUtil.sleepAtLeast( 200 );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet_EventLogging_simple()
+        throws Exception
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testGet_EventLogging_simple" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTestCEL" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+        diskCache.processRemoveAll();
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        diskCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        diskCache.get( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMultiple_EventLogging_simple()
+        throws Exception
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testGetMultiple_EventLogging_simple" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTestCEL" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+        diskCache.processRemoveAll();
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        diskCache.setCacheEventLogger( cacheEventLogger );
+
+        Set<String> keys = new HashSet<String>();
+        keys.add( "junk" );
+
+        // DO WORK
+        diskCache.getMultiple( keys );
+
+        // VERIFY
+        // 1 for get multiple and 1 for get.
+        assertEquals( "Start should have been called.", 2, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 2, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemove_EventLogging_simple()
+        throws Exception
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemoveAll_EventLogging_simple" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTestCEL" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+        diskCache.processRemoveAll();
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        diskCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        diskCache.remove( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAll_EventLogging_simple()
+        throws Exception
+    {
+        // SETUP
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testRemoveAll_EventLogging_simple" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTestCEL" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+        diskCache.processRemoveAll();
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        diskCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        diskCache.remove( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Test the basic get matching.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGetMatching_SmallWait()
+        throws Exception
+    {
+        // SETUP
+        int items = 200;
+
+        String cacheName = "testPutGetMatching_SmallWait";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        // DO WORK
+        for ( int i = 0; i <= items; i++ )
+        {
+            diskCache.update( new CacheElement<String, String>( cacheName, i + ":key", cacheName + " data " + i ) );
+        }
+        Thread.sleep( 500 );
+
+        Map<String, ICacheElement<String, String>> matchingResults = diskCache.getMatching( "1.8.+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned", 10, matchingResults.size() );
+        //System.out.println( "matchingResults.keySet() " + matchingResults.keySet() );
+        //System.out.println( "\nAFTER TEST \n" + diskCache.getStats() );
+    }
+
+    /**
+     * Test the basic get matching. With no wait this will all come from purgatory.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGetMatching_NoWait()
+        throws Exception
+    {
+        // SETUP
+        int items = 200;
+
+        String cacheName = "testPutGetMatching_NoWait";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        // DO WORK
+        for ( int i = 0; i <= items; i++ )
+        {
+            diskCache.update( new CacheElement<String, String>( cacheName, i + ":key", cacheName + " data " + i ) );
+        }
+
+        Map<String, ICacheElement<String, String>> matchingResults = diskCache.getMatching( "1.8.+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned", 10, matchingResults.size() );
+        //System.out.println( "matchingResults.keySet() " + matchingResults.keySet() );
+        //System.out.println( "\nAFTER TEST \n" + diskCache.getStats() );
+    }
+
+    /**
+     * Verify that the block disk cache can handle utf encoded strings.
+     * <p>
+     * @throws Exception
+     */
+    public void testUTF8String()
+        throws Exception
+    {
+        String string = "IÒtÎrn‚tiÙn‡lizÊti¯n";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( sb.toString() ); // big string
+        }
+        string = sb.toString();
+
+        //System.out.println( "The string contains " + string.length() + " characters" );
+
+        String cacheName = "testUTF8String";
+
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, String>( cacheName, "x", string ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, String> afterElement = diskCache.get( "x" );
+        assertNotNull( afterElement );
+        // System.out.println( "afterElement = " + afterElement );
+        String after = afterElement.getVal();
+
+        assertNotNull( after );
+        assertEquals( "wrong string after retrieval", string, after );
+    }
+
+    /**
+     * Verify that the block disk cache can handle utf encoded strings.
+     * <p>
+     * @throws Exception
+     */
+    public void testUTF8ByteArray()
+        throws Exception
+    {
+        String string = "IÒtÎrn‚tiÙn‡lizÊti¯n";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( sb.toString() ); // big string
+        }
+        string = sb.toString();
+        //System.out.println( "The string contains " + string.length() + " characters" );
+        String UTF8 = "UTF-8";
+        byte[] bytes = string.getBytes( UTF8 );
+
+        String cacheName = "testUTF8ByteArray";
+
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, byte[]> diskCache = new IndexedDiskCache<String, byte[]>( cattr );
+
+        // DO WORK
+        diskCache.update( new CacheElement<String, byte[]>( cacheName, "x", bytes ) );
+
+        // VERIFY
+        assertNotNull( diskCache.get( "x" ) );
+        Thread.sleep( 1000 );
+        ICacheElement<String, byte[]> afterElement = diskCache.get( "x" );
+        assertNotNull( afterElement );
+        // System.out.println( "afterElement = " + afterElement );
+        byte[] after = afterElement.getVal();
+
+        assertNotNull( after );
+        assertEquals( "wrong bytes after retrieval", string, new String( after, UTF8 ) );
+    }
+
+    /**
+     * Verify the item makes it to disk.
+     * <p>
+     * @throws IOException
+     */
+    public void testProcessUpdate_Simple()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testProcessUpdate_Simple";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        String key = "myKey";
+        String value = "myValue";
+        ICacheElement<String, String> ce = new CacheElement<String, String>( cacheName, key, value );
+
+        // DO WORK
+        diskCache.processUpdate( ce );
+        ICacheElement<String, String> result = diskCache.processGet( key );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        long fileSize = diskCache.getDataFileSize();
+        assertTrue( "File should be greater than 0", fileSize > 0 );
+    }
+
+    /**
+     * Verify the item makes it to disk.
+     * <p>
+     * @throws IOException
+     */
+    public void testProcessUpdate_SameKeySameSize()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testProcessUpdate_SameKeySameSize";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        String key = "myKey";
+        String value = "myValue";
+        ICacheElement<String, String> ce1 = new CacheElement<String, String>( cacheName, key, value );
+
+        // DO WORK
+        diskCache.processUpdate( ce1 );
+        long fileSize1 = diskCache.getDataFileSize();
+
+        // DO WORK
+        ICacheElement<String, String> ce2 = new CacheElement<String, String>( cacheName, key, value );
+        diskCache.processUpdate( ce2 );
+        ICacheElement<String, String> result = diskCache.processGet( key );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        long fileSize2 = diskCache.getDataFileSize();
+        assertEquals( "File should be the same", fileSize1, fileSize2 );
+        int binSize = diskCache.getRecyleBinSize();
+        assertEquals( "Should be nothing in the bin.", 0, binSize );
+    }
+
+    /**
+     * Verify the item makes it to disk.
+     * <p>
+     * @throws IOException
+     */
+    public void testProcessUpdate_SameKeySmallerSize()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testProcessUpdate_SameKeySmallerSize";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        String key = "myKey";
+        String value = "myValue";
+        String value2 = "myValu";
+        ICacheElement<String, String> ce1 = new CacheElement<String, String>( cacheName, key, value );
+
+        // DO WORK
+        diskCache.processUpdate( ce1 );
+        long fileSize1 = diskCache.getDataFileSize();
+
+        // DO WORK
+        ICacheElement<String, String> ce2 = new CacheElement<String, String>( cacheName, key, value2 );
+        diskCache.processUpdate( ce2 );
+        ICacheElement<String, String> result = diskCache.processGet( key );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        long fileSize2 = diskCache.getDataFileSize();
+        assertEquals( "File should be the same", fileSize1, fileSize2 );
+        int binSize = diskCache.getRecyleBinSize();
+        assertEquals( "Should be nothing in the bin.", 0, binSize );
+    }
+
+    /**
+     * Verify that the old slot gets in the recycle bin.
+     * <p>
+     * @throws IOException
+     */
+    public void testProcessUpdate_SameKeyBiggerSize()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testProcessUpdate_SameKeyBiggerSize";
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( cacheName );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/IndexDiskCacheUnitTest" );
+        IndexedDiskCache<String, String> diskCache = new IndexedDiskCache<String, String>( cattr );
+
+        String key = "myKey";
+        String value = "myValue";
+        String value2 = "myValue2";
+        ICacheElement<String, String> ce1 = new CacheElement<String, String>( cacheName, key, value );
+
+        // DO WORK
+        diskCache.processUpdate( ce1 );
+        long fileSize1 = diskCache.getDataFileSize();
+
+        // DO WORK
+        ICacheElement<String, String> ce2 = new CacheElement<String, String>( cacheName, key, value2 );
+        diskCache.processUpdate( ce2 );
+        ICacheElement<String, String> result = diskCache.processGet( key );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        long fileSize2 = diskCache.getDataFileSize();
+        assertTrue( "File should be greater.", fileSize1 < fileSize2 );
+        int binSize = diskCache.getRecyleBinSize();
+        assertEquals( "Should be one in the bin.", 1, binSize );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentNoDeadLockUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentNoDeadLockUnitTest.java
new file mode 100644
index 0000000..eca55d6
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentNoDeadLockUnitTest.java
@@ -0,0 +1,148 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.textui.TestRunner;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+/**
+ * Test which exercises the indexed disk cache. Runs three threads against the
+ * same region.
+ *
+ * @version $Id: TestDiskCacheConcurrentForDeadLock.java,v 1.2 2005/02/01
+ *          00:01:59 asmuts Exp $
+ */
+public class IndexedDiskCacheConcurrentNoDeadLockUnitTest
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public IndexedDiskCacheConcurrentNoDeadLockUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { IndexedDiskCacheConcurrentNoDeadLockUnitTest.class.getName() };
+        TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new IndexedDiskCacheRandomConcurrentTestUtil( "testIndexedDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 1, 200, 1 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheRandomConcurrentTestUtil( "testIndexedDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 10000, 50000, 2 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheRandomConcurrentTestUtil( "testIndexedDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 10000, 50000, 3 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheRandomConcurrentTestUtil( "testIndexedDiskCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 10000, 50000, 4 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheRandomConcurrentTestUtil( "testIndexedDiskCache5" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 10000, 50000, 5 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheCon.ccf" );
+    }
+
+    /**
+     * Test tearDown. Dispose of the cache.
+     */
+    @Override
+    public void tearDown()
+    {
+        try
+        {
+            CompositeCacheManager cacheMgr = CompositeCacheManager.getInstance();
+            cacheMgr.shutDown();
+        }
+        catch ( Exception e )
+        {
+            // log.error(e);
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentUnitTest.java
new file mode 100644
index 0000000..a1c18f3
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheConcurrentUnitTest.java
@@ -0,0 +1,251 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the indexed disk cache. This one uses three different
+ * regions for thre threads.
+ *
+ * @version $Id: IndexedDiskCacheConcurrentUnitTest.java 1593844 2014-05-11 19:53:32Z rmannibucau $
+ */
+public class IndexedDiskCacheConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory
+     * cache regions.
+     */
+    private static int items = 200;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public IndexedDiskCacheConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { IndexedDiskCacheConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new IndexedDiskCacheConcurrentUnitTest( "testIndexedDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion1" );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheConcurrentUnitTest( "testIndexedDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion2" );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheConcurrentUnitTest( "testIndexedDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion3" );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheConcurrentUnitTest( "testIndexedDiskCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegionInRange( "indexedRegion3", 300, 600 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+        // another thread may have inserted since
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key" + "\n stats " + jcs.getStats(), jcs
+                .get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     * @param start
+     * @param end
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegionInRange( String region, int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+        for ( int i = start; i <= end; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = start; i <= end; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = start; i <= end; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        // Verify removal
+        // another thread may have inserted since
+        for ( int i = start; i <= end; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key " + "\n stats " + jcs.getStats(), jcs.get( i
+                + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheDefragPerformanceTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheDefragPerformanceTest.java
new file mode 100644
index 0000000..7ce6788
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheDefragPerformanceTest.java
@@ -0,0 +1,180 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.util.Random;
+
+/**
+ * This is for manually testing the defrag process.
+ */
+public class IndexedDiskCacheDefragPerformanceTest
+    extends TestCase
+{
+    /** For readability */
+    private static final String LOG_DIVIDER = "---------------------------";
+
+    /** total to test with */
+    private static final int TOTAL_ELEMENTS = 30000;
+
+    /** time to wait */
+    private static final long SLEEP_TIME_DISK = 8000;
+
+    /** how often to log */
+    private static final int LOG_INCREMENT = 5000;
+
+    /** for getting memory usage */
+    private static Runtime rt = Runtime.getRuntime();
+
+    /** for displaying memory usage */
+    private static DecimalFormat format = new DecimalFormat( "#,###" );
+
+    /**
+     * @throws Exception
+     */
+    public void testRealTimeOptimization()
+        throws Exception
+    {
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "JCS DEFRAG PERFORMANCE TESTS" );
+        System.out.println( LOG_DIVIDER );
+        logMemoryUsage();
+        IndexedDiskCacheDefragPerformanceTest.runRealTimeOptimizationTest();
+        logMemoryUsage();
+
+        System.out.println( LOG_DIVIDER );
+    }
+
+    /**
+     * @throws Exception
+     */
+    private static void runRealTimeOptimizationTest()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestDiskCacheDefragPerformance.ccf" );
+        CacheAccess<Integer, Tile> jcs = JCS.getInstance( "defrag" );
+
+        Tile tile;
+        System.out.println( "Cache Defrag Test" );
+
+        Random random = new Random( 89 );
+        for ( int i = 0; i < TOTAL_ELEMENTS; i++ )
+        {
+            int bytes = random.nextInt( 20 );
+            // 4-24 KB
+            tile = new Tile( Integer.valueOf( i ), new byte[( bytes + 4 ) * 1024] );
+            // images
+
+            jcs.put( tile.id, tile );
+
+            if ( ( i != 0 ) && ( 0 == ( i % 100 ) ) )
+            {
+                jcs.get( Integer.valueOf( random.nextInt( i ) ) );
+            }
+
+            if ( 0 == ( i % LOG_INCREMENT ) )
+            {
+                System.out.print( i + ", " );
+                Thread.sleep( SLEEP_TIME_DISK );
+            }
+        }
+
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Total elements = " + TOTAL_ELEMENTS );
+        System.out.println( "Stats prior to sleeping " + jcs.getStats() );
+
+        // Allow system to settle down
+        System.out.println( "Sleeping for a a minute." );
+        Thread.sleep( 60000 );
+
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Stats prior to dispose " + jcs.getStats() );
+
+        jcs.dispose();
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Stats after dispose " + jcs.getStats() );
+        System.out.println( "Done testing." );
+    }
+
+    /**
+     * Logs the memory usage.
+     */
+    private static void logMemoryUsage()
+    {
+        long byte2MB = 1024 * 1024;
+        long total = rt.totalMemory() / byte2MB;
+        long free = rt.freeMemory() / byte2MB;
+        long used = total - free;
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Memory:" + " Used:" + format.format( used ) + "MB" + " Free:" + format.format( free )
+            + "MB" + " Total:" + format.format( total ) + "MB" );
+    }
+
+    /**
+     * Resembles a cached image.
+     */
+    private static class Tile
+        implements Serializable
+    {
+        /** Don't change */
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Key
+         */
+        public Integer id;
+
+        /**Byte size
+         *
+         */
+        public byte[] imageBytes;
+
+        /**
+         * @param id
+         * @param imageBytes
+         */
+        public Tile( Integer id, byte[] imageBytes )
+        {
+            this.id = id;
+            this.imageBytes = imageBytes;
+        }
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        try
+        {
+            IndexedDiskCacheDefragPerformanceTest tester = new IndexedDiskCacheDefragPerformanceTest();
+            tester.testRealTimeOptimization();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
new file mode 100644
index 0000000..644213e
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
@@ -0,0 +1,149 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+/**
+ * Test store and load keys.
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class IndexedDiskCacheKeyStoreUnitTest
+    extends TestCase
+{
+
+    /**
+     * Add some keys, store them, load them from disk, then check to see that we
+     * can get the items.
+     *
+     * @throws Exception
+     *
+     */
+    public void testStoreKeys()
+        throws Exception
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testStoreKeys" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/KeyStoreUnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        disk.processRemoveAll();
+
+        int cnt = 25;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            ICacheElement<String, String> element = new CacheElement<String, String>( cattr.getCacheName(), "key:" + i, "data:" + i );
+            element.setElementAttributes( eAttr );
+            disk.processUpdate( element );
+        }
+
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = disk.processGet( "key:" + i );
+            assertNotNull( "presave, Should have received an element.", element );
+            assertEquals( "presave, element is wrong.", "data:" + i, element.getVal() );
+        }
+
+        disk.saveKeys();
+
+        disk.loadKeys();
+
+        assertEquals( "The disk is the wrong size.", cnt, disk.getSize() );
+
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = disk.processGet( "key:" + i );
+            assertNotNull( "postsave, Should have received an element.", element );
+            assertEquals( "postsave, element is wrong.", "data:" + i, element.getVal() );
+        }
+
+        disk.dump();
+
+    }
+
+
+    /**
+     * Add some elements, remove 1, call optimize, verify that the removed isn't present.
+     *
+     * We should also compare the data file sizes. . . .
+     *
+     * @throws Exception
+     *
+     */
+    public void testOptiimize()
+        throws Exception
+    {
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testOptimize" );
+        cattr.setMaxKeySize( 100 );
+        cattr.setDiskPath( "target/test-sandbox/KeyStoreUnitTest" );
+        IndexedDiskCache<String, String> disk = new IndexedDiskCache<String, String>( cattr );
+
+        disk.processRemoveAll();
+
+        int cnt = 25;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            IElementAttributes eAttr = new ElementAttributes();
+            eAttr.setIsSpool( true );
+            ICacheElement<String, String> element = new CacheElement<String, String>( cattr.getCacheName(), "key:" + i, "data:" + i );
+            element.setElementAttributes( eAttr );
+            disk.processUpdate( element );
+        }
+
+        long preAddRemoveSize = disk.getDataFileSize();
+
+        IElementAttributes eAttr = new ElementAttributes();
+        eAttr.setIsSpool( true );
+        ICacheElement<String, String> elementSetup = new CacheElement<String, String>( cattr.getCacheName(), "key:" + "A", "data:" + "A" );
+        elementSetup.setElementAttributes( eAttr );
+        disk.processUpdate( elementSetup );
+
+        ICacheElement<String, String> elementRet = disk.processGet( "key:" + "A" );
+        assertNotNull( "postsave, Should have received an element.", elementRet );
+        assertEquals( "postsave, element is wrong.", "data:" + "A", elementRet.getVal() );
+
+        disk.remove( "key:" + "A" );
+
+        long preSize = disk.getDataFileSize();
+        // synchronous versoin
+        disk.optimizeFile(); //deoptimizeRealTime();
+        long postSize = disk.getDataFileSize();
+
+        assertTrue( "Should be smaller. postsize="+postSize+" preSize="+preSize, postSize < preSize );
+        assertEquals( "Should be the same size after optimization as before add and remove.", preAddRemoveSize, postSize );
+
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = disk.processGet( "key:" + i );
+            assertNotNull( "postsave, Should have received an element.", element );
+            assertEquals( "postsave, element is wrong.", "data:" + i, element.getVal() );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManagerUnitTest.java
new file mode 100644
index 0000000..faec356
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheManagerUnitTest.java
@@ -0,0 +1,70 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.io.IOException;
+
+/** Unit tests for the manager */
+public class IndexedDiskCacheManagerUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the disk cache has the event logger
+     * @throws IOException
+     */
+    public void testGetCache_normal()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testGetCache_normal";
+        IndexedDiskCacheAttributes defaultCacheAttributes = new IndexedDiskCacheAttributes();
+        defaultCacheAttributes.setDiskPath( "target/IndexedDiskCacheManagerUnitTest" );
+
+        ICacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        MockElementSerializer elementSerializer = new MockElementSerializer();
+
+        String key = "myKey";
+        ICacheElement<String, String> cacheElement = new CacheElement<String, String>( "test", key, "MyValue" );
+
+        IndexedDiskCacheManager manager = IndexedDiskCacheManager.getInstance( defaultCacheAttributes,
+                                                                               cacheEventLogger, elementSerializer );
+
+        // DO WORK
+        IndexedDiskCache<String, String> cache = manager.getCache( cacheName );
+
+        cache.update( cacheElement );
+        SleepUtil.sleepAtLeast( 100 );
+        cache.get( key );
+
+        // VERIFY
+        assertEquals( "wrong cacheEventLogger", cacheEventLogger, cache.getCacheEventLogger() );
+        assertEquals( "wrong elementSerializer", elementSerializer, cache.getElementSerializer() );
+        assertEquals( "Wrong serialize count", elementSerializer.serializeCount, 1 );
+        assertEquals( "Wrong deSerialize count", elementSerializer.deSerializeCount, 1 );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheNoMemoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheNoMemoryUnitTest.java
new file mode 100644
index 0000000..82ad2cc
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheNoMemoryUnitTest.java
@@ -0,0 +1,178 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the indexed disk cache. This one uses three different
+ * regions for thre threads. It uses a config file that specifies 0 items in
+ * memory.
+ */
+public class IndexedDiskCacheNoMemoryUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache; the configured maxObjects for the memory cache
+     * regions is 0.
+     */
+    private static int items = 2000;
+
+    /**
+     * @param testName
+     */
+    public IndexedDiskCacheNoMemoryUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * <p>
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { IndexedDiskCacheNoMemoryUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new IndexedDiskCacheNoMemoryUnitTest( "testIndexedDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion1" );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheNoMemoryUnitTest( "testIndexedDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion2" );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheNoMemoryUnitTest( "testIndexedDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion3" );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheNoMemory.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key" + "\n stats " + jcs.getStats(), jcs.get( i + ":key" ) );
+        }
+
+        // dump the stats to the report
+//        System.out.println( jcs.getStats() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheOptimizationUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheOptimizationUnitTest.java
new file mode 100644
index 0000000..18e54ea
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheOptimizationUnitTest.java
@@ -0,0 +1,93 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.disk.DiskTestObject;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+/**
+ * Tests for the optimization routine.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class IndexedDiskCacheOptimizationUnitTest
+    extends TestCase
+{
+    /**
+     * Set the optimize at remove count to 10. Add 20. Check the file size. Remove 10. Check the
+     * times optimized. Check the file size.
+     * @throws Exception
+     */
+    public void testBasicOptimization()
+        throws Exception
+    {
+        // SETUP
+        int removeCount = 50;
+
+        IndexedDiskCacheAttributes cattr = new IndexedDiskCacheAttributes();
+        cattr.setCacheName( "testOptimization" );
+        cattr.setMaxKeySize( removeCount * 3 );
+        cattr.setOptimizeAtRemoveCount( removeCount );
+        cattr.setMaxRecycleBinSize( removeCount * 3 );
+        cattr.setDiskPath( "target/test-sandbox/testOptimization" );
+        IndexedDiskCache<Integer, DiskTestObject> disk = new IndexedDiskCache<Integer, DiskTestObject>( cattr );
+
+        disk.removeAll();
+
+        int numberToInsert = removeCount * 2;
+        ICacheElement<Integer, DiskTestObject>[] elements = DiskTestObjectUtil
+            .createCacheElementsWithTestObjectsOfVariableSizes( numberToInsert, cattr.getCacheName() );
+
+        for ( int i = 0; i < elements.length; i++ )
+        {
+            disk.processUpdate( elements[i] );
+        }
+
+        Thread.sleep( 1000 );
+        long sizeBeforeRemove = disk.getDataFileSize();
+        System.out.println( "file sizeBeforeRemove " + sizeBeforeRemove );
+        System.out.println( "totalSize inserted " + DiskTestObjectUtil.totalSize( elements, numberToInsert ) );
+
+        // DO WORK
+        for ( int i = 0; i < removeCount; i++ )
+        {
+            disk.processRemove( Integer.valueOf( i ) );
+        }
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+        // VERIFY
+        long sizeAfterRemove = disk.getDataFileSize();
+        long expectedSizeAfterRemove = DiskTestObjectUtil.totalSize( elements, removeCount, elements.length );
+
+        // test is prone to failure for timing reasons.
+        if ( expectedSizeAfterRemove != sizeAfterRemove )
+        {
+            SleepUtil.sleepAtLeast( 2000 );
+        }
+
+        assertTrue( "The post optimization size should be smaller."
+                +"sizeAfterRemove=" + sizeAfterRemove + " sizeBeforeRemove= " +sizeBeforeRemove
+                , sizeAfterRemove < sizeBeforeRemove );
+        assertEquals( "The file size is not as expected size.", expectedSizeAfterRemove, sizeAfterRemove );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheRandomConcurrentTestUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheRandomConcurrentTestUtil.java
new file mode 100644
index 0000000..5d7b8cb
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheRandomConcurrentTestUtil.java
@@ -0,0 +1,86 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.TestCacheAccess;
+
+/**
+ * This is used by other tests to generate a random load on the disk cache.
+ */
+public class IndexedDiskCacheRandomConcurrentTestUtil
+    extends TestCase
+{
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public IndexedDiskCacheRandomConcurrentTestUtil( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Randomly adds items to cache, gets them, and removes them. The range
+     * count is more than the size of the memory cache, so items should spool to
+     * disk.
+     *
+     * @param region
+     *            Name of the region to access
+     * @param range
+     * @param numOps
+     * @param testNum
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region, int range, int numOps, int testNum )
+        throws Exception
+    {
+        // run a rondom operation test to detect deadlocks
+        TestCacheAccess tca = new TestCacheAccess( "/TestDiskCacheCon.ccf" );
+        tca.setRegion( region );
+        tca.random( range, numOps );
+
+        // make sure a simple put then get works
+        // this may fail if the other tests are flooding the disk cache
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        String key = "testKey" + testNum;
+        String data = "testData" + testNum;
+        jcs.put( key, data );
+        String value = jcs.get( key );
+        assertEquals( data, value );
+
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheCon.ccf" );
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSameRegionConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSameRegionConcurrentUnitTest.java
new file mode 100644
index 0000000..ea0182d
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSameRegionConcurrentUnitTest.java
@@ -0,0 +1,208 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the indexed disk cache. Runs three threads against the
+ * same region.
+ */
+public class IndexedDiskCacheSameRegionConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public IndexedDiskCacheSameRegionConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { IndexedDiskCacheSameRegionConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new IndexedDiskCacheSameRegionConcurrentUnitTest( "testIndexedDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 0, 200 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheSameRegionConcurrentUnitTest( "testIndexedDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 1000, 1200 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheSameRegionConcurrentUnitTest( "testIndexedDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 2000, 2200 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheSameRegionConcurrentUnitTest( "testIndexedDiskCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 2200, 5200 );
+            }
+        } );
+
+        suite.addTest( new IndexedDiskCacheSameRegionConcurrentUnitTest( "testIndexedDiskCache5" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion4", 0, 5200 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheCon.ccf" );
+    }
+
+    // /**
+    // * Tests the region which uses the indexed disk cache
+    // */
+    // public void testIndexedDiskCache()
+    // throws Exception
+    // {
+    // runTestForRegion( "indexedRegion" );
+    // }
+    //
+    // /**
+    // * Tests the region which uses the indexed disk cache
+    // */
+    // public void testIndexedDiskCache2()
+    // throws Exception
+    // {
+    // runTestForRegion( "indexedRegion2" );
+    // }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should spool to disk.
+     *
+     * @param region
+     *            Name of the region to access
+     * @param start
+     * @param end
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region, int start, int end )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = start; i <= end; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // Test that all items are in cache
+
+        for ( int i = start; i <= end; i++ )
+        {
+            String key = i + ":key";
+            String value = jcs.get( key );
+
+            assertEquals( "Wrong value for key [" + key + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = start; i <= end; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = start; i <= end; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // you can't remove in one thread and expect them to be in another //
+        //          Remove all the items
+        //
+        //          for ( int i = start; i <= end; i++ ) { jcs.remove( i + ":key" ); } //
+        //          Verify removal
+        //
+        //          for ( int i = start; i <= end; i++ ) { assertNull( "Removed key
+        //          should be null: " + i + ":key", jcs.get( i + ":key" ) ); }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSteadyLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSteadyLoadTest.java
new file mode 100644
index 0000000..ebb24d3
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/indexed/IndexedDiskCacheSteadyLoadTest.java
@@ -0,0 +1,153 @@
+package org.apache.commons.jcs.auxiliary.disk.indexed;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.auxiliary.disk.DiskTestObject;
+import org.apache.commons.jcs.utils.timing.ElapsedTimer;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+
+/**
+ * This allows you to put thousands of large objects into the disk cache and to force removes to
+ * trigger optimizations along the way.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class IndexedDiskCacheSteadyLoadTest
+    extends TestCase
+{
+    /** For display */
+    private static final String LOG_DIVIDER = "---------------------------";
+
+    /** For getting memory info */
+    private static Runtime rt = Runtime.getRuntime();
+
+    /** For display */
+    private static DecimalFormat format = new DecimalFormat( "#,###" );
+
+    /**
+     * Insert 2000 wait 1 second, repeat. Average 1000 / sec.
+     * <p>
+     * @throws Exception
+     */
+    public void testRunSteadyLoadTest()
+        throws Exception
+    {
+        JCS.setConfigFilename( "/TestDiskCacheSteadyLoad.ccf" );
+
+        System.out.println( "runSteadyLoadTest" );
+
+        logMemoryUsage();
+
+        int numPerRun = 200;
+        long pauseBetweenRuns = 1000;
+        int runCount = 0;
+        int runs = 1000;
+        int upperKB = 50;
+
+        CacheAccess<String, DiskTestObject> jcs = JCS.getInstance( ( numPerRun / 2 ) + "aSecond" );
+
+        ElapsedTimer timer = new ElapsedTimer();
+        int numToGet = numPerRun * ( runs / 10 );
+        for ( int i = 0; i < numToGet; i++ )
+        {
+            jcs.get( String.valueOf( i ) );
+        }
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "After getting " + numToGet );
+        System.out.println( "Elapsed " + timer.getElapsedTimeString() );
+        logMemoryUsage();
+
+        jcs.clear();
+        Thread.sleep( 3000 );
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Start putting" );
+
+        long totalSize = 0;
+        int totalPut = 0;
+
+        Random random = new Random( 89 );
+        while ( runCount < runs )
+        {
+            runCount++;
+            for ( int i = 0; i < numPerRun; i++ )
+            {
+                // 1/2 upper to upperKB-4 KB
+                int kiloBytes = Math.max( upperKB / 2, random.nextInt( upperKB ) );
+                int bytes = ( kiloBytes ) * 1024;
+                totalSize += bytes;
+                totalPut++;
+                DiskTestObject object = new DiskTestObject( Integer.valueOf( i ), new byte[bytes] );
+                jcs.put( String.valueOf( totalPut ), object );
+            }
+
+            // remove half of those inserted the previous run
+            if ( runCount > 1 )
+            {
+                for ( int j = ( ( totalPut - numPerRun ) - ( numPerRun / 2 ) ); j < ( totalPut - numPerRun ); j++ )
+                {
+                    jcs.remove( String.valueOf( j ) );
+                }
+            }
+
+            Thread.sleep( pauseBetweenRuns );
+            if ( runCount % 100 == 0 )
+            {
+                System.out.println( LOG_DIVIDER );
+                System.out.println( "Elapsed " + timer.getElapsedTimeString() );
+                System.out.println( "Run count: " + runCount + " Average size: " + ( totalSize / totalPut ) + "\n"
+                    + jcs.getStats() );
+                logMemoryUsage();
+            }
+        }
+
+        Thread.sleep( 3000 );
+        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+
+        Thread.sleep( 10000 );
+        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+
+        System.gc();
+        Thread.sleep( 3000 );
+        System.gc();
+        System.out.println( jcs.getStats() );
+        logMemoryUsage();
+    }
+
+    /**
+     * Logs the memory usage.
+     */
+    private static void logMemoryUsage()
+    {
+        long byte2MB = 1024 * 1024;
+        long total = rt.totalMemory() / byte2MB;
+        long free = rt.freeMemory() / byte2MB;
+        long used = total - free;
+        System.out.println( LOG_DIVIDER );
+        System.out.println( "Memory:" + " Used:" + format.format( used ) + "MB" + " Free:" + format.format( free )
+            + "MB" + " Total:" + format.format( total ) + "MB" );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/HsqlSetupTableUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/HsqlSetupTableUtil.java
new file mode 100644
index 0000000..163e545
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/HsqlSetupTableUtil.java
@@ -0,0 +1,94 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/** Can use this to setup a table. */
+public class HsqlSetupTableUtil
+{
+    /**
+     * SETUP a TABLE FOR CACHE testing
+     * <p>
+     * @param cConn
+     * @param tableName
+     *
+     * @throws SQLException if database problems occur
+     */
+    public static void setupTABLE( Connection cConn, String tableName ) throws SQLException
+    {
+        boolean newT = true;
+
+        StringBuilder createSql = new StringBuilder();
+        createSql.append( "CREATE CACHED TABLE " + tableName + " " );
+        createSql.append( "( " );
+        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
+        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
+        createSql.append( "ELEMENT               BINARY, " );
+        createSql.append( "CREATE_TIME           TIMESTAMP, " );
+        createSql.append( "UPDATE_TIME_SECONDS   BIGINT, " );
+        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
+        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
+        createSql.append( "IS_ETERNAL            CHAR(1), " );
+        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
+        createSql.append( ");" );
+
+        Statement sStatement = cConn.createStatement();
+
+        try
+        {
+            sStatement.executeQuery( createSql.toString() );
+        }
+        catch ( SQLException e )
+        {
+            if ( e.toString().indexOf( "already exists" ) != -1 )
+            {
+                newT = false;
+            }
+            else
+            {
+                throw e;
+            }
+        }
+        finally
+        {
+            sStatement.close();
+        }
+
+        String setupData[] = { "create index iKEY on JCS_STORE2 (CACHE_KEY, REGION)" };
+
+        if ( newT )
+        {
+            for ( int i = 1; i < setupData.length; i++ )
+            {
+                try
+                {
+                    sStatement.executeQuery( setupData[i] );
+                }
+                catch ( SQLException e )
+                {
+                    System.out.println( "Exception: " + e );
+                }
+            }
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerUnitTest.java
new file mode 100644
index 0000000..fe2627a
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheManagerUnitTest.java
@@ -0,0 +1,56 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** Unit tests for the manager */
+public class JDBCDiskCacheManagerUnitTest
+    extends TestCase
+{
+    /** Verify that the disk cache has the event logger */
+    public void testGetCache_normal()
+    {
+        // SETUP
+        String cacheName = "testGetCache_normal";
+        JDBCDiskCacheAttributes defaultCacheAttributes = new JDBCDiskCacheAttributes();
+        // Just use something that exists
+        defaultCacheAttributes.setDriverClassName( "org.hsqldb.jdbcDriver" );
+        defaultCacheAttributes.setDiskPath( "target/JDBCDiskCacheManagerUnitTest" );
+
+        ICacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        IElementSerializer elementSerializer = new MockElementSerializer();
+
+        JDBCDiskCacheManager manager = JDBCDiskCacheManager.getInstance( defaultCacheAttributes, CompositeCacheManager
+            .getUnconfiguredInstance(), cacheEventLogger, elementSerializer );
+
+        // DO WORK
+        JDBCDiskCache<String, String> cache = manager.getCache( cacheName );
+
+        // VERIFY
+        assertEquals( "wrong cacheEventLogger", cacheEventLogger, cache.getCacheEventLogger() );
+        assertEquals( "wrong elementSerializer", elementSerializer, cache.getElementSerializer() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManagerUnitTest.java
new file mode 100644
index 0000000..c65aab0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCachePoolAccessManagerUnitTest.java
@@ -0,0 +1,110 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.Properties;
+
+/** Unit tests for the pool manager */
+public class JDBCDiskCachePoolAccessManagerUnitTest
+    extends TestCase
+{
+    /** Verify that we can configure the object based on the props. */
+    public void testConfigurePoolAccessAttributes_Simple()
+    {
+        // SETUP
+        String poolName = "testConfigurePoolAccessAttributes_Simple";
+
+        String url = "adfads";
+        String userName = "zvzvz";
+        String password = "qewrrewq";
+        int maxActive = 10;
+        String driverClassName = "org.hsqldb.jdbcDriver";
+
+        Properties props = new Properties();
+        String prefix = JDBCDiskCachePoolAccessManager.POOL_CONFIGURATION_PREFIX + poolName
+            + JDBCDiskCachePoolAccessManager.ATTRIBUTE_PREFIX;
+        props.put( prefix + ".url", url );
+        props.put( prefix + ".userName", userName );
+        props.put( prefix + ".password", password );
+        props.put( prefix + ".maxActive", String.valueOf( maxActive ) );
+        props.put( prefix + ".driverClassName", driverClassName );
+
+        JDBCDiskCachePoolAccessManager manager = JDBCDiskCachePoolAccessManager.getInstance();
+
+        // DO WORK
+        JDBCDiskCachePoolAccessAttributes result = manager.configurePoolAccessAttributes( poolName, props );
+
+        // VERIFY
+        assertEquals( "Wrong url value", url, result.getUrl() );
+        assertEquals( "Wrong userName value", userName, result.getUserName() );
+        assertEquals( "Wrong password value", password, result.getPassword() );
+        assertEquals( "Wrong maxActive value", maxActive, result.getMaxActive() );
+        assertEquals( "Wrong driverClassName value", driverClassName, result.getDriverClassName() );
+    }
+
+    /**
+     * Verify that we can get access.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetJDBCDiskCachePoolAccess_Simple()
+        throws Exception
+    {
+        // SETUP
+        String poolName = "testGetJDBCDiskCachePoolAccess_Simple";
+
+        String url = "jdbc:hsqldb:";
+        String userName = "sa";
+        String password = "";
+        int maxActive = 10;
+        String driverClassName = "org.hsqldb.jdbcDriver";
+
+        Properties props = new Properties();
+        String prefix = JDBCDiskCachePoolAccessManager.POOL_CONFIGURATION_PREFIX + poolName
+            + JDBCDiskCachePoolAccessManager.ATTRIBUTE_PREFIX;
+        props.put( prefix + ".url", url );
+        props.put( prefix + ".userName", userName );
+        props.put( prefix + ".password", password );
+        props.put( prefix + ".maxActive", String.valueOf( maxActive ) );
+        props.put( prefix + ".driverClassName", driverClassName );
+
+        JDBCDiskCachePoolAccessManager manager = JDBCDiskCachePoolAccessManager.getInstance();
+
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        String database = rafroot + "/cache_hsql_db";
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driverClassName ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, userName, password );
+        HsqlSetupTableUtil.setupTABLE( cConn, "JCSTESTTABLE_ACCESS" );
+
+        // DO WORK
+        JDBCDiskCachePoolAccess result = manager.getJDBCDiskCachePoolAccess( poolName, props );
+
+        // VERIFY
+        assertNotNull( "Should have an access class", result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheRemovalUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheRemovalUnitTest.java
new file mode 100644
index 0000000..e7aaff6
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheRemovalUnitTest.java
@@ -0,0 +1,175 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+/** Tests for the removal functionality. */
+public class JDBCDiskCacheRemovalUnitTest
+    extends TestCase
+{
+    /** db name -- set in system props */
+    private final String databaseName = "JCS_STORE_REMOVAL";
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        System.setProperty( "DATABASE_NAME", databaseName );
+        JCS.setConfigFilename( "/TestJDBCDiskCacheRemoval.ccf" );
+    }
+
+    /**
+     * Verify the fix for BUG JCS-20
+     * <p>
+     * Setup an hsql db. Add an item. Remove using partial key.
+     * @throws Exception
+     */
+    public void testPartialKeyRemoval_Good()
+        throws Exception
+    {
+        // SETUP
+        setupDatabase();
+
+        String keyPart1 = "part1";
+        String keyPart2 = "part2";
+        String region = "testCache1";
+        String data = "adfadsfasfddsafasasd";
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // DO WORK
+        jcs.put( keyPart1 + ":" + keyPart2, data );
+        Thread.sleep( 1000 );
+
+        // VERIFY
+        String resultBeforeRemove = jcs.get( keyPart1 + ":" + keyPart2 );
+        assertEquals( "Wrong result", data, resultBeforeRemove );
+
+        jcs.remove( keyPart1 + ":" );
+        String resultAfterRemove = jcs.get( keyPart1 + ":" + keyPart2 );
+        assertNull( "Should not have a result after removal.", resultAfterRemove );
+
+//        System.out.println( jcs.getStats() );
+    }
+
+    /**
+     * Create the database.
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     * @throws ClassNotFoundException
+     * @throws SQLException
+     */
+    private void setupDatabase()
+        throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
+    {
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        Properties p = new Properties();
+        String driver = p.getProperty( "driver", "org.hsqldb.jdbcDriver" );
+        String url = p.getProperty( "url", "jdbc:hsqldb:" );
+        String database = p.getProperty( "database", rafroot + "/JDBCDiskCacheRemovalUnitTest" );
+        String user = p.getProperty( "user", "sa" );
+        String password = p.getProperty( "password", "" );
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driver ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, user, password );
+
+        setupTABLE( cConn );
+    }
+
+    /**
+     * SETUP TABLE FOR CACHE
+     * <p>
+     * @param cConn
+     *
+     * @throws SQLException if database problems occur
+     */
+    private void setupTABLE( Connection cConn ) throws SQLException
+    {
+        boolean newT = true;
+
+        StringBuilder createSql = new StringBuilder();
+        createSql.append( "CREATE CACHED TABLE " + databaseName );
+        createSql.append( "( " );
+        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
+        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
+        createSql.append( "ELEMENT               BINARY, " );
+        createSql.append( "CREATE_TIME           TIMESTAMP, " );
+        createSql.append( "UPDATE_TIME_SECONDS   BIGINT, " );
+        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
+        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
+        createSql.append( "IS_ETERNAL            CHAR(1), " );
+        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
+        createSql.append( ");" );
+
+        Statement sStatement = cConn.createStatement();
+
+        try
+        {
+            sStatement.executeQuery( createSql.toString() );
+        }
+        catch ( SQLException e )
+        {
+            // FIXME: This is unreliable
+            if ( e.toString().indexOf( "already exists" ) != -1 )
+            {
+                newT = false;
+            }
+            else
+            {
+                throw e;
+            }
+        }
+        finally
+        {
+            sStatement.close();
+        }
+
+        String setupData[] = { "create index iKEY on " + databaseName + " (CACHE_KEY, REGION)" };
+
+        if ( newT )
+        {
+            for ( int i = 1; i < setupData.length; i++ )
+            {
+                try
+                {
+                    sStatement.executeQuery( setupData[i] );
+                }
+                catch ( SQLException e )
+                {
+                    System.out.println( "Exception: " + e );
+                }
+            }
+        } // end ifnew
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheSharedPoolUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheSharedPoolUnitTest.java
new file mode 100644
index 0000000..79f8311
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheSharedPoolUnitTest.java
@@ -0,0 +1,141 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Runs basic tests for the JDBC disk cache using a shared connection pool.
+ *<p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCacheSharedPoolUnitTest
+    extends TestCase
+{
+    /** Test setup */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestJDBCDiskCacheSharedPool.ccf" );
+    }
+
+    /**
+     * Test the basic JDBC disk cache functionality with a hsql backing.
+     * @throws Exception
+     */
+    public void testSimpleJDBCPutGetWithHSQL()
+        throws Exception
+    {
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        Properties p = new Properties();
+        String driver = p.getProperty( "driver", "org.hsqldb.jdbcDriver" );
+        String url = p.getProperty( "url", "jdbc:hsqldb:" );
+        String database = p.getProperty( "database", rafroot + "/cache_hsql_db_sharedpool" );
+        String user = p.getProperty( "user", "sa" );
+        String password = p.getProperty( "password", "" );
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driver ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, user, password );
+
+        HsqlSetupTableUtil.setupTABLE( cConn, "JCS_STORE_0" );
+
+        HsqlSetupTableUtil.setupTABLE( cConn, "JCS_STORE_1" );
+
+        runTestForRegion( "testCache1", 200 );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @param region Name of the region to access
+     * @param items
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region, int items )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+//        System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        Thread.sleep( 1000 );
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheShrinkUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheShrinkUnitTest.java
new file mode 100644
index 0000000..aaf9be2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheShrinkUnitTest.java
@@ -0,0 +1,279 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+/**
+ * Runs basic tests for the JDBC disk cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCacheShrinkUnitTest
+    extends TestCase
+{
+
+    /**
+     * Test setup
+     * <p>
+     * @throws ClassNotFoundException
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     * @throws SQLException
+     */
+    @Override
+    public void setUp()
+        throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException
+    {
+        JCS.setConfigFilename( "/TestJDBCDiskCacheShrink.ccf" );
+
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        Properties p = new Properties();
+        String driver = p.getProperty( "driver", "org.hsqldb.jdbcDriver" );
+        String url = p.getProperty( "url", "jdbc:hsqldb:" );
+        String database = p.getProperty( "database", rafroot + "/JDBCDiskCacheShrinkUnitTest" );
+        String user = p.getProperty( "user", "sa" );
+        String password = p.getProperty( "password", "" );
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driver ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, user, password );
+
+        setupTABLE( cConn );
+    }
+
+    /**
+     * Test the basic JDBC disk cache functionality with a hsql backing. Verify that items
+     * configured to expire after 1 second actually expire.
+     * <p>
+     * @throws Exception
+     */
+    public void testExpireInBackground()
+        throws Exception
+    {
+        String regionExpire = "expire1Second";
+        int items = 200;
+
+        CacheAccess<String, String> jcsExpire = JCS.getInstance( regionExpire );
+
+//        System.out.println( "BEFORE PUT \n" + jcsExpire.getStats() );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcsExpire.put( i + ":key", regionExpire + " data " + i );
+        }
+
+//        System.out.println( jcsExpire.getStats() );
+
+        // the shrinker is supposed to run every second
+        SleepUtil.sleepAtLeast( 3000 );
+
+//        System.out.println( jcsExpire.getStats() );
+
+        // Test that all items have been removed from the cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcsExpire.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Verify that those not scheduled to expire do not expire.
+     * <p>
+     * @throws CacheException
+     * @throws InterruptedException
+     */
+    public void testDidNotExpire()
+        throws CacheException, InterruptedException
+    {
+        String region = "expire100Second";
+        int items = 200;
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+//        System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Remove all the items
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Verify that eternal trumps max life.
+     * @throws CacheException
+     * @throws InterruptedException
+     */
+    public void testDidNotExpireEternal()
+        throws CacheException, InterruptedException
+    {
+        String region = "eternal";
+        int items = 200;
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+//        System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Remove all the items
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * SETUP TABLE FOR CACHE
+     * @param cConn
+     *
+     * @throws SQLException if database problems occur
+     */
+    void setupTABLE( Connection cConn ) throws SQLException
+    {
+        boolean newT = true;
+
+        StringBuilder createSql = new StringBuilder();
+        createSql.append( "CREATE CACHED TABLE JCS_STORE_SHRINK " );
+        createSql.append( "( " );
+        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
+        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
+        createSql.append( "ELEMENT               BINARY, " );
+        createSql.append( "CREATE_TIME           TIMESTAMP, " );
+        createSql.append( "UPDATE_TIME_SECONDS   BIGINT, " );
+        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
+        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
+        createSql.append( "IS_ETERNAL            CHAR(1), " );
+        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
+        createSql.append( ");" );
+
+        Statement sStatement = cConn.createStatement();
+
+        try
+        {
+            sStatement.executeQuery( createSql.toString() );
+        }
+        catch ( SQLException e )
+        {
+            if ( e.toString().indexOf( "already exists" ) != -1 )
+            {
+                newT = false;
+            }
+            else
+            {
+                throw e;
+            }
+        }
+        finally
+        {
+            sStatement.close();
+        }
+
+        String setupData[] = { "create index iKEY on JCS_STORE_SHRINK (CACHE_KEY, REGION)" };
+
+        if ( newT )
+        {
+            for ( int i = 1; i < setupData.length; i++ )
+            {
+                try
+                {
+                    sStatement.executeQuery( setupData[i] );
+                }
+                catch ( SQLException e )
+                {
+                    System.out.println( "Exception: " + e );
+                }
+            }
+        } // end ifnew
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheUnitTest.java
new file mode 100644
index 0000000..09349bf
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/JDBCDiskCacheUnitTest.java
@@ -0,0 +1,194 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Runs basic tests for the JDBC disk cache.
+ *<p>
+ * @author Aaron Smuts
+ */
+public class JDBCDiskCacheUnitTest
+    extends TestCase
+{
+    /** Test setup */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestJDBCDiskCache.ccf" );
+    }
+
+    /**
+     * Test the basic JDBC disk cache functionality with a hsql backing.
+     * @throws Exception
+     */
+    public void testSimpleJDBCPutGetWithHSQL()
+        throws Exception
+    {
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        Properties p = new Properties();
+        String driver = p.getProperty( "driver", "org.hsqldb.jdbcDriver" );
+        String url = p.getProperty( "url", "jdbc:hsqldb:" );
+        String database = p.getProperty( "database", rafroot + "/cache_hsql_db" );
+        String user = p.getProperty( "user", "sa" );
+        String password = p.getProperty( "password", "" );
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driver ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, user, password );
+
+        HsqlSetupTableUtil.setupTABLE( cConn, "JCS_STORE2" );
+
+        runTestForRegion( "testCache1", 200 );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @param region Name of the region to access
+     * @param items
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region, int items )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+//        System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        Thread.sleep( 1000 );
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Verfiy that it uses the pool access manager config.
+     * <p>
+     * @throws Exception
+     */
+    public void testInitializePoolAccess_withPoolName()
+        throws Exception
+    {
+        // SETUP
+        String poolName = "testInitializePoolAccess_withPoolName";
+
+        String url = "jdbc:hsqldb:";
+        String userName = "sa";
+        String password = "";
+        int maxActive = 10;
+        String driverClassName = "org.hsqldb.jdbcDriver";
+
+        Properties props = new Properties();
+        String prefix = JDBCDiskCachePoolAccessManager.POOL_CONFIGURATION_PREFIX + poolName
+            + JDBCDiskCachePoolAccessManager.ATTRIBUTE_PREFIX;
+        props.put( prefix + ".url", url );
+        props.put( prefix + ".userName", userName );
+        props.put( prefix + ".password", password );
+        props.put( prefix + ".maxActive", String.valueOf( maxActive ) );
+        props.put( prefix + ".driverClassName", driverClassName );
+
+        JDBCDiskCacheAttributes cattr = new JDBCDiskCacheAttributes();
+        cattr.setConnectionPoolName( poolName );
+
+        TableState tableState = new TableState( "JCSTESTTABLE_InitializePoolAccess" );
+        MockCompositeCacheManager compositeCacheManager = new MockCompositeCacheManager();
+        compositeCacheManager.setConfigurationProperties( props );
+
+        JDBCDiskCache<String, String> diskCache = new JDBCDiskCache<String, String>( cattr, tableState, compositeCacheManager );
+
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        String database = rafroot + "/cache_hsql_db";
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driverClassName ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, userName, password );
+
+        HsqlSetupTableUtil.setupTABLE( cConn, "JCSTESTTABLE_InitializePoolAccess" );
+
+        // DO WORK
+        JDBCDiskCachePoolAccess result = diskCache.initializePoolAccess( cattr, compositeCacheManager );
+
+        // VEIFY
+        assertNotNull( "Should have an access class", result );
+        assertEquals( "wrong name", poolName, result.getPoolName() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheConcurrentUnitTest.java
new file mode 100644
index 0000000..33d8957
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheConcurrentUnitTest.java
@@ -0,0 +1,175 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.hsql;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the indexed disk cache. This one uses three different regions for thre
+ * threads.
+ */
+public class HSQLDiskCacheConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory cache regions.
+     */
+    private static int items = 100;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     * @param testName
+     */
+    public HSQLDiskCacheConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * <p>
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { HSQLDiskCacheConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit. Uses ActiveTestSuite to run multiple tests concurrently.
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new HSQLDiskCacheConcurrentUnitTest( "testHSQLDiskCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion1" );
+            }
+        } );
+
+        suite.addTest( new HSQLDiskCacheConcurrentUnitTest( "testHSQLDiskCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion2" );
+            }
+        } );
+
+        suite.addTest( new HSQLDiskCacheConcurrentUnitTest( "testHSQLDiskCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion3" );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestHSQLDiskCacheConcurrent.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @param region Name of the region to access
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheUnitTest.java
new file mode 100644
index 0000000..d5bca8a
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/hsql/HSQLDiskCacheUnitTest.java
@@ -0,0 +1,189 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.hsql;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the indexed disk cache. This one uses three different regions for thre
+ * threads.
+ */
+public class HSQLDiskCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestHSQLDiskCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @throws Exception If an error occurs
+     */
+    public void testBasicPutRemove()
+        throws Exception
+    {
+        int items = 20;
+
+        String region = "testBasicPutRemove";
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        //SleepUtil.sleepAtLeast( 1000 );
+
+//        System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+
+        // Remove all the items
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Verify that remove all work son a region where it is not prohibited.
+     * <p>
+     * @throws CacheException
+     * @throws InterruptedException
+     */
+    public void testRemoveAll()
+        throws CacheException, InterruptedException
+    {
+        String region = "removeAllAllowed";
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        int items = 20;
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // a db thread could be updating when we call remove all?
+        // there was a race on remove all, an element may be put to disk after it is called even
+        // though the put
+        // was called before clear.
+        // I discovered it and removed it.
+        // Thread.sleep( 500 );
+
+//        System.out.println( jcs.getStats() );
+
+        jcs.clear();
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertNull( "value should be null key = [" + i + ":key] value = [" + value + "]", value );
+        }
+    }
+
+    /**
+     * Verify that remove all does not work on a region where it is prohibited.
+     * <p>
+     * @throws CacheException
+     * @throws InterruptedException
+     */
+    public void testRemoveAllProhibition()
+        throws CacheException, InterruptedException
+    {
+        String region = "noRemoveAll";
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+
+        int items = 20;
+
+        // Add items to cache
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        // a db thread could be updating the disk when
+        // Thread.sleep( 500 );
+
+//        System.out.println( jcs.getStats() );
+
+        jcs.clear();
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheHsqlBackedUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheHsqlBackedUnitTest.java
new file mode 100644
index 0000000..12d99bc
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheHsqlBackedUnitTest.java
@@ -0,0 +1,238 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Runs basic tests for the JDBC disk cache.
+ * @author Aaron Smuts
+ */
+public class MySQLDiskCacheHsqlBackedUnitTest
+    extends TestCase
+{
+    /**
+     * Creates the DB
+     * <p>
+     * @throws Exception
+     */
+    public MySQLDiskCacheHsqlBackedUnitTest()
+        throws Exception
+    {
+        super();
+        System.setProperty( "hsqldb.cache_scale", "8" );
+
+        String rafroot = "target";
+        Properties p = new Properties();
+        String driver = p.getProperty( "driver", "org.hsqldb.jdbcDriver" );
+        String url = p.getProperty( "url", "jdbc:hsqldb:" );
+        String database = p.getProperty( "database", rafroot + "/MySQLDiskCacheHsqlBackedUnitTest" );
+        String user = p.getProperty( "user", "sa" );
+        String password = p.getProperty( "password", "" );
+
+        new org.hsqldb.jdbcDriver();
+        Class.forName( driver ).newInstance();
+        Connection cConn = DriverManager.getConnection( url + database, user, password );
+
+        setupTABLE( cConn );
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestMySQLDiskCache.ccf" );
+    }
+
+    /**
+     * Test the basic JDBC disk cache functionality with a hsql backing.
+     * @throws Exception
+     */
+    public void testSimpleJDBCPutGetWithHSQL()
+        throws Exception
+    {
+        runTestForRegion( "testCache1", 200 );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more than the size of the
+     * memory cache, so items should spool to disk.
+     * <p>
+     * @param region Name of the region to access
+     * @param items
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region, int items )
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        //System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // Add items to cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+
+        //System.out.println( jcs.getStats() );
+        Thread.sleep( 1000 );
+        //System.out.println( jcs.getStats() );
+
+        // Test that all items are in cache
+        for ( int i = 0; i <= items; i++ )
+        {
+            String value = jcs.get( i + ":key" );
+
+            assertEquals( "key = [" + i + ":key] value = [" + value + "]", region + " data " + i, value );
+        }
+
+        // Test that getElements returns all the expected values
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i <= items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = jcs.getCacheElements( keys );
+        for ( int i = 0; i <= items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i <= items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", jcs.get( i + ":key" ) );
+        }
+    }
+
+    /**
+     * Test the basic JDBC disk cache functionality with a hsql backing.
+     * <p>
+     * @throws Exception
+     */
+    public void testPutGetMatchingWithHSQL()
+        throws Exception
+    {
+        // SETUP
+        int items = 200;
+        String region = "testCache2";
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+//        System.out.println( "BEFORE PUT \n" + jcs.getStats() );
+
+        // DO WORK
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", region + " data " + i );
+        }
+        Thread.sleep( 1000 );
+
+        Map<String, ICacheElement<String, String>> matchingResults = jcs.getMatchingCacheElements( "1.8.+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned", 10, matchingResults.size() );
+//        System.out.println( "matchingResults.keySet() " + matchingResults.keySet() );
+//        System.out.println( "\nAFTER TEST \n" + jcs.getStats() );
+    }
+
+    /**
+     * SETUP TABLE FOR CACHE
+     * @param cConn
+     */
+    void setupTABLE( Connection cConn ) throws SQLException
+    {
+        boolean newT = true;
+
+        StringBuilder createSql = new StringBuilder();
+        createSql.append( "CREATE CACHED TABLE JCS_STORE_MYSQL " );
+        createSql.append( "( " );
+        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
+        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
+        createSql.append( "ELEMENT               BINARY, " );
+        createSql.append( "CREATE_TIME           TIMESTAMP, " );
+        createSql.append( "UPDATE_TIME_SECONDS   BIGINT, " );
+        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
+        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
+        createSql.append( "IS_ETERNAL            CHAR(1), " );
+        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
+        createSql.append( ");" );
+
+        Statement sStatement = cConn.createStatement();
+
+        try
+        {
+            sStatement.executeQuery( createSql.toString() );
+            sStatement.close();
+        }
+        catch ( SQLException e )
+        {
+            if ( e.toString().indexOf( "already exists" ) != -1 )
+            {
+                newT = false;
+            }
+            else
+            {
+                // TODO figure out if it exists prior to trying to create it.
+                // log.error( "Problem creating table.", e );
+                throw e;
+            }
+        }
+
+        String setupData[] = { "create index iKEY on JCS_STORE_MYSQL (CACHE_KEY, REGION)" };
+
+        if ( newT )
+        {
+            for ( int i = 1; i < setupData.length; i++ )
+            {
+                try
+                {
+                    sStatement.executeQuery( setupData[i] );
+                }
+                catch ( SQLException e )
+                {
+                    System.out.println( "Exception: " + e );
+                }
+            }
+        } // end ifnew
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManagerUnitTest.java
new file mode 100644
index 0000000..7c4ce47
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheManagerUnitTest.java
@@ -0,0 +1,56 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** Unit tests for the manager */
+public class MySQLDiskCacheManagerUnitTest
+    extends TestCase
+{
+    /** Verify that the disk cache has the event logger */
+    public void testGetCache_normal()
+    {
+        // SETUP
+        String cacheName = "testGetCache_normal";
+        MySQLDiskCacheAttributes defaultCacheAttributes = new MySQLDiskCacheAttributes();
+        // Just use something that exists
+        defaultCacheAttributes.setDriverClassName( "org.hsqldb.jdbcDriver" );
+        defaultCacheAttributes.setDiskPath( "target/JDBCDiskCacheManagerUnitTest" );
+
+        ICacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        IElementSerializer elementSerializer = new MockElementSerializer();
+
+        MySQLDiskCacheManager manager = MySQLDiskCacheManager.getInstance( defaultCacheAttributes, CompositeCacheManager.getUnconfiguredInstance(), cacheEventLogger,
+                                                                           elementSerializer );
+
+        // DO WORK
+        MySQLDiskCache<String, String> cache = manager.getCache( cacheName );
+
+        // VERIFY
+        assertEquals( "wrong cacheEventLogger", cacheEventLogger, cache.getCacheEventLogger() );
+        assertEquals( "wrong elementSerializer", elementSerializer, cache.getElementSerializer() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheUnitTest.java
new file mode 100644
index 0000000..4de6b88
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLDiskCacheUnitTest.java
@@ -0,0 +1,69 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import java.sql.SQLException;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+/**
+ * Simple tests for the MySQLDisk Cache.
+ * <p>
+ * We will probably need to setup an hsql behind this, to test some of the pass through methods.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLDiskCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we simply return null on get if an optimization is in
+     * progress and the cache is configured to balk on optimization.
+     * <p>
+     * This is a bit tricky since we don't want to have to have a mysql instance
+     * running. Right now this doesn't really test much
+     * @throws SQLException
+     */
+    public void testBalkOnGet() throws SQLException
+    {
+        // SETUP
+        MySQLDiskCacheAttributes attributes = new MySQLDiskCacheAttributes();
+        String tableName = "JCS_TEST";
+        // Just use something that exists
+        attributes.setDriverClassName( "org.hsqldb.jdbcDriver" );
+        attributes.setTableName( tableName );
+        attributes.setBalkDuringOptimization( true );
+
+        TableState tableState = new TableState( tableName );
+        tableState.setState( TableState.OPTIMIZATION_RUNNING );
+
+        MySQLDiskCache<String, String> cache = new MySQLDiskCache<String, String>( attributes, tableState,
+        		CompositeCacheManager.getUnconfiguredInstance() );
+
+        // DO WORK
+        Object result = cache.processGet( "myKey" );
+
+        // VERIFY
+        assertNull( "The result should be null", result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizerManualTester.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizerManualTester.java
new file mode 100644
index 0000000..575cdd9
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/MySQLTableOptimizerManualTester.java
@@ -0,0 +1,83 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCachePoolAccessManager;
+import org.apache.commons.jcs.auxiliary.disk.jdbc.TableState;
+
+/**
+ * Hand run tests for the MySQL table optimizer.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MySQLTableOptimizerManualTester
+    extends TestCase
+{
+    /**
+     * Run the optimization against live a table.
+     * <p>
+     * @throws Exception
+     */
+    public void testBasicOptimization()
+        throws Exception
+    {
+        // SETUP
+        MySQLDiskCacheAttributes attributes = new MySQLDiskCacheAttributes();
+        attributes.setUserName( "java" );
+        attributes.setPassword( "letmein" );
+        attributes.setUrl( "jdbc:mysql://10.19.98.43:3306/flight_option_cache" );
+        attributes.setDriverClassName( "com.mysql.jdbc.Driver" );
+        String tableName = "JCS_STORE_FLIGHT_OPTION_ITINERARY";
+        attributes.setTableName( tableName );
+        TableState tableState = new TableState( tableName );
+
+        MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, JDBCDiskCachePoolAccessManager
+            .createPoolAccess( attributes ) );
+
+        // DO WORK
+        optimizer.optimizeTable();
+    }
+
+    /**
+     * Run the optimization against live a table.
+     * <p>
+     * @throws Exception
+     */
+    public void testBasicOptimizationUnknownTable()
+        throws Exception
+    {
+        // SETUP
+        MySQLDiskCacheAttributes attributes = new MySQLDiskCacheAttributes();
+        attributes.setUserName( "java" );
+        attributes.setPassword( "letmein" );
+        attributes.setUrl( "jdbc:mysql://10.19.98.43:3306/flight_option_cache" );
+        attributes.setDriverClassName( "com.mysql.jdbc.Driver" );
+        String tableName = "DOESNTEXIST";
+        attributes.setTableName( tableName );
+        TableState tableState = new TableState( tableName );
+
+        MySQLTableOptimizer optimizer = new MySQLTableOptimizer( attributes, tableState, JDBCDiskCachePoolAccessManager
+            .createPoolAccess( attributes ) );
+
+        // DO WORK
+        optimizer.optimizeTable();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParserUtilUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParserUtilUnitTest.java
new file mode 100644
index 0000000..bbc6846
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/jdbc/mysql/util/ScheduleParserUtilUnitTest.java
@@ -0,0 +1,128 @@
+package org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.util;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.util.Date;
+
+/**
+ * Unit tests for the schedule parser.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ScheduleParserUtilUnitTest
+    extends TestCase
+{
+
+    /**
+     * Verify that we get an exception and not a null pointer for null input.
+     */
+    public void testGetDatesWithNullInput()
+    {
+        try
+        {
+            ScheduleParser.createDatesForSchedule( null );
+
+            fail( "Should have thrown an exception" );
+        }
+        catch ( ScheduleFormatException e )
+        {
+            // expected
+        }
+    }
+
+    /**
+     * Verify that we get an exception and not a null pointer for null input.
+     */
+    public void testGetDateWithNullInput()
+    {
+        try
+        {
+            ScheduleParser.getDateForSchedule( null );
+
+            fail( "Should have thrown an exception" );
+        }
+        catch ( ScheduleFormatException e )
+        {
+            // expected
+        }
+    }
+
+    /**
+     * Verify that we get one date for one date.
+     * @throws ScheduleFormatException
+     */
+    public void testGetsDatesSingle()
+        throws ScheduleFormatException
+    {
+        String schedule = "12:34:56";
+        Date[] dates = ScheduleParser.createDatesForSchedule( schedule );
+
+        assertEquals( "Wrong number of dates returned.", 1, dates.length );
+    }
+    /**
+     * Verify that we get one date for one date.
+     * @throws ScheduleFormatException
+     */
+    public void testGetsDatesMultiple()
+        throws ScheduleFormatException
+    {
+        String schedule = "12:34:56,03:51:00,12:34:12";
+        Date[] dates = ScheduleParser.createDatesForSchedule( schedule );
+        //System.out.println( dates );
+        assertEquals( "Wrong number of dates returned.", 3, dates.length );
+    }
+
+    /**
+     * Verify that we get an exception for a single bad date in a list.
+     */
+    public void testGetDatesMalformedNoColon()
+    {
+        try
+        {
+            String schedule = "12:34:56,03:51:00,123234";
+            ScheduleParser.createDatesForSchedule( schedule );
+
+            fail( "Should have thrown an exception for a malformed date" );
+        }
+        catch ( ScheduleFormatException e )
+        {
+            // expected
+        }
+    }
+    /**
+     * Verify that we get an exception for a schedule that has a non numeric item.
+     */
+    public void testGetDatesMalformedNan()
+    {
+        try
+        {
+            String schedule = "12:34:56,03:51:00,aa:12:12";
+            ScheduleParser.createDatesForSchedule( schedule );
+
+            fail( "Should have thrown an exception for a malformed date" );
+        }
+        catch ( ScheduleFormatException e )
+        {
+            // expected
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacadeUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacadeUnitTest.java
new file mode 100644
index 0000000..cc90583
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/LateralCacheNoWaitFacadeUnitTest.java
@@ -0,0 +1,143 @@
+package org.apache.commons.jcs.auxiliary.lateral;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+
+/**
+ * Tests for LateralCacheNoWaitFacade.
+ */
+public class LateralCacheNoWaitFacadeUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we can remove an item.
+     */
+    public void testAddThenRemoveNoWait_InList()
+    {
+        // SETUP
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( "testCache1" );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        facade.addNoWait( noWait );
+
+        // VERIFY
+        assertTrue( "Should be in the list.", facade.containsNoWait( noWait ) );
+
+        // DO WORK
+        facade.removeNoWait( noWait );
+
+        // VERIFY
+        assertEquals( "Should have 0", 0, facade.noWaits.length );
+        assertFalse( "Should not be in the list. ", facade.containsNoWait( noWait ) );
+    }
+
+    /**
+     * Verify that we can remove an item.
+     */
+    public void testAddThenRemoveNoWait_InListSize2()
+    {
+        // SETUP
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( "testCache1" );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+        LateralCacheNoWait<String, String> noWait2 = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        facade.addNoWait( noWait );
+        facade.addNoWait( noWait2 );
+
+        // VERIFY
+        assertEquals( "Should have 2", 2, facade.noWaits.length );
+        assertTrue( "Should be in the list.", facade.containsNoWait( noWait ) );
+        assertTrue( "Should be in the list.", facade.containsNoWait( noWait2 ) );
+
+        // DO WORK
+        facade.removeNoWait( noWait );
+
+        // VERIFY
+        assertEquals( "Should only have 1", 1, facade.noWaits.length );
+        assertFalse( "Should not be in the list. ", facade.containsNoWait( noWait ) );
+        assertTrue( "Should be in the list.", facade.containsNoWait( noWait2 ) );
+    }
+
+    /**
+     * Verify that we can remove an item.
+     */
+    public void testAdd_InList()
+    {
+        // SETUP
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( "testCache1" );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        facade.addNoWait( noWait );
+        facade.addNoWait( noWait );
+
+        // VERIFY
+        assertTrue( "Should be in the list.", facade.containsNoWait( noWait ) );
+        assertEquals( "Should only have 1", 1, facade.noWaits.length );
+    }
+
+    /**
+     * Verify that we can remove an item.
+     */
+    public void testAddThenRemoveNoWait_NotInList()
+    {
+        // SETUP
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( "testCache1" );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        facade.removeNoWait( noWait );
+
+        // VERIFY
+        assertFalse( "Should not be in the list.", facade.containsNoWait( noWait ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/http/broadcast/LateralCacheTester.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/http/broadcast/LateralCacheTester.java
new file mode 100644
index 0000000..2326dff
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/http/broadcast/LateralCacheTester.java
@@ -0,0 +1,61 @@
+package org.apache.commons.jcs.auxiliary.lateral.http.broadcast;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Aaron Smuts
+ * @version 1.0
+ */
+public class LateralCacheTester
+{
+
+    //    /** Description of the Method */
+    //    public static void main( String args[] )
+    //    {
+    //
+    //        String[] servers = {"10.1.17.109", "10.1.17.108"};
+    //
+    //        try
+    //        {
+    //
+    //            //for ( int i=0; i <100; i++ ) {
+    //            String val = "test object value";
+    //            LateralCacheThread dct = new LateralCacheThread( "testTable", "testkey",
+    // val, servers );
+    //            dct.setPriority( Thread.NORM_PRIORITY - 1 );
+    //            dct.start();
+    //
+    //            String val2 = "test object value2";
+    //            LateralCacheThread dct2 = new LateralCacheThread( "testTable", "testkey",
+    // val, servers );
+    //            dct2.setPriority( Thread.NORM_PRIORITY - 1 );
+    //            dct2.start();
+    //            //}
+    //
+    //        }
+    //        catch ( Exception e )
+    //        {
+    //            System.out.println( e.toString() );
+    //        }
+    //
+    //    }
+
+}
+// end class
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPConcurrentRandomTestUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPConcurrentRandomTestUtil.java
new file mode 100644
index 0000000..1f1ae8b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPConcurrentRandomTestUtil.java
@@ -0,0 +1,195 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.Random;
+
+/**
+ * @author Aaron Smuts
+ */
+public class LateralTCPConcurrentRandomTestUtil
+    extends TestCase
+{
+    /** Should we write out. */
+    private static boolean isSysOut = false;
+    //private static boolean isSysOut = true;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public LateralTCPConcurrentRandomTestUtil( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestTCPLateralCacheConcurrent.ccf" );
+    }
+
+    /**
+     * Randomly adds items to cache, gets them, and removes them. The range
+     * count is more than the size of the memory cache, so items should spool to
+     * disk.
+     * <p>
+     * @param region
+     *            Name of the region to access
+     * @param range
+     * @param numOps
+     * @param testNum
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region, int range, int numOps, int testNum )
+        throws Exception
+    {
+        boolean show = true;//false;
+
+        CacheAccess<String, String> cache = JCS.getInstance( region );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1103 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:1102" );
+
+        // this service will put and remove using the lateral to
+        // the cache instance above
+        // the cache thinks it is different since the listenerid is different
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        try
+        {
+            for ( int i = 1; i < numOps; i++ )
+            {
+                Random ran = new Random( i );
+                int n = ran.nextInt( 4 );
+                int kn = ran.nextInt( range );
+                String key = "key" + kn;
+                if ( n == 1 )
+                {
+                    ICacheElement<String, String> element = new CacheElement<String, String>( region, key, region + ":data" + i
+                        + " junk asdfffffffadfasdfasf " + kn + ":" + n );
+                    service.update( element );
+                    if ( show )
+                    {
+                        p( "put " + key );
+                    }
+                }
+                /**/
+                else if ( n == 2 )
+                {
+                    service.remove( region, key );
+                    if ( show )
+                    {
+                        p( "removed " + key );
+                    }
+                }
+                /**/
+                else
+                {
+                    // slightly greater chance of get
+                    try
+                    {
+                        Object obj = service.get( region, key );
+                        if ( show && obj != null )
+                        {
+                            p( obj.toString() );
+                        }
+                    }
+                    catch ( Exception e )
+                    {
+                        // consider failing, some timeouts are expected
+                        e.printStackTrace();
+                    }
+                }
+
+                if ( i % 100 == 0 )
+                {
+                    p( cache.getStats() );
+                }
+
+            }
+            p( "Finished random cycle of " + numOps );
+        }
+        catch ( Exception e )
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+            throw e;
+        }
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        String key = "testKey" + testNum;
+        String data = "testData" + testNum;
+        jcs.put( key, data );
+        String value = jcs.get( key );
+        assertEquals( "Couldn't put normally.", data, value );
+
+        // make sure the items we can find are in the correct region.
+        for ( int i = 1; i < numOps; i++ )
+        {
+            String keyL = "key" + i;
+            String dataL = jcs.get( keyL );
+            if ( dataL != null )
+            {
+                assertTrue( "Incorrect region detected.", dataL.startsWith( region ) );
+            }
+
+        }
+
+        //Thread.sleep( 1000 );
+
+        //ICacheElement<String, String> element = new CacheElement( region, "abc", "testdata");
+        //service.update( element );
+
+        //Thread.sleep( 2500 );
+        // could be too mcuh going on right now to get ti through, sot he test
+        // might fail.
+        //String value2 = (String) jcs.get( "abc" );
+        //assertEquals( "Couldn't put laterally, could be too much traffic in
+        // queue.", "testdata", value2 );
+
+    }
+
+    /**
+     * @param s string to print
+     */
+    public static void p( String s )
+    {
+        if ( isSysOut )
+        {
+            System.out.println( s );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerUnitTest.java
new file mode 100644
index 0000000..aafba54
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPDiscoveryListenerUnitTest.java
@@ -0,0 +1,284 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCache;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWait;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheNoWaitFacade;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.socket.tcp.behavior.ITCPLateralCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.logging.MockCacheEventLogger;
+import org.apache.commons.jcs.utils.discovery.DiscoveredService;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.util.ArrayList;
+
+/** Test for the listener that observers UDP discovery events. */
+public class LateralTCPDiscoveryListenerUnitTest
+    extends TestCase
+{
+    /** the listener */
+    private LateralTCPDiscoveryListener listener;
+
+    /** The cache manager. */
+    private ICompositeCacheManager cacheMgr;
+
+    /** The event logger. */
+    protected MockCacheEventLogger cacheEventLogger;
+
+    /** The serializer. */
+    protected IElementSerializer elementSerializer;
+
+    /** Create the listener for testing */
+    @Override
+    protected void setUp() throws Exception
+    {
+        cacheMgr = CompositeCacheManager.getInstance();
+        cacheEventLogger = new MockCacheEventLogger();
+        elementSerializer = new StandardSerializer();
+
+        listener = new LateralTCPDiscoveryListener( cacheMgr, cacheEventLogger, elementSerializer );
+    }
+
+    /**
+     * Add a no wait facade.
+     */
+    public void testAddNoWaitFacade_NotInList()
+    {
+        // SETUP
+        String cacheName = "testAddNoWaitFacade_NotInList";
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+
+        // DO WORK
+        listener.addNoWaitFacade( cacheName, facade );
+
+        // VERIFY
+        assertTrue( "Should have the facade.", listener.containsNoWaitFacade( cacheName ) );
+    }
+
+    /**
+     * Add a no wait to a known facade.
+     */
+    public void testAddNoWait_FacadeInList()
+    {
+        // SETUP
+        String cacheName = "testAddNoWaitFacade_FacadeInList";
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+        listener.addNoWaitFacade( cacheName, facade );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        boolean result = listener.addNoWait( noWait );
+
+        // VERIFY
+        assertTrue( "Should have added the no wait.", result );
+    }
+
+    /**
+     * Add a no wait from an unknown facade.
+     */
+    public void testAddNoWait_FacadeNotInList()
+    {
+        // SETUP
+        String cacheName = "testAddNoWaitFacade_FacadeInList";
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        boolean result = listener.addNoWait( noWait );
+
+        // VERIFY
+        assertFalse( "Should not have added the no wait.", result );
+    }
+
+    /**
+     * Remove a no wait from an unknown facade.
+     */
+    public void testRemoveNoWait_FacadeNotInList()
+    {
+        // SETUP
+        String cacheName = "testRemoveNoWaitFacade_FacadeNotInList";
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        boolean result = listener.removeNoWait( noWait );
+
+        // VERIFY
+        assertFalse( "Should not have removed the no wait.", result );
+    }
+
+    /**
+     * Remove a no wait from a known facade.
+     */
+    public void testRemoveNoWait_FacadeInList_NoWaitNot()
+    {
+        // SETUP
+        String cacheName = "testAddNoWaitFacade_FacadeInList";
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+        listener.addNoWaitFacade( cacheName, facade );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+
+        // DO WORK
+        boolean result = listener.removeNoWait( noWait );
+
+        // VERIFY
+        assertFalse( "Should not have removed the no wait.", result );
+    }
+
+    /**
+     * Remove a no wait from a known facade.
+     */
+    public void testRemoveNoWait_FacadeInList_NoWaitIs()
+    {
+        // SETUP
+        String cacheName = "testRemoveNoWaitFacade_FacadeInListNoWaitIs";
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+        listener.addNoWaitFacade( cacheName, facade );
+
+        LateralCache<String, String> cache = new LateralCache<String, String>( cattr );
+        LateralCacheNoWait<String, String> noWait = new LateralCacheNoWait<String, String>( cache );
+        listener.addNoWait( noWait );
+
+        // DO WORK
+        boolean result = listener.removeNoWait( noWait );
+
+        // VERIFY
+        assertTrue( "Should have removed the no wait.", result );
+    }
+
+    /**
+     * Add a no wait to a known facade.
+     */
+    public void testAddDiscoveredService_FacadeInList_NoWaitNot()
+    {
+        // SETUP
+        String cacheName = "testAddDiscoveredService_FacadeInList_NoWaitNot";
+
+        ArrayList<String> cacheNames = new ArrayList<String>();
+        cacheNames.add( cacheName );
+
+        DiscoveredService service = new DiscoveredService();
+        service.setCacheNames( cacheNames );
+        service.setServiceAddress( "localhost" );
+        service.setServicePort( 9999 );
+
+        // since the no waits are compared by object equality, I have to do this
+        // TODO add an equals method to the noWait.  the problem if is figuring out what to compare.
+        ITCPLateralCacheAttributes lca = new TCPLateralCacheAttributes();
+        lca.setTransmissionType( LateralCacheAttributes.Type.TCP );
+        lca.setTcpServer( service.getServiceAddress() + ":" + service.getServicePort() );
+        LateralTCPCacheManager lcm = LateralTCPCacheManager.getInstance( lca, cacheMgr, cacheEventLogger,
+                                                                         elementSerializer );
+        LateralCacheNoWait<String, String> noWait = lcm.getCache( cacheName );
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+        listener.addNoWaitFacade( cacheName, facade );
+
+        // DO WORK
+        listener.addDiscoveredService( service );
+
+        // VERIFY
+        assertTrue( "Should have no wait.", listener.containsNoWait( cacheName, noWait ) );
+    }
+
+    /**
+     * Remove a no wait from a known facade.
+     */
+    public void testRemoveDiscoveredService_FacadeInList_NoWaitIs()
+    {
+        // SETUP
+        String cacheName = "testRemoveDiscoveredService_FacadeInList_NoWaitIs";
+
+        ArrayList<String> cacheNames = new ArrayList<String>();
+        cacheNames.add( cacheName );
+
+        DiscoveredService service = new DiscoveredService();
+        service.setCacheNames( cacheNames );
+        service.setServiceAddress( "localhost" );
+        service.setServicePort( 9999 );
+
+        // since the no waits are compared by object equality, I have to do this
+        // TODO add an equals method to the noWait.  the problem if is figuring out what to compare.
+        ITCPLateralCacheAttributes lca = new TCPLateralCacheAttributes();
+        lca.setTransmissionType( LateralCacheAttributes.Type.TCP );
+        lca.setTcpServer( service.getServiceAddress() + ":" + service.getServicePort() );
+        LateralTCPCacheManager lcm = LateralTCPCacheManager.getInstance( lca, cacheMgr, cacheEventLogger,
+                                                                         elementSerializer );
+        LateralCacheNoWait<String, String> noWait = lcm.getCache( cacheName );
+
+        @SuppressWarnings("unchecked")
+        LateralCacheNoWait<String, String>[] noWaits = new LateralCacheNoWait[0];
+        ILateralCacheAttributes cattr = new LateralCacheAttributes();
+        cattr.setCacheName( cacheName );
+        LateralCacheNoWaitFacade<String, String> facade = new LateralCacheNoWaitFacade<String, String>( null, noWaits, cattr );
+        listener.addNoWaitFacade( cacheName, facade );
+        listener.addDiscoveredService( service );
+
+        // DO WORK
+        listener.removeDiscoveredService( service );
+
+        // VERIFY
+        assertFalse( "Should not have no wait.", listener.containsNoWait( cacheName, noWait ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPFilterRemoveHashCodeUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPFilterRemoveHashCodeUnitTest.java
new file mode 100644
index 0000000..90203f2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPFilterRemoveHashCodeUnitTest.java
@@ -0,0 +1,192 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.Serializable;
+
+/**
+ * @author Aaron Smuts
+ */
+public class LateralTCPFilterRemoveHashCodeUnitTest
+    extends TestCase
+{
+    /** Does the test print to system out. */
+    private static boolean isSysOut = false;
+
+    /** The port the server will listen to. */
+    private final int serverPort = 2001;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public LateralTCPFilterRemoveHashCodeUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        System.setProperty( "jcs.auxiliary.LTCP.attributes.TcpServers", "localhost:" + serverPort );
+        JCS.setConfigFilename( "/TestTCPLateralRemoveFilter.ccf" );
+    }
+
+    /**
+     *
+     * @throws Exception
+     */
+    public void test()
+        throws Exception
+    {
+        this.runTestForRegion( "region1", 200, 1 );
+    }
+
+    /**
+     * This tests issues tons of puts. It also check to see that a key that was
+     * put in was removed by the clients remove command.
+     *
+     * @param region
+     *            Name of the region to access
+     * @param numOps
+     * @param testNum
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region, int numOps, int testNum )
+        throws Exception
+    {
+        CacheAccess<String, Serializable> cache = JCS.getInstance( region );
+
+        Thread.sleep( 100 );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1102 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:" + serverPort );
+        lattr2.setIssueRemoveOnPut( true );
+        // should still try to remove
+        lattr2.setAllowPut( false );
+
+        // this service will put and remove using the lateral to
+        // the cache instance above
+        // the cache thinks it is different since the listenerid is different
+        LateralTCPService<String, Serializable> service = new LateralTCPService<String, Serializable>( lattr2 );
+        service.setListenerId( 123456 );
+
+        String keyToBeRemovedOnPut = "test1";
+
+        String keyToNotBeRemovedOnPut = "test2";
+
+        Serializable dataToPassHashCodeCompare = new Serializable()
+        {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public int hashCode()
+            {
+                return 1;
+            }
+        };
+        //String dataToPassHashCodeCompare = "this should be the same and not
+        // get removed.";
+        //p( "dataToPassHashCodeCompare hashcode = " + +
+        // dataToPassHashCodeCompare.hashCode() );
+
+        cache.put( keyToBeRemovedOnPut, "this should get removed." );
+        ICacheElement<String, Serializable> element1 = new CacheElement<String, Serializable>( region, keyToBeRemovedOnPut, region
+            + ":data-this shouldn't get there" );
+        service.update( element1 );
+
+        cache.put( keyToNotBeRemovedOnPut, dataToPassHashCodeCompare );
+        ICacheElement<String, Serializable> element2 = new CacheElement<String, Serializable>( region, keyToNotBeRemovedOnPut, dataToPassHashCodeCompare );
+        service.update( element2 );
+
+        /*
+         * try { for ( int i = 1; i < numOps; i++ ) { Random ran = new Random( i );
+         * int n = ran.nextInt( 4 ); int kn = ran.nextInt( range ); String key =
+         * "key" + kn;
+         *
+         * ICacheElement<String, String> element = new CacheElement( region, key, region +
+         * ":data" + i + " junk asdfffffffadfasdfasf " + kn + ":" + n );
+         * service.update( element ); if ( show ) { p( "put " + key ); }
+         *
+         * if ( i % 100 == 0 ) { System.out.println( cache.getStats() ); }
+         *  } p( "Finished cycle of " + numOps ); } catch ( Exception e ) { p(
+         * e.toString() ); e.printStackTrace( System.out ); throw e; }
+         */
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        String key = "testKey" + testNum;
+        String data = "testData" + testNum;
+        jcs.put( key, data );
+        String value = jcs.get( key );
+        assertEquals( "Couldn't put normally.", data, value );
+
+        // make sure the items we can find are in the correct region.
+        for ( int i = 1; i < numOps; i++ )
+        {
+            String keyL = "key" + i;
+            String dataL = jcs.get( keyL );
+            if ( dataL != null )
+            {
+                assertTrue( "Incorrect region detected.", dataL.startsWith( region ) );
+            }
+
+        }
+
+        Thread.sleep( 200 );
+
+        Object testObj1 = cache.get( keyToBeRemovedOnPut );
+        p( "test object1 = " + testObj1 );
+        assertNull( "The test object should have been remvoed by a put.", testObj1 );
+
+        Object testObj2 = cache.get( keyToNotBeRemovedOnPut );
+        p( "test object2 = " + testObj2 + " hashCode = " );
+        if ( testObj2 != null )
+        {
+            p( "test2 hashcode = " + +testObj2.hashCode() );
+        }
+        assertNotNull( "This should not have been removed, since the hascode were the same.", testObj2 );
+
+    }
+
+    /**
+     * @param s String to print
+     */
+    public static void p( String s )
+    {
+        if ( isSysOut )
+        {
+            System.out.println( s );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPIssueRemoveOnPutUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPIssueRemoveOnPutUnitTest.java
new file mode 100644
index 0000000..6a11be0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPIssueRemoveOnPutUnitTest.java
@@ -0,0 +1,225 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.util.Random;
+
+/**
+ * Tests the issue remove on put fuctionality.
+ * @author asmuts
+ */
+public class LateralTCPIssueRemoveOnPutUnitTest
+    extends TestCase
+{
+    /** Should log data go to system out. */
+    private static boolean isSysOut = false;
+
+    /** The port the server will listen to. */
+    private final int serverPort = 1118;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     * <p>
+     * @param testName
+     */
+    public LateralTCPIssueRemoveOnPutUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        System.setProperty( "jcs.auxiliary.LTCP.attributes.TcpServers", "localhost:" + serverPort );
+
+        JCS.setConfigFilename( "/TestTCPLateralIssueRemoveCache.ccf" );
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testPutLocalPutRemoteGetBusyVerifyRemoved()
+        throws Exception
+    {
+        this.runTestForRegion( "region1", 1, 200, 1 );
+    }
+
+    /**
+     * Verify that a standard put works. Get the cache configured from a file. Create a tcp service
+     * to talk to that cache. Put via the service. Verify that the cache got the data.
+     * <p>
+     * @throws Exception
+     */
+    public void testStandardPut()
+        throws Exception
+    {
+        String region = "region1";
+
+        CacheAccess<String, String> cache = JCS.getInstance( region );
+
+        Thread.sleep( 100 );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1102 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:" + serverPort );
+        lattr2.setIssueRemoveOnPut( false );
+        // should still try to remove
+        // lattr2.setAllowPut( false );
+
+        // Using the lateral, this service will put to and remove from
+        // the cache instance above.
+        // The cache thinks it is different since the listenerid is different
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        String keyToBeRemovedOnPut = "test1_notremoved";
+
+        ICacheElement<String, String> element1 = new CacheElement<String, String>( region, keyToBeRemovedOnPut, region
+            + ":data-this shouldn't get removed, it should get to the cache." );
+        service.update( element1 );
+
+        Thread.sleep( 1000 );
+
+        Object testObj = cache.get( keyToBeRemovedOnPut );
+        p( "testStandardPut, test object = " + testObj );
+        assertNotNull( "The test object should not have been removed by a put.", testObj );
+    }
+
+    /**
+     * This tests issues tons of puts. It also check to see that a key that was put in was removed
+     * by the clients remove command.
+     * <p>
+     * @param region Name of the region to access
+     * @param range
+     * @param numOps
+     * @param testNum
+     * @throws Exception If an error occurs
+     */
+    public void runTestForRegion( String region, int range, int numOps, int testNum )
+        throws Exception
+    {
+
+        boolean show = false;
+
+        CacheAccess<String, String> cache = JCS.getInstance( region );
+
+        Thread.sleep( 100 );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1102 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:" + serverPort );
+        lattr2.setIssueRemoveOnPut( true );
+        // should still try to remove
+        lattr2.setAllowPut( false );
+
+        // Using the lateral, this service will put to and remove from
+        // the cache instance above.
+        // The cache thinks it is different since the listenerid is different
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        String keyToBeRemovedOnPut = "test1";
+        cache.put( keyToBeRemovedOnPut, "this should get removed." );
+
+        ICacheElement<String, String> element1 = new CacheElement<String, String>( region, keyToBeRemovedOnPut, region
+            + ":data-this shouldn't get there" );
+        service.update( element1 );
+
+        try
+        {
+            for ( int i = 1; i < numOps; i++ )
+            {
+                Random ran = new Random( i );
+                int n = ran.nextInt( 4 );
+                int kn = ran.nextInt( range );
+                String key = "key" + kn;
+
+                ICacheElement<String, String> element = new CacheElement<String, String>( region, key, region + ":data" + i
+                    + " junk asdfffffffadfasdfasf " + kn + ":" + n );
+                service.update( element );
+                if ( show )
+                {
+                    p( "put " + key );
+                }
+
+                if (show && i % 100 == 0 )
+                {
+                    System.out.println( cache.getStats() );
+                }
+
+            }
+            p( "Finished cycle of " + numOps );
+        }
+        catch ( Exception e )
+        {
+            p( e.toString() );
+            e.printStackTrace( System.out );
+            throw e;
+        }
+
+        CacheAccess<String, String> jcs = JCS.getInstance( region );
+        String key = "testKey" + testNum;
+        String data = "testData" + testNum;
+        jcs.put( key, data );
+        String value = jcs.get( key );
+        assertEquals( "Couldn't put normally.", data, value );
+
+        // make sure the items we can find are in the correct region.
+        for ( int i = 1; i < numOps; i++ )
+        {
+            String keyL = "key" + i;
+            String dataL = jcs.get( keyL );
+            if ( dataL != null )
+            {
+                assertTrue( "Incorrect region detected.", dataL.startsWith( region ) );
+            }
+
+        }
+
+        Thread.sleep( 200 );
+
+        Object testObj = cache.get( keyToBeRemovedOnPut );
+        p( "runTestForRegion, test object = " + testObj );
+        assertNull( "The test object should have been removed by a put.", testObj );
+
+    }
+
+    /**
+     * @param s String to be printed
+     */
+    public static void p( String s )
+    {
+        if ( isSysOut )
+        {
+            System.out.println( s );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPNoDeadLockConcurrentTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPNoDeadLockConcurrentTest.java
new file mode 100644
index 0000000..8019f85
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/LateralTCPNoDeadLockConcurrentTest.java
@@ -0,0 +1,146 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+/**
+ * Test which exercises the tcp lateral cache. Runs two threads against the
+ * same region and two against other regions.
+ */
+public class LateralTCPNoDeadLockConcurrentTest
+    extends TestCase
+{
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public LateralTCPNoDeadLockConcurrentTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { LateralTCPNoDeadLockConcurrentTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+
+        System.setProperty( "jcs.auxiliary.LTCP.attributes.PutOnlyMode", "false" );
+
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new LateralTCPConcurrentRandomTestUtil( "testLateralTCPCache1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "region1", 1, 200, 1 );
+            }
+        } );
+
+        suite.addTest( new LateralTCPConcurrentRandomTestUtil( "testLateralTCPCache2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "region2", 10000, 12000, 2 );
+            }
+        } );
+
+        suite.addTest( new LateralTCPConcurrentRandomTestUtil( "testLateralTCPCache3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "region3", 10000, 12000, 3 );
+            }
+        } );
+
+        suite.addTest( new LateralTCPConcurrentRandomTestUtil( "testLateralTCPCache4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "region3", 10000, 13000, 4 );
+            }
+        } );
+
+        suite.addTest( new LateralTCPConcurrentRandomTestUtil( "testLateralTCPCache5" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "region4", 10000, 11000, 5 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestTCPLateralCacheConcurrent.ccf" );
+    }
+
+    /**
+     * Test tearDown. Dispose of the cache.
+     */
+    @Override
+    public void tearDown()
+    {
+        try
+        {
+            CompositeCacheManager cacheMgr = CompositeCacheManager.getInstance();
+            cacheMgr.shutDown();
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/MockLateralCache.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/MockLateralCache.java
new file mode 100644
index 0000000..8c8e833
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/MockLateralCache.java
@@ -0,0 +1,125 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.lateral.LateralCache;
+import org.apache.commons.jcs.auxiliary.lateral.behavior.ILateralCacheAttributes;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * For testing things that need a lateral cache
+ */
+public class MockLateralCache<K extends Serializable, V extends Serializable>
+    extends LateralCache<K, V>
+    implements ICache<K, V>
+{
+    /** junk */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @param cattr
+     */
+    protected MockLateralCache( ILateralCacheAttributes cattr )
+    {
+        super( cattr );
+    }
+
+    /**
+     * Nothing.
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    protected void processUpdate( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        // nothing
+    }
+
+    /**
+     * @param key
+     * @return ICacheElement
+     * @throws IOException
+     */
+    @Override
+    protected ICacheElement<K, V> processGet( K key )
+        throws IOException
+    {
+        return null;
+    }
+
+    /**
+     * @param key
+     * @return false
+     * @throws IOException
+     */
+    @Override
+    protected boolean processRemove( K key )
+        throws IOException
+    {
+        return false;
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void processRemoveAll()
+        throws IOException
+    {
+        //nothing
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void processDispose()
+        throws IOException
+    {
+        // nothing
+    }
+
+    /** @return 0 */
+    @Override
+    public int getSize()
+    {
+        return 0;
+    }
+
+    /** @return 0 */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return CacheStatus.ALIVE;
+    }
+
+    /** @return String */
+    @Override
+    public String getStats()
+    {
+        return null;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TestTCPLateralUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TestTCPLateralUnitTest.java
new file mode 100644
index 0000000..eeaa8ee
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/lateral/socket/tcp/TestTCPLateralUnitTest.java
@@ -0,0 +1,367 @@
+package org.apache.commons.jcs.auxiliary.lateral.socket.tcp;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes;
+import org.apache.commons.jcs.auxiliary.lateral.LateralCommand;
+import org.apache.commons.jcs.auxiliary.lateral.LateralElementDescriptor;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Basic unit tests for the sending and receiving portions of the lateral cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class TestTCPLateralUnitTest
+    extends TestCase
+{
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestTCPLateralCache.ccf" );
+    }
+
+    /**
+     * Make sure we can send a bunch to the listener. This would be better if we could plugin a Mock
+     * CacheManger. The listener will instantiate it on its own. We have to configure one before
+     * that.
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleSend()
+        throws Exception
+    {
+        // SETUP
+        // force initialization
+        JCS.getInstance( "test" );
+
+        TCPLateralCacheAttributes lac = new TCPLateralCacheAttributes();
+        lac.setTransmissionType( LateralCacheAttributes.Type.TCP );
+        lac.setTcpServer( "localhost" + ":" + 8111 );
+        lac.setTcpListenerPort( 8111 );
+
+        ICompositeCacheManager cacheMgr = CompositeCacheManager.getInstance();
+
+        // start the listener
+        LateralTCPListener<String, String> listener = LateralTCPListener.getInstance( lac, cacheMgr );
+
+        // send to the listener
+        LateralTCPSender lur = new LateralTCPSender( lac );
+
+        // DO WORK
+        int numMes = 10;
+        for ( int i = 0; i < numMes; i++ )
+        {
+            String message = "adsfasasfasfasdasf";
+            CacheElement<String, String> ce = new CacheElement<String, String>( "test", "test", message );
+            LateralElementDescriptor<String, String> led = new LateralElementDescriptor<String, String>( ce );
+            led.command = LateralCommand.UPDATE;
+            led.requesterId = 1;
+            lur.send( led );
+        }
+
+        SleepUtil.sleepAtLeast( numMes * 3 );
+
+        // VERIFY
+        assertEquals( "Should have received " + numMes + " by now.", numMes, listener.getPutCnt() );
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testReceive()
+        throws Exception
+    {
+        // VERIFY
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1101 );
+        lattr.setTransmissionTypeName( "TCP" );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+//        System.out.println( "mock cache = " + cacheMgr.getCache( "test" ) );
+
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1102 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:1101" );
+
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        // DO WORK
+        int cnt = 100;
+        for ( int i = 0; i < cnt; i++ )
+        {
+            ICacheElement<String, String> element = new CacheElement<String, String>( "test", "key" + i, "value1" );
+            service.update( element );
+        }
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+        // VERIFY
+        assertEquals( "Didn't get the correct number", cnt, cacheMgr.getCache().getUpdateCount() );
+    }
+
+    /**
+     * Send objects with the same key but different values.
+     * <p>
+     * @throws Exception
+     */
+    public void testSameKeyDifferentObject()
+        throws Exception
+    {
+        // SETUP
+        // setup a listener
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1103 );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        CompositeCache<String, String> cache = cacheMgr.getCache( "test" );
+//        System.out.println( "mock cache = " + cache );
+
+        // get the listener started
+        // give it our mock cache manager
+        //LateralTCPListener listener = (LateralTCPListener)
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        // setup a service to talk to the listener started above.
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1104 );
+        lattr2.setTcpServer( "localhost:1103" );
+
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( "test", "key", "value1" );
+        service.update( element );
+
+        SleepUtil.sleepAtLeast( 300 );
+
+        ICacheElement<String, String> element2 = new CacheElement<String, String>( "test", "key", "value2" );
+        service.update( element2 );
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+        // VERIFY
+        ICacheElement<String, String> cacheElement = cache.get( "key" );
+        assertEquals( "Didn't get the correct object "+ cacheElement, element2.getVal(), cacheElement.getVal() );
+    }
+
+    /**
+     * Send objects with the same key but different values.
+     * <p>
+     * @throws Exception
+     */
+    public void testSameKeyObjectDifferentValueObject()
+        throws Exception
+    {
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1105 );
+        lattr.setTransmissionTypeName( "TCP" );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        CompositeCache<String, String> cache = cacheMgr.getCache( "test" );
+//        System.out.println( "mock cache = " + cache );
+
+        // get the listener started
+        // give it our mock cache manager
+        //LateralTCPListener listener = (LateralTCPListener)
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1106 );
+        lattr2.setTransmissionTypeName( "TCP" );
+        lattr2.setTcpServer( "localhost:1105" );
+
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        // DO WORK
+        String key = "key";
+        ICacheElement<String, String> element = new CacheElement<String, String>( "test", key, "value1" );
+        service.update( element );
+
+        SleepUtil.sleepAtLeast( 300 );
+
+        ICacheElement<String, String> element2 = new CacheElement<String, String>( "test", key, "value2" );
+        service.update( element2 );
+
+        SleepUtil.sleepAtLeast( 1000 );
+
+        // VERIFY
+        ICacheElement<String, String> cacheElement = cache.get( "key" );
+        assertEquals( "Didn't get the correct object: " + cacheElement , element2.getVal(), cacheElement.getVal() );
+    }
+
+    /**
+     * Create a listener. Add an element to the listeners cache. Setup a service. Try to get from
+     * the service.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet_SendAndReceived()
+        throws Exception
+    {
+        // SETUP
+        // setup a listener
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1107 );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        CompositeCache<String, String> cache = cacheMgr.getCache( "test" );
+//        System.out.println( "mock cache = " + cache );
+
+        // get the listener started
+        // give it our mock cache manager
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        // add the item to the listeners cache
+        ICacheElement<String, String> element = new CacheElement<String, String>( "test", "key", "value1" );
+        cache.update( element );
+
+        // setup a service to talk to the listener started above.
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1108 );
+        lattr2.setTcpServer( "localhost:1107" );
+
+        LateralTCPService<String, String> service = new LateralTCPService<String, String>( lattr2 );
+        service.setListenerId( 123456 );
+
+        SleepUtil.sleepAtLeast( 300 );
+
+        // DO WORK
+        ICacheElement<String, String> result = service.get( "test", "key" );
+
+        // VERIFY
+        assertNotNull( "Result should not be null.", result );
+        assertEquals( "Didn't get the correct object", element.getVal(), result.getVal() );
+    }
+
+    /**
+     * Create a listener. Add an element to the listeners cache. Setup a service. Try to get keys from
+     * the service.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetGroupKeys_SendAndReceived()  throws Exception
+    {
+        // SETUP
+        // setup a listener
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1150 );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        CompositeCache<GroupAttrName<String>, String> cache = cacheMgr.getCache( "test" );
+//        System.out.println( "mock cache = " + cache );
+
+        // get the listener started
+        // give it our mock cache manager
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        // add the item to the listeners cache
+        GroupAttrName<String> groupKey = new GroupAttrName<String>(new GroupId("test", "group"), "key");
+        ICacheElement<GroupAttrName<String>, String> element =
+            new CacheElement<GroupAttrName<String>, String>( "test", groupKey, "value1" );
+        cache.update( element );
+
+        // setup a service to talk to the listener started above.
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1151 );
+        lattr2.setTcpServer( "localhost:1150" );
+
+        LateralTCPService<GroupAttrName<String>, String> service =
+            new LateralTCPService<GroupAttrName<String>, String>( lattr2 );
+        service.setListenerId( 123459 );
+
+        SleepUtil.sleepAtLeast( 500 );
+
+        // DO WORK
+        Set<GroupAttrName<String>> result = service.getKeySet("test");
+
+       // SleepUtil.sleepAtLeast( 5000000 );
+
+        // VERIFY
+        assertNotNull( "Result should not be null.", result );
+        assertEquals( "Didn't get the correct object", "key", result.toArray()[0] );
+    }
+
+    /**
+     * Create a listener. Add an element to the listeners cache. Setup a service. Try to get from
+     * the service.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatching_WithData()
+        throws Exception
+    {
+        // SETUP
+        // setup a listener
+        TCPLateralCacheAttributes lattr = new TCPLateralCacheAttributes();
+        lattr.setTcpListenerPort( 1108 );
+        MockCompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        CompositeCache<String, Integer> cache = cacheMgr.getCache( "test" );
+//        System.out.println( "mock cache = " + cache );
+
+        // get the listener started
+        // give it our mock cache manager
+        LateralTCPListener.getInstance( lattr, cacheMgr );
+
+        String keyprefix1 = "MyPrefix1";
+        int numToInsertPrefix1 = 10;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            // add the item to the listeners cache
+            ICacheElement<String, Integer> element = new CacheElement<String, Integer>( "test", keyprefix1 + String.valueOf( i ), Integer.valueOf( i ) );
+            cache.update( element );
+        }
+
+        // setup a service to talk to the listener started above.
+        TCPLateralCacheAttributes lattr2 = new TCPLateralCacheAttributes();
+        lattr2.setTcpListenerPort( 1108 );
+        lattr2.setTcpServer( "localhost:1108" );
+
+        LateralTCPService<String, Integer> service = new LateralTCPService<String, Integer>( lattr2 );
+        service.setListenerId( 123456 );
+
+        SleepUtil.sleepAtLeast( 300 );
+
+        // DO WORK
+        Map<String, ICacheElement<String, Integer>> result = service.getMatching( "test", keyprefix1 + ".+" );
+
+        // VERIFY
+        assertNotNull( "Result should not be null.", result );
+        assertEquals( "Wrong number returned 1:", numToInsertPrefix1, result.size() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheClient.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheClient.java
new file mode 100644
index 0000000..b72fce1
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheClient.java
@@ -0,0 +1,280 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Used for testing the no wait.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MockRemoteCacheClient<K extends Serializable, V extends Serializable>
+    extends AbstractAuxiliaryCache<K, V>
+    implements IRemoteCacheClient<K, V>
+{
+    /** For serialization. Don't change. */
+    private static final long serialVersionUID = 1L;
+
+    /** log instance */
+    private static final Log log = LogFactory.getLog( MockRemoteCacheClient.class );
+
+    /** List of ICacheElement<K, V> objects passed into update. */
+    public List<ICacheElement<K, V>> updateList = new LinkedList<ICacheElement<K,V>>();
+
+    /** List of key objects passed into remove. */
+    public List<K> removeList = new LinkedList<K>();
+
+    /** status to return. */
+    public CacheStatus status = CacheStatus.ALIVE;
+
+    /** Can setup values to return from get. values must be ICacheElement<K, V> */
+    public Map<K, ICacheElement<K, V>> getSetupMap = new HashMap<K, ICacheElement<K,V>>();
+
+    /** Can setup values to return from get. values must be Map<K, ICacheElement<K, V>> */
+    public Map<Set<K>, Map<K, ICacheElement<K, V>>> getMultipleSetupMap =
+        new HashMap<Set<K>, Map<K,ICacheElement<K,V>>>();
+
+    /** The last service passed to fixCache */
+    public ICacheServiceNonLocal<K, V> fixed;
+
+    /** Attributes. */
+    public RemoteCacheAttributes attributes = new RemoteCacheAttributes();
+
+    /**
+     * Stores the last argument as fixed.
+     * <p>
+     * @see org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheClient#fixCache(org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal)
+     */
+    @Override
+    @SuppressWarnings("unchecked") // Don't know how to do this properly
+    public void fixCache( ICacheServiceNonLocal<?, ?> remote )
+    {
+        fixed = (ICacheServiceNonLocal<K, V>)remote;
+    }
+
+    /**
+     * @return long
+     */
+    @Override
+    public long getListenerId()
+    {
+        return 0;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public IRemoteCacheListener<K, V> getListener()
+    {
+        return null;
+    }
+
+    /**
+     * Adds the argument to the updatedList.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#update(org.apache.commons.jcs.engine.behavior.ICacheElement)
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+    {
+        updateList.add( ce );
+    }
+
+    /**
+     * Looks in the getSetupMap for a value.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#get(java.io.Serializable)
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+    {
+        log.info( "get [" + key + "]" );
+        return getSetupMap.get( key );
+    }
+
+    /**
+     * Gets multiple items from the cache based on the given set of keys.
+     * <p>
+     * @param keys
+     * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no
+     *         data in cache for any of these keys
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+    {
+        log.info( "get [" + keys + "]" );
+        return getMultipleSetupMap.get( keys );
+    }
+
+    /**
+     * Adds the key to the remove list.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#remove(java.io.Serializable)
+     */
+    @Override
+    public boolean remove( K key )
+    {
+        removeList.add( key );
+        return false;
+    }
+
+    /**
+     * Removes all cached items from the cache.
+     */
+    @Override
+    public void removeAll()
+    {
+        // do nothing
+    }
+
+    /**
+     * Prepares for shutdown.
+     */
+    @Override
+    public void dispose()
+    {
+        // do nothing
+    }
+
+    /**
+     * Returns the current cache size in number of elements.
+     * <p>
+     * @return number of elements
+     */
+    @Override
+    public int getSize()
+    {
+        return 0;
+    }
+
+    /**
+     * Returns the status setup variable. (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getStatus()
+     */
+    @Override
+    public CacheStatus getStatus()
+    {
+        return status;
+    }
+
+    /**
+     * Returns the cache name.
+     * <p>
+     * @return usually the region name.
+     */
+    @Override
+    public String getCacheName()
+    {
+        return null;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public Set<K> getKeySet( )
+    {
+        return null;
+    }
+
+    /**
+     * @return null
+     */
+    @Override
+    public IStats getStatistics()
+    {
+        return null;
+    }
+
+    /**
+     * Returns the setup attributes. By default they are not null.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getAuxiliaryCacheAttributes()
+     */
+    @Override
+    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+    {
+        return attributes;
+    }
+
+    /**
+     * Returns the cache stats.
+     * <p>
+     * @return String of important historical information.
+     */
+    @Override
+    public String getStats()
+    {
+        return null;
+    }
+
+    /** @return 0 */
+    @Override
+    public CacheType getCacheType()
+    {
+        return CacheType.REMOTE_CACHE;
+    }
+
+    /**
+     * @param pattern
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching(String pattern)
+        throws IOException
+    {
+        return new HashMap<K, ICacheElement<K,V>>();
+    }
+
+    /**
+     * Nothing important
+     * <p>
+     * @return null
+     */
+    @Override
+    public String getEventLoggingExtraInfo()
+    {
+        return null;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheListener.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheListener.java
new file mode 100644
index 0000000..9ede420
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheListener.java
@@ -0,0 +1,169 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * For testing.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MockRemoteCacheListener<K extends Serializable, V extends Serializable>
+    implements IRemoteCacheListener<K, V>
+{
+    /** Setup the listener id that this will return. */
+    private long listenerId;
+
+    /** Setup the listener ip that this will return. */
+    public String localAddress;
+
+    /** Number of times handlePut was called. */
+    public int putCount;
+
+    /** List of ICacheElements passed to handlePut. */
+    public List<ICacheElement<K, V>> putItems = new LinkedList<ICacheElement<K,V>>();
+
+    /** List of Serializable objects passed to handleRemove. */
+    public List<K> removedKeys = new LinkedList<K>();
+
+    /** Number of times handleRemote was called. */
+    public int removeCount;
+
+    /** The type of remote listener */
+    public RemoteType remoteType = RemoteType.LOCAL;
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * returns the listener id, which can be setup.
+     * @return listenerId
+     * @throws IOException
+     */
+    @Override
+    public long getListenerId()
+        throws IOException
+    {
+        return listenerId;
+    }
+
+    /**
+     * @return localAddress
+     * @throws IOException
+     */
+    @Override
+    public String getLocalHostAddress()
+        throws IOException
+    {
+        return localAddress;
+    }
+
+    /**
+     * Return the setup remoteType.
+     * @return remoteType
+     * @throws IOException
+     */
+    @Override
+    public RemoteType getRemoteType()
+        throws IOException
+    {
+        return remoteType;
+    }
+
+    /**
+     * Allows you to setup the listener id.
+     * <p>
+     * @param id
+     * @throws IOException
+     */
+    @Override
+    public void setListenerId( long id )
+        throws IOException
+    {
+        listenerId = id;
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleDispose( String cacheName )
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * This increments the put count and adds the item to the putItem list.
+     * <p>
+     * @param item
+     * @throws IOException
+     */
+    @Override
+    public void handlePut( ICacheElement<K, V> item )
+        throws IOException
+    {
+        putCount++;
+        this.putItems.add( item );
+    }
+
+    /**
+     * Increments the remove count and adds the key to the removedKeys list.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void handleRemove( String cacheName, K key )
+        throws IOException
+    {
+        removeCount++;
+        removedKeys.add( key );
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleRemoveAll( String cacheName )
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheService.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheService.java
new file mode 100644
index 0000000..933a178
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/MockRemoteCacheService.java
@@ -0,0 +1,246 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a mock impl of the remote cache service.
+ */
+public class MockRemoteCacheService<K extends Serializable, V extends Serializable>
+    implements ICacheServiceNonLocal<K, V>
+{
+    /** The key last passed to get */
+    public Serializable lastGetKey;
+
+    /** The pattern last passed to get */
+    public String lastGetMatchingPattern;
+
+    /** The keya last passed to getMatching */
+    public Set<K> lastGetMultipleKeys;
+
+    /** The object that was last passed to update. */
+    public ICacheElement<K, V> lastUpdate;
+
+    /** List of updates. */
+    public List<ICacheElement<K, V>> updateRequestList = new ArrayList<ICacheElement<K,V>>();
+
+    /** List of request ids. */
+    public List<Long> updateRequestIdList = new ArrayList<Long>();
+
+    /** The key that was last passed to remove. */
+    public K lastRemoveKey;
+
+    /** The cache name that was last passed to removeAll. */
+    public String lastRemoveAllCacheName;
+
+    /**
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     * @return null
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+    {
+        lastGetKey = key;
+        return null;
+    }
+
+    /**
+     * @param cacheName
+     * @return empty set
+     */
+    @Override
+    public Set<K> getKeySet( String cacheName )
+    {
+        return new HashSet<K>();
+    }
+
+    /**
+     * Set the last remove key.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+    {
+        lastRemoveKey = key;
+    }
+
+    /**
+     * Set the lastRemoveAllCacheName to the cacheName.
+     * <p>
+     * (non-Javadoc)
+     * @see org.apache.commons.jcs.auxiliary.remote.behavior.ICacheServiceNonLocal#removeAll(java.lang.String,
+     *      long)
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        lastRemoveAllCacheName = cacheName;
+    }
+
+    /**
+     * Set the last update item.
+     * <p>
+     * @param item
+     * @param requesterId
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long requesterId )
+    {
+        lastUpdate = item;
+        updateRequestList.add( item );
+        updateRequestIdList.add( Long.valueOf( requesterId ) );
+    }
+
+    /**
+     * Do nothing.
+     * <p>
+     * @param cacheName
+     */
+    @Override
+    public void dispose( String cacheName )
+    {
+        return;
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     * @return null
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+    {
+        return get( cacheName, key, 0 );
+    }
+
+    /**
+     * Do nothing.
+     */
+    @Override
+    public void release()
+    {
+        return;
+    }
+
+    /**
+     * Set the last remove key.
+     * <p>
+     * @param cacheName
+     * @param key
+     */
+    @Override
+    public void remove( String cacheName, K key )
+    {
+        lastRemoveKey = key;
+    }
+
+    /**
+     * Set the last remove all cache name.
+     * <p>
+     * @param cacheName
+     */
+    @Override
+    public void removeAll( String cacheName )
+    {
+        lastRemoveAllCacheName = cacheName;
+    }
+
+    /**
+     * Set the last update item.
+     * <p>
+     * @param item
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+    {
+        lastUpdate = item;
+    }
+
+    /**
+     * @param cacheName
+     * @param keys
+     * @param requesterId
+     * @return empty map
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+    {
+        lastGetMultipleKeys = keys;
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * @param cacheName
+     * @param keys
+     * @return empty map
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+    {
+        return getMultiple( cacheName, keys, 0 );
+    }
+
+    /**
+     * Returns an empty map. Zombies have no internal data.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return an empty map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, 0 );
+    }
+
+    /**
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        lastGetMatchingPattern = pattern;
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheClientTester.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheClientTester.java
new file mode 100644
index 0000000..cdb2686
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheClientTester.java
@@ -0,0 +1,342 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.access.exception.ObjectExistsException;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheObserver;
+import org.apache.commons.jcs.engine.behavior.ICacheService;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.rmi.Naming;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.registry.Registry;
+import java.rmi.server.ExportException;
+import java.rmi.server.UnicastRemoteObject;
+
+/**
+ * Manual tester.
+ */
+public class RemoteCacheClientTester
+    implements IRemoteCacheListener<String, String>, IRemoteCacheConstants, Remote
+{
+    /** the observer */
+    protected ICacheObserver watch;
+
+    /** the service */
+    protected ICacheService<String, String> cache;
+
+    /** The registry host name. */
+    final String host;
+
+    /** The registry port number. */
+    final int port;
+
+    /** call count */
+    final int count;
+
+    /** Description of the Field */
+    protected static long listenerId = 0;
+
+    /**
+     * Gets the remoteType attribute of the RemoteCacheClientTest object
+     * @return The remoteType value
+     * @throws IOException
+     */
+    @Override
+    public RemoteType getRemoteType()
+        throws IOException
+    {
+        return RemoteType.LOCAL;
+    }
+
+    /**
+     * Constructor for the RemoteCacheClientTest object
+     * @param count
+     * @throws MalformedURLException
+     * @throws NotBoundException
+     * @throws IOException
+     */
+    public RemoteCacheClientTester( int count )
+        throws MalformedURLException, NotBoundException, IOException
+    {
+        this( count, true, true, false );
+    }
+
+    /**
+     * Constructor for the RemoteCacheClientTest object
+     * @param count
+     * @param write
+     * @param read
+     * @param delete
+     * @throws MalformedURLException
+     * @throws NotBoundException
+     * @throws IOException
+     */
+    public RemoteCacheClientTester( int count, boolean write, boolean read, boolean delete )
+        throws MalformedURLException, NotBoundException, IOException
+    {
+        this( "", Registry.REGISTRY_PORT, count, write, read, delete );
+    }
+
+    /**
+     * Constructor for the RemoteCacheClientTest object
+     * @param host
+     * @param port
+     * @param count
+     * @param write
+     * @param read
+     * @param delete
+     * @throws MalformedURLException
+     * @throws NotBoundException
+     * @throws IOException
+     */
+    @SuppressWarnings("unchecked")
+    public RemoteCacheClientTester( String host, int port, int count, boolean write, boolean read, boolean delete )
+        throws MalformedURLException, NotBoundException, IOException
+    {
+        this.count = count;
+        this.host = host;
+        this.port = port;
+        // record export exception
+        Exception ee = null;
+
+        try
+        {
+            // Export this remote object to make it available to receive
+            // incoming calls,
+            // using an anonymous port.
+            UnicastRemoteObject.exportObject( this );
+        }
+        catch ( ExportException e )
+        {
+            // use already exported object; remember exception
+            ee = e;
+            ee.printStackTrace();
+        }
+        String service = System.getProperty( REMOTE_CACHE_SERVICE_NAME );
+
+        if ( service == null )
+        {
+            service = REMOTE_CACHE_SERVICE_VAL;
+        }
+        String registry = RemoteUtils.getNamingURL(host, port, service);
+
+        p( "looking up server " + registry );
+
+        Object obj = Naming.lookup( registry );
+
+        p( "server found" );
+
+        cache = (ICacheService<String, String>) obj;
+        watch = (ICacheObserver) obj;
+
+        p( "subscribing to the server" );
+
+        watch.addCacheListener( "testCache", this );
+        ICacheElement<String, String> cb = new CacheElement<String, String>( "testCache", "testKey", "testVal" );
+
+        for ( int i = 0; i < count; i++ )
+        {
+            cb = new CacheElement<String, String>( "testCache", "" + i, "" + i );
+
+            if ( delete )
+            {
+                p( "deleting a cache item from the server " + i );
+
+                cache.remove( cb.getCacheName(), cb.getKey() );
+            }
+            if ( write )
+            {
+                p( "putting a cache bean to the server " + i );
+
+                try
+                {
+                    cache.update( cb );
+                }
+                catch ( ObjectExistsException oee )
+                {
+                    p( oee.toString() );
+                }
+            }
+            if ( read )
+            {
+                try
+                {
+                    Object val = cache.get( cb.getCacheName(), cb.getKey() );
+                    p( "get " + cb.getKey() + " returns " + val );
+                }
+                catch ( CacheException onfe )
+                {
+                    // nothing
+                }
+            }
+        }
+    }
+
+    /**
+     * @param cb
+     * @throws IOException
+     */
+    @Override
+    public void handlePut( ICacheElement<String, String> cb )
+        throws IOException
+    {
+        p( "handlePut> cb=" + cb );
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     * @throws IOException
+     */
+    @Override
+    public void handleRemove( String cacheName, String key )
+        throws IOException
+    {
+        p( "handleRemove> cacheName=" + cacheName + ", key=" + key );
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleRemoveAll( String cacheName )
+        throws IOException
+    {
+        p( "handleRemove> cacheName=" + cacheName );
+    }
+
+    /**
+     * @param cacheName
+     * @throws IOException
+     */
+    @Override
+    public void handleDispose( String cacheName )
+        throws IOException
+    {
+        p( "handleDispose> cacheName=" + cacheName );
+    }
+
+    /*
+     * public void handleRelease() throws IOException { p("handleRelease>"); }
+     */
+    /**
+     * The main program for the RemoteCacheClientTest class
+     * @param args The command line arguments
+     * @throws Exception
+     */
+    public static void main( String[] args )
+        throws Exception
+    {
+        int count = 0;
+        boolean read = false;
+        boolean write = false;
+        boolean delete = false;
+
+        for ( int i = 0; i < args.length; i++ )
+        {
+            if ( args[i].startsWith( "-" ) )
+            {
+                if ( !read )
+                {
+                    read = args[i].indexOf( "r" ) != -1;
+                }
+                if ( !write )
+                {
+                    write = args[i].indexOf( "w" ) != -1;
+                }
+                if ( !delete )
+                {
+                    delete = args[i].indexOf( "d" ) != -1;
+                }
+            }
+            else
+            {
+                count = Integer.parseInt( args[i] );
+            }
+        }
+        new RemoteCacheClientTester( count, write, read, delete );
+    }
+
+    /**
+     * Sets the listenerId attribute of the RemoteCacheClientTest object
+     * @param id The new listenerId value
+     * @throws IOException
+     */
+    @Override
+    public void setListenerId( long id )
+        throws IOException
+    {
+        listenerId = id;
+        p( "listenerId = " + id );
+    }
+
+    /**
+     * Gets the listenerId attribute of the RemoteCacheClientTest object
+     * @return The listenerId value
+     * @throws IOException
+     */
+    @Override
+    public long getListenerId()
+        throws IOException
+    {
+        return listenerId;
+    }
+
+    /**
+     * Helper for output, this is an user run test class
+     * @param s
+     */
+    private static void p( String s )
+    {
+        System.out.println( s );
+    }
+
+    /**
+     * @return null
+     * @throws IOException
+     */
+    @Override
+    public String getLocalHostAddress()
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        // TODO Auto-generated method stub
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListenerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListenerUnitTest.java
new file mode 100644
index 0000000..0dfead5
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheListenerUnitTest.java
@@ -0,0 +1,123 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.engine.CacheElementSerialized;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICache;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+/**
+ * Tests for the remote cache listener.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheListenerUnitTest
+    extends TestCase
+{
+    /**
+     * Create a RemoteCacheListener with a mock cache manager.  Set remove on put to false.
+     * Create a serialized element.  Call put on the listener.
+     * Verify that the deserialized element is in the cache.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_PutOnPut()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes irca = new RemoteCacheAttributes();
+        irca.setRemoveUponRemotePut( false );
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        RemoteCacheListener<String, String> listener = new RemoteCacheListener<String, String>( irca, cacheMgr );
+
+        String cacheName = "testName";
+        String key = "key";
+        String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(34);
+
+        IElementSerializer elementSerializer = new StandardSerializer();
+
+        ICacheElementSerialized<String, String> element =
+            new CacheElementSerialized<String, String>( cacheName, key, elementSerializer
+            .serialize( value ), attr );
+
+        // DO WORK
+        listener.handlePut( element );
+
+        // VERIFY
+        ICache<String, String> cache = cacheMgr.getCache( cacheName );
+        ICacheElement<String, String> after = cache.get( key );
+
+        assertNotNull( "Should have a deserialized object.", after );
+        assertEquals( "Values should be the same.", value, after.getVal() );
+        assertEquals( "Attributes should be the same.", attr.getMaxLife(), after
+            .getElementAttributes().getMaxLife() );
+        assertEquals( "Keys should be the same.", key, after.getKey() );
+        assertEquals( "Cache name should be the same.", cacheName, after.getCacheName() );
+    }
+
+    /**
+     * Create a RemoteCacheListener with a mock cache manager.  Set remove on put to true.
+     * Create a serialized element.  Call put on the listener.
+     * Verify that the deserialized element is not in the cache.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_RemoveOnPut()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes irca = new RemoteCacheAttributes();
+        irca.setRemoveUponRemotePut( true );
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        RemoteCacheListener<String, String> listener = new RemoteCacheListener<String, String>( irca, cacheMgr );
+
+        String cacheName = "testName";
+        String key = "key";
+        String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(34);
+
+        IElementSerializer elementSerializer = new StandardSerializer();
+
+        ICacheElementSerialized<String, String> element =
+            new CacheElementSerialized<String, String>( cacheName, key, elementSerializer
+            .serialize( value ), attr );
+
+        // DO WORK
+        listener.handlePut( element );
+
+        // VERIFY
+        ICache<String, String> cache = cacheMgr.getCache( cacheName );
+        ICacheElement<String, String> after = cache.get( key );
+
+        assertNull( "Should not have a deserialized object since remove on put is true.", after );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitUnitTest.java
new file mode 100644
index 0000000..fe671ae
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheNoWaitUnitTest.java
@@ -0,0 +1,214 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for the remote cache no wait. The no wait manages a queue on top of the client.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheNoWaitUnitTest
+    extends TestCase
+{
+    /**
+     * Simply verify that the client gets updated via the no wait.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testUpdate", "key", "value" );
+
+        // DO WORK
+        noWait.update( element );
+
+        // VERIFY
+        SleepUtil.sleepAtLeast( 10 );
+
+        assertEquals( "Wrong number updated.", 1, client.updateList.size() );
+        assertEquals( "Wrong element", element, client.updateList.get( 0 ) );
+    }
+
+    /**
+     * Simply verify that the client get is called from the no wait.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        ICacheElement<String, String> input = new CacheElement<String, String>( "testUpdate", "key", "value" );
+        client.getSetupMap.put( "key", input );
+
+        // DO WORK
+        ICacheElement<String, String> result = noWait.get( "key" );
+
+        // VERIFY
+        assertEquals( "Wrong element", input, result );
+    }
+
+    /**
+     * Simply verify that the client getMultiple is called from the no wait.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMultiple()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( "testUpdate", "key", "value" );
+        Map<String, ICacheElement<String, String>> inputMap = new HashMap<String, ICacheElement<String,String>>();
+        inputMap.put( "key", inputElement );
+
+        Set<String> keys = new HashSet<String>();
+        keys.add( "key" );
+
+        client.getMultipleSetupMap.put( keys, inputMap );
+
+        // DO WORK
+        Map<String, ICacheElement<String, String>> result = noWait.getMultiple( keys );
+
+        // VERIFY
+        assertEquals( "elements map", inputMap, result );
+    }
+
+    /**
+     * Simply verify that the client gets updated via the no wait.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemove()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        String input = "MyKey";
+
+        // DO WORK
+        noWait.remove( input );
+
+        SleepUtil.sleepAtLeast( 10 );
+
+        // VERIFY
+        assertEquals( "Wrong number updated.", 1, client.removeList.size() );
+        assertEquals( "Wrong key", input, client.removeList.get( 0 ) );
+    }
+
+    /**
+     * Simply verify that the client status is returned in the stats.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetStats()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        client.status = CacheStatus.ALIVE;
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        // DO WORK
+        String result = noWait.getStats();
+
+        // VERIFY
+        assertTrue( "Status should contain 'ALIVE'", result.indexOf( "ALIVE" ) != -1 );
+    }
+
+    /**
+     * Simply verify that we get a status of error if the cache is in error..
+     * <p>
+     * @throws Exception
+     */
+    public void testGetStatus_error()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        client.status = CacheStatus.ERROR;
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        // DO WORK
+        CacheStatus result = noWait.getStatus();
+
+        // VERIFY
+        assertEquals( "Wrong status", CacheStatus.ERROR, result );
+    }
+
+    /**
+     * Simply verify that the serviced supplied to fix is passed onto the client. Verify that the
+     * original event queue is destroyed. A new event queue willbe plugged in on fix.
+     * <p>
+     * @throws Exception
+     */
+    public void testFixCache()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheClient<String, String> client = new MockRemoteCacheClient<String, String>();
+        client.status = CacheStatus.ALIVE;
+        RemoteCacheNoWait<String, String> noWait = new RemoteCacheNoWait<String, String>( client );
+
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testUpdate", "key", "value" );
+
+        // DO WORK
+        noWait.update( element );
+        SleepUtil.sleepAtLeast( 10 );
+        ICacheEventQueue<String, String> originalQueue = noWait.getCacheEventQueue();
+
+        noWait.fixCache( service );
+
+        noWait.update( element );
+        SleepUtil.sleepAtLeast( 10 );
+        ICacheEventQueue<String, String> newQueue = noWait.getCacheEventQueue();
+
+        // VERIFY
+        assertEquals( "Wrong status", service, client.fixed );
+        assertFalse( "Original queue should not alive", originalQueue.isAlive() );
+        assertTrue( "New queue should be alive." + newQueue, newQueue.isAlive() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheUnitTest.java
new file mode 100644
index 0000000..8c9a383
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteCacheUnitTest.java
@@ -0,0 +1,319 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.utils.serialization.SerializationConversionUtil;
+
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Unit Tests for the Remote Cache.
+ */
+public class RemoteCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the remote service update method is called. The remote cache serializes the obect
+     * first.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate()
+        throws Exception
+    {
+        // SETUP
+        long listenerId = 123;
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+        listener.setListenerId( listenerId );
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        remoteCache.update( element );
+
+        // VERIFY
+        assertTrue( "The element should be in the serialized wrapper.",
+                    service.lastUpdate instanceof ICacheElementSerialized );
+        ICacheElement<String, String> result = SerializationConversionUtil
+            .getDeSerializedCacheElement( (ICacheElementSerialized<String, String>) service.lastUpdate, remoteCache
+                .getElementSerializer() );
+        assertEquals( "Wrong element updated.", element.getVal(), result.getVal() );
+        assertEquals( "Wrong listener id.", Long.valueOf( listenerId ), service.updateRequestIdList.get( 0 ) );
+    }
+
+    /**
+     * Verify that when we call fix events queued in the zombie are propagated to the new service.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdateZombieThenFix()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        // set the zombie
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, zombie, listener );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        remoteCache.update( element );
+        // set the new service, this should call propagate
+        remoteCache.fixCache( service );
+
+        // VERIFY
+        assertTrue( "The element should be in the serialized warapper.",
+                    service.lastUpdate instanceof ICacheElementSerialized );
+        ICacheElement<String, String> result = SerializationConversionUtil
+            .getDeSerializedCacheElement( (ICacheElementSerialized<String, String>) service.lastUpdate, remoteCache
+                .getElementSerializer() );
+        assertEquals( "Wrong element updated.", element.getVal(), result.getVal() );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        ICacheElement<String, String> item = new CacheElement<String, String>( "region", "key", "value" );
+
+        // DO WORK
+        remoteCache.update( item );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.get( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMultiple_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.getMultiple( new HashSet<String>() );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemove_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.remove( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAll_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.remove( "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatching_simple()
+        throws Exception
+    {
+        // SETUP
+        String pattern = "adsfasdfasd.?";
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        Map<String, ICacheElement<String, String>> result = remoteCache.getMatching( pattern );
+
+        // VERIFY
+        assertNotNull( "Should have a map", result );
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testDispose_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.dispose( );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify that there is no problem if there is no listener.
+     * <p>
+     * @throws Exception
+     */
+    public void testDispose_nullListener()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheAttributes cattr = new RemoteCacheAttributes();
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+        MockRemoteCacheListener<String, String> listener = null;
+
+        RemoteCache<String, String> remoteCache = new RemoteCache<String, String>( cattr, service, listener );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        remoteCache.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        remoteCache.dispose( );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtilsUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtilsUnitTest.java
new file mode 100644
index 0000000..3f4e428
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/RemoteUtilsUnitTest.java
@@ -0,0 +1,51 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.rmi.registry.Registry;
+
+/**
+ * Simple tests for remote utils. It is difficult to verify most of the things is does.
+ *<p>
+ * @author Aaron Smuts
+ */
+public class RemoteUtilsUnitTest
+    extends TestCase
+{
+    /**
+     * Call create registry.
+     * <p>
+     * The exception is in the security manager setting.
+     */
+    public void testCreateRegistry()
+    {
+        Registry registry = RemoteUtils.createRegistry( 1102 );
+        assertNotNull("Registry should not be null", registry);
+    }
+
+    public void testgetNamingURL()
+    {
+        assertEquals("//host:1/servicename", RemoteUtils.getNamingURL("host",1,"servicename"));
+        assertEquals("//127.0.0.1:2/servicename", RemoteUtils.getNamingURL("127.0.0.1",2,"servicename"));
+        assertEquals("//[0:0:0:0:0:0:0:1%251]:3/servicename", RemoteUtils.getNamingURL("0:0:0:0:0:0:0:1%1",3,"servicename"));
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/TestRemoteCache.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/TestRemoteCache.java
new file mode 100644
index 0000000..fb701c3
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/TestRemoteCache.java
@@ -0,0 +1,135 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author Aaron SMuts
+ */
+public class TestRemoteCache
+    extends TestCase
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( TestRemoteCache.class );
+
+    /**
+     * Start the cache.
+     */
+    public TestRemoteCache()
+    {
+        super();
+        try
+        {
+            System.out.println( "main> creating registry on the localhost" );
+            RemoteUtils.createRegistry( 1101 );
+
+            RemoteCacheServerFactory.startup( "localhost", 1101, "/TestRemoteServer.ccf" );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestRemoteClient.ccf" );
+    }
+
+    /**
+     * @throws Exception
+     *
+     *
+     */
+    public void skiptestSimpleSend()
+        throws Exception
+    {
+        log.info( "testSimpleSend" );
+
+        CacheAccess<String, String> cache = JCS.getInstance( "testCache" );
+
+        log.info( "cache = " + cache );
+
+        for ( int i = 0; i < 1000; i++ )
+        {
+//            System.out.println( "puttting " + i );
+            cache.put( "key" + i, "data" + i );
+//            System.out.println( "put " + i );
+            log.info( "put " + i );
+        }
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testService()
+        throws Exception
+    {
+
+        Thread.sleep( 100 );
+
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+
+        RemoteCacheAttributes rca = new RemoteCacheAttributes();
+        rca.setRemoteHost( "localhost" );
+        rca.setRemotePort( 1101 );
+
+        RemoteCacheManager mgr = RemoteCacheManager.getInstance( rca, cacheMgr, new MockCacheEventLogger(), new MockElementSerializer() );
+        AuxiliaryCache<String, String> cache = mgr.getCache( "testCache" );
+
+        int numMes = 100;
+        for ( int i = 0; i < numMes; i++ )
+        {
+            String message = "adsfasasfasfasdasf";
+            CacheElement<String, String> ce = new CacheElement<String, String>( "key" + 1, "data" + i, message );
+            cache.update( ce );
+//            System.out.println( "put " + ce );
+        }
+
+        // Thread.sleep( 2000 );
+
+        /*
+         * // the receiver instance. JCS cacheReceiver = JCS.getInstance(
+         * "testCache" );
+         *
+         * log.info( "cache = " + cache );
+         *
+         * for ( int i = 0; i < numMes; i++ ) { System.out.println( "getting " +
+         * i ); Object data = cacheReceiver.get( "key" + i );
+         * System.out.println( i + " = " + data ); }
+         */
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheServiceUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheServiceUnitTest.java
new file mode 100644
index 0000000..c66e909
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/ZombieRemoteCacheServiceUnitTest.java
@@ -0,0 +1,127 @@
+package org.apache.commons.jcs.auxiliary.remote;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ZombieCacheServiceNonLocal;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+/**
+ * Tests for the zombie remote cache service.
+ */
+public class ZombieRemoteCacheServiceUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that an update event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdateThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        zombie.update( element, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", element, service.lastUpdate );
+    }
+
+    /**
+     * Verify that nothing is added if the max is set to 0.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdateThenWalk_zeroSize()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 0 );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        zombie.update( element, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertNull( "Nothing should have been put to the service.", service.lastUpdate );
+    }
+
+    /**
+     * Verify that a remove event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testRemoveThenWalk";
+        String key = "myKey";
+
+        // DO WORK
+        zombie.remove( cacheName, key, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", key, service.lastRemoveKey );
+    }
+
+    /**
+     * Verify that a removeAll event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAllThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockRemoteCacheService<String, String> service = new MockRemoteCacheService<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testRemoveThenWalk";
+
+        // DO WORK
+        zombie.removeAll( cacheName, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", cacheName, service.lastRemoveAllCacheName);
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/MockRemoteCacheDispatcher.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/MockRemoteCacheDispatcher.java
new file mode 100644
index 0000000..e3986db
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/MockRemoteCacheDispatcher.java
@@ -0,0 +1,53 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheDispatcher;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+
+import java.io.IOException;
+
+/** For testing the service. */
+public class MockRemoteCacheDispatcher
+    implements IRemoteCacheDispatcher
+{
+    /** The last request passes to dispatch */
+    public RemoteCacheRequest<?, ?> lastRemoteCacheRequest;
+
+    /** The response setup */
+    public RemoteCacheResponse<?> setupRemoteCacheResponse;
+
+    /** Records the last and returns setupRemoteCacheResponse.
+     * <p>
+     * @param remoteCacheRequest
+     * @return RemoteCacheResponse
+     * @throws IOException
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <K, V, T>
+        RemoteCacheResponse<T> dispatchRequest( RemoteCacheRequest<K, V> remoteCacheRequest )
+        throws IOException
+    {
+        this.lastRemoteCacheRequest = remoteCacheRequest;
+        return (RemoteCacheResponse<T>)setupRemoteCacheResponse;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClientUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClientUnitTest.java
new file mode 100644
index 0000000..be8e3e2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheClientUnitTest.java
@@ -0,0 +1,275 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteRequestType;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/** Unit tests for the client. */
+public class RemoteHttpCacheClientUnitTest
+    extends TestCase
+{
+    /**
+     * Verify get functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testGet_nullFromDispatcher()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+        String key = "key";
+
+        mockDispatcher.setupRemoteCacheResponse = null;
+
+        // DO WORK
+        ICacheElement<String, String> result = client.get( cacheName, key );
+
+        // VERIFY
+        assertNull( "Wrong result.", result );
+        assertEquals( "Wrong type.", RemoteRequestType.GET, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+
+    /**
+     * Verify get functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testGet_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+        String key = "key";
+
+        ICacheElement<String, String> expected = new CacheElement<String, String>( cacheName, key, "value" );
+        RemoteCacheResponse<ICacheElement<String, String>> remoteHttpCacheResponse =
+            new RemoteCacheResponse<ICacheElement<String,String>>();
+        remoteHttpCacheResponse.setPayload( expected );
+
+        mockDispatcher.setupRemoteCacheResponse = remoteHttpCacheResponse;
+
+        // DO WORK
+        ICacheElement<String, String> result = client.get( cacheName, key );
+
+        // VERIFY
+        assertEquals( "Wrong result.", expected, result );
+        assertEquals( "Wrong type.", RemoteRequestType.GET, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+
+    /**
+     * Verify get functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testGetMatching_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+        String pattern = "key";
+
+        ICacheElement<String, String> expected = new CacheElement<String, String>( cacheName, "key", "value" );
+        Map<String, ICacheElement<String, String>> expectedMap = new HashMap<String, ICacheElement<String,String>>();
+        expectedMap.put( "key", expected );
+        RemoteCacheResponse<Map<String, ICacheElement<String, String>>> remoteHttpCacheResponse =
+            new RemoteCacheResponse<Map<String,ICacheElement<String,String>>>();
+        remoteHttpCacheResponse.setPayload( expectedMap );
+
+        mockDispatcher.setupRemoteCacheResponse = remoteHttpCacheResponse;
+
+        // DO WORK
+        Map<String, ICacheElement<String, String>> result = client.getMatching( cacheName, pattern );
+
+        // VERIFY
+        assertEquals( "Wrong result.", expected, result.get( "key" ) );
+        assertEquals( "Wrong type.", RemoteRequestType.GET_MATCHING,
+                      mockDispatcher.lastRemoteCacheRequest.getRequestType() );
+    }
+
+    /**
+     * Verify get functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testGetMultiple_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+        Set<String> keys = Collections.emptySet();
+
+        ICacheElement<String, String> expected = new CacheElement<String, String>( cacheName, "key", "value" );
+        Map<String, ICacheElement<String, String>> expectedMap = new HashMap<String, ICacheElement<String,String>>();
+        expectedMap.put( "key", expected );
+        RemoteCacheResponse<Map<String, ICacheElement<String, String>>> remoteHttpCacheResponse =
+            new RemoteCacheResponse<Map<String,ICacheElement<String,String>>>();
+        remoteHttpCacheResponse.setPayload( expectedMap );
+
+        mockDispatcher.setupRemoteCacheResponse = remoteHttpCacheResponse;
+
+        // DO WORK
+        Map<String, ICacheElement<String, String>> result = client.getMultiple( cacheName, keys );
+
+        // VERIFY
+        assertEquals( "Wrong result.", expected, result.get( "key" ) );
+        assertEquals( "Wrong type.", RemoteRequestType.GET_MULTIPLE,
+                      mockDispatcher.lastRemoteCacheRequest.getRequestType() );
+    }
+
+    /**
+     * Verify remove functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testRemove_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+        String key = "key";
+
+        // DO WORK
+        client.remove( cacheName, key );
+
+        // VERIFY
+        assertEquals( "Wrong type.", RemoteRequestType.REMOVE, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+
+    /**
+     * Verify removeall functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testRemoveAll_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+
+        // DO WORK
+        client.removeAll( cacheName );
+
+        // VERIFY
+        assertEquals( "Wrong type.", RemoteRequestType.REMOVE_ALL, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+
+    /**
+     * Verify update functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testUpdate_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+
+        // DO WORK
+        client.update( element );
+
+        // VERIFY
+        assertEquals( "Wrong type.", RemoteRequestType.UPDATE, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+
+    /**
+     * Verify dispose functionality
+     * <p>
+     * @throws IOException
+     */
+    public void testDispose_normal()
+        throws IOException
+    {
+        // SETUP
+        RemoteHttpCacheAttributes attributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheClient<String, String> client = new RemoteHttpCacheClient<String, String>( attributes );
+
+        MockRemoteCacheDispatcher mockDispatcher = new MockRemoteCacheDispatcher();
+        client.setRemoteDispatcher( mockDispatcher );
+
+        String cacheName = "test";
+
+        // DO WORK
+        client.dispose( cacheName );
+
+        // VERIFY
+        assertEquals( "Wrong type.", RemoteRequestType.DISPOSE, mockDispatcher.lastRemoteCacheRequest
+            .getRequestType() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcherUniTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcherUniTest.java
new file mode 100644
index 0000000..ca448c4
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheDispatcherUniTest.java
@@ -0,0 +1,52 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteRequestType;
+
+/** Unit tests for the dispatcher. */
+public class RemoteHttpCacheDispatcherUniTest
+    extends TestCase
+{
+    /**
+     * Verify that we don't get two ?'s
+     */
+    public void testAddParameters_withQueryString()
+    {
+        // SETUP
+        RemoteHttpCacheAttributes remoteHttpCacheAttributes = new RemoteHttpCacheAttributes();
+        RemoteHttpCacheDispatcher dispatcher = new RemoteHttpCacheDispatcher( remoteHttpCacheAttributes );
+
+        RemoteCacheRequest<String, String> remoteCacheRequest = new RemoteCacheRequest<String, String>();
+        remoteCacheRequest.setRequestType( RemoteRequestType.REMOVE_ALL );
+        String cacheName = "myCache";
+        remoteCacheRequest.setCacheName( cacheName );
+
+        String baseUrl = "http://localhost?thishasaquestionmark";
+
+        // DO WORK
+        String result = dispatcher.addParameters( remoteCacheRequest, baseUrl );
+
+        // VERIFY
+        assertEquals( "Wrong url", baseUrl + "&CacheName=" + cacheName + "&Key=&RequestType=REMOVE_ALL", result  );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManagerUnitTest.java
new file mode 100644
index 0000000..149e69b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManagerUnitTest.java
@@ -0,0 +1,98 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.remote.http.client.behavior.IRemoteHttpCacheClient;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/** Unit tests for the manager. */
+public class RemoteHttpCacheManagerUnitTest
+    extends TestCase
+{
+    /** Verify that we get the default. */
+    public void testCreateRemoteHttpCacheClient_Bad()
+    {
+        // SETUP
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        ICacheEventLogger cacheEventLogger = null;
+        IElementSerializer elementSerializer = null;
+
+        String remoteHttpClientClassName = "junk";
+        RemoteHttpCacheAttributes cattr = new RemoteHttpCacheAttributes();
+        cattr.setRemoteHttpClientClassName( remoteHttpClientClassName );
+
+        RemoteHttpCacheManager manager = RemoteHttpCacheManager.getInstance( cacheMgr, cacheEventLogger,
+                                                                             elementSerializer );
+
+        // DO WORK
+        IRemoteHttpCacheClient<String, String> result = manager.createRemoteHttpCacheClientForAttributes( cattr );
+
+        // VEIFY
+        assertNotNull( "Should have a cache.", result );
+        assertTrue( "Wrong default.", result instanceof RemoteHttpCacheClient );
+        assertTrue( "Should be initialized", ((RemoteHttpCacheClient<String, String>)result).isInitialized() );
+    }
+
+    /** Verify that we get the default. */
+    public void testCreateRemoteHttpCacheClient_deafult()
+    {
+        // SETUP
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        ICacheEventLogger cacheEventLogger = null;
+        IElementSerializer elementSerializer = null;
+
+        RemoteHttpCacheAttributes cattr = new RemoteHttpCacheAttributes();
+
+        RemoteHttpCacheManager manager = RemoteHttpCacheManager.getInstance( cacheMgr, cacheEventLogger,
+                                                                             elementSerializer );
+
+        // DO WORK
+        IRemoteHttpCacheClient<String, String> result = manager.createRemoteHttpCacheClientForAttributes( cattr );
+
+        // VEIFY
+        assertNotNull( "Should have a cache.", result );
+        assertTrue( "Wrong default.", result instanceof RemoteHttpCacheClient );
+    }
+
+    /** Verify that we get a cache no wait. */
+    public void testGetCache_normal()
+    {
+        // SETUP
+        ICompositeCacheManager cacheMgr = new MockCompositeCacheManager();
+        ICacheEventLogger cacheEventLogger = null;
+        IElementSerializer elementSerializer = null;
+
+        RemoteHttpCacheAttributes cattr = new RemoteHttpCacheAttributes();
+
+        RemoteHttpCacheManager manager = RemoteHttpCacheManager.getInstance( cacheMgr, cacheEventLogger,
+                                                                             elementSerializer );
+
+        // DO WORK
+        AuxiliaryCache<String, String> result = manager.getCache( cattr );
+
+        // VEIFY
+        assertNotNull( "Should have a cache.", result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManualTester.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManualTester.java
new file mode 100644
index 0000000..d65720c
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/client/RemoteHttpCacheManualTester.java
@@ -0,0 +1,75 @@
+package org.apache.commons.jcs.auxiliary.remote.http.client;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+
+/** Manual tester for a JCS instance configured to use the http client. */
+public class RemoteHttpCacheManualTester
+    extends TestCase
+{
+    /** number to use for the test */
+    private static int items = 100;
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestRemoteHttpCache.ccf" );
+    }
+
+    /**
+     * A unit test for JUnit
+     * @throws Exception Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "testCache1" );
+
+        jcs.put( "TestKey", "TestValue" );
+
+//        System.out.println( jcs.getStats() );
+
+        for ( int i = 1; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items; i > 0; i-- )
+        {
+            String res = jcs.get( i + ":key" );
+            if ( res == null )
+            {
+                //assertNotNull( "[" + i + ":key] should not be null", res );
+            }
+        }
+
+        // test removal
+        jcs.remove( "300:key" );
+        assertNull( jcs.get( "TestKey" ) );
+
+//        System.out.println( jcs.getStats() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptorUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptorUnitTest.java
new file mode 100644
index 0000000..18ddffc
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteCacheServiceAdaptorUnitTest.java
@@ -0,0 +1,190 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.MockRemoteCacheService;
+import org.apache.commons.jcs.auxiliary.remote.util.RemoteCacheRequestFactory;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheResponse;
+import org.apache.commons.jcs.engine.CacheElement;
+
+import java.util.Collections;
+import java.util.Set;
+
+/** Unit tests for the adaptor. */
+public class RemoteCacheServiceAdaptorUnitTest
+    extends TestCase
+{
+    /** Verify that we balk and return an error. */
+    public void testProcessRequest_null()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        RemoteCacheRequest<String, String> request = null;
+
+        // DO WORK
+        RemoteCacheResponse<String> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertTrue( "Should have 'The request is null' in the errorMessage", result.getErrorMessage().indexOf( "The request is null" ) != -1 );
+        assertTrue( "Should have 'The request is null' in the toString", result.toString().indexOf( "The request is null" ) != -1 );
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_Get()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "test";
+        String key = "key";
+        long requesterId = 2;
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createGetRequest( cacheName, key, requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<Object> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong key.", key, remoteHttpCacheService.lastGetKey );
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_GetMatching()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "test";
+        String pattern = "pattern";
+        long requesterId = 2;
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createGetMatchingRequest( cacheName, pattern,
+                                                                                                  requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<Object> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong pattern.", pattern, remoteHttpCacheService.lastGetMatchingPattern );
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_GetMultiple()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "test";
+        Set<String> keys = Collections.emptySet();
+        long requesterId = 2;
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createGetMultipleRequest( cacheName, keys,
+                                                                                                  requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<Object> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong keys.", keys, remoteHttpCacheService.lastGetMultipleKeys );
+
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_Update()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "test";
+        String key = "key";
+        long requesterId = 2;
+        CacheElement<String, String> element = new CacheElement<String, String>( cacheName, key, null );
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createUpdateRequest( element, requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<String> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong object.", element, remoteHttpCacheService.lastUpdate );
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_Remove()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "test";
+        String key = "key";
+        long requesterId = 2;
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createRemoveRequest( cacheName, key, requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<String> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong key.", key, remoteHttpCacheService.lastRemoveKey );
+    }
+
+    /** Verify that the service is called. */
+    public void testProcessRequest_RemoveAll()
+    {
+        // SETUP
+        RemoteCacheServiceAdaptor<String, String> adaptor = new RemoteCacheServiceAdaptor<String, String>();
+
+        MockRemoteCacheService<String, String> remoteHttpCacheService = new MockRemoteCacheService<String, String>();
+        adaptor.setRemoteCacheService( remoteHttpCacheService );
+
+        String cacheName = "testRemoveALl";
+        long requesterId = 2;
+        RemoteCacheRequest<String, String> request = RemoteCacheRequestFactory.createRemoveAllRequest( cacheName, requesterId );
+
+        // DO WORK
+        RemoteCacheResponse<String> result = adaptor.processRequest( request );
+
+        // VERIFY
+        assertNotNull( "Should have a result.", result );
+        assertEquals( "Wrong cacheName.", cacheName, remoteHttpCacheService.lastRemoveAllCacheName );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServiceUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServiceUnitTest.java
new file mode 100644
index 0000000..049438d
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheServiceUnitTest.java
@@ -0,0 +1,181 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+
+import java.util.HashSet;
+
+/** Unit tests for the service. */
+public class RemoteHttpCacheServiceUnitTest
+    extends TestCase
+{
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        String cacheName = "test";
+        String key = "key";
+        long requesterId = 2;
+        CacheElement<String, String> element = new CacheElement<String, String>( cacheName, key, null );
+
+        // DO WORK
+        server.update( element, requesterId );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        // DO WORK
+        server.get( "region", "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatching_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        // DO WORK
+        server.getMatching( "region", "pattern", 0 );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMultiple_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        // DO WORK
+        server.getMultiple( "region", new HashSet<String>() );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemove_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        // DO WORK
+        server.remove( "region", "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAll_simple()
+        throws Exception
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteHttpCacheServerAttributes rcsa = new RemoteHttpCacheServerAttributes();
+        RemoteHttpCacheService<String, String> server =
+            new RemoteHttpCacheService<String, String>( manager, rcsa, cacheEventLogger );
+
+        // DO WORK
+        server.removeAll( "region" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactoryUnitTest.java
new file mode 100644
index 0000000..beb4c8c
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/http/server/RemoteHttpCacheSeviceFactoryUnitTest.java
@@ -0,0 +1,115 @@
+package org.apache.commons.jcs.auxiliary.remote.http.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
+import org.apache.commons.jcs.auxiliary.remote.http.behavior.IRemoteHttpCacheConstants;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.engine.logging.MockCacheEventLogger;
+
+import java.util.Properties;
+
+/** Unit tests for the factory */
+public class RemoteHttpCacheSeviceFactoryUnitTest
+    extends TestCase
+{
+    /** verify that we get the CacheEventLogger value */
+    public void testCreateRemoteHttpCacheService_WithLogger()
+    {
+        // SETUP
+        MockCompositeCacheManager manager = new MockCompositeCacheManager();
+        String className = MockCacheEventLogger.class.getName();
+
+        Properties props = new Properties();
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX
+            + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX, className );
+
+        boolean allowClusterGet = false;
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".allowClusterGet", String
+            .valueOf( allowClusterGet ) );
+
+        manager.setConfigurationProperties( props );
+
+        // DO WORK
+        RemoteHttpCacheService<String, String> result = RemoteHttpCacheSeviceFactory
+            .createRemoteHttpCacheService( manager );
+
+        // VERIFY
+        assertNotNull( "Should have a service.", result );
+    }
+
+    /** verify that we get the CacheEventLogger value */
+    public void testConfigureCacheEventLogger_Present()
+    {
+        // SETUP
+        String testPropertyValue = "This is the value";
+        String className = MockCacheEventLogger.class.getName();
+
+        Properties props = new Properties();
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX
+            + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX, className );
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_PREFIX
+            + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX + AuxiliaryCacheConfigurator.ATTRIBUTE_PREFIX
+            + ".testProperty", testPropertyValue );
+
+        // DO WORK
+        MockCacheEventLogger result = (MockCacheEventLogger) RemoteHttpCacheSeviceFactory
+            .configureCacheEventLogger( props );
+
+        // VERIFY
+        assertNotNull( "Should have a logger.", result );
+        assertEquals( "Property should be set.", testPropertyValue, result.getTestProperty() );
+    }
+
+    /** verify that we get the allowClusterGet value */
+    public void testConfigureRemoteCacheServerAttributes_allowClusterGetPresent()
+    {
+        // SETUP
+        boolean allowClusterGet = false;
+        Properties props = new Properties();
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".allowClusterGet", String
+            .valueOf( allowClusterGet ) );
+
+        // DO WORK
+        RemoteHttpCacheServerAttributes result = RemoteHttpCacheSeviceFactory
+            .configureRemoteHttpCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong allowClusterGet", allowClusterGet, result.isAllowClusterGet() );
+    }
+
+    /** verify that we get the startRegistry value */
+    public void testConfigureRemoteCacheServerAttributes_localClusterConsistencyPresent()
+    {
+        // SETUP
+        boolean localClusterConsistency = false;
+        Properties props = new Properties();
+        props.put( IRemoteHttpCacheConstants.HTTP_CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".localClusterConsistency",
+                   String.valueOf( localClusterConsistency ) );
+
+        // DO WORK
+        RemoteHttpCacheServerAttributes result = RemoteHttpCacheSeviceFactory
+            .configureRemoteHttpCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong localClusterConsistency", localClusterConsistency, result.isLocalClusterConsistency() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/BasicRemoteCacheClientServerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/BasicRemoteCacheClientServerUnitTest.java
new file mode 100644
index 0000000..2c544a3
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/BasicRemoteCacheClientServerUnitTest.java
@@ -0,0 +1,335 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.auxiliary.remote.MockRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.RemoteCacheManager;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.MockCompositeCacheManager;
+import org.apache.commons.jcs.engine.control.MockElementSerializer;
+import org.apache.commons.jcs.utils.net.HostNameUtil;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+
+/**
+ * These tests startup the remote server and make requests to it.
+ * <p/>
+ *
+ * @author Aaron Smuts
+ */
+ at FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class BasicRemoteCacheClientServerUnitTest extends Assert
+{
+    /**
+     * Server instance to use in the tests.
+     */
+    private static RemoteCacheServer<String, String> server = null;
+
+    /**
+     * the remote server port
+     */
+    private static int remotePort;
+
+    /**
+     * Starts the server. This is not in a setup, since the server is slow to kill right now.
+     */
+    @BeforeClass
+    public static void setup()
+    {
+        // Add some debug to try and find out why test fails on Jenkins/Continuum
+        try {
+            InetAddress lh = InetAddress.getByName("localhost");
+            System.out.println("localhost="+lh);
+            InetAddress ina=InetAddress.getLocalHost();
+            System.out.println("InetAddress.getLocalHost()="+ina);
+            // Iterate all NICs (network interface cards)...
+            for ( Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces.hasMoreElements(); )
+            {
+                NetworkInterface iface = ifaces.nextElement();
+                // Iterate all IP addresses assigned to each card...
+                for ( Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); )
+                {
+                    InetAddress inetAddr = inetAddrs.nextElement();
+                    boolean loopbackAddress = inetAddr.isLoopbackAddress();
+                    boolean siteLocalAddress = inetAddr.isSiteLocalAddress();
+                    System.out.println("Found: "+ inetAddr +
+                            " isLoopback: " + loopbackAddress + 
+                            " isSiteLocal: " + siteLocalAddress +
+                            ((!loopbackAddress && siteLocalAddress) ? " *" : ""));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        // end of debug
+        String configFile = "TestRemoteCacheClientServer.ccf";
+        server = RemoteCacheServerStartupUtil.startServerUsingProperties(configFile);
+        remotePort = server.remoteCacheServerAttributes.getRemotePort();
+    }
+
+    @AfterClass
+    public static void stop() throws IOException
+    {
+        if (server != null) { // in case setup failed, no point throwing NPE as well
+            server.shutdown("localhost", remotePort);
+        }
+        // Debug: unfortunately Surefire restarts JVM so log files get overwritten
+        // There's probably a better way to fix this ...
+        java.io.File jcsLog = new java.io.File("target/jcs.log");
+        java.io.File logSave = new java.io.File("target/BasicRemoteCacheClientServerUnitTest_jcs.log");
+        System.out.println("Renamed log file? "+jcsLog.renameTo(logSave));
+    }
+
+    /**
+     * Verify that we can start the remote cache server. Send an item to the remote. Verify that the
+     * remote put count goes up. If we go through JCS, the manager will be shared and we will get
+     * into an endless loop. We will use a mock cache manager instead.
+     * <p/>
+     * The remote server uses the real JCS. We can verify that items are added to JCS behind the
+     * server by calling get. We cannot access it directly via JCS since it is serialized.
+     * <p/>
+     * This test uses a mock injected client to test a normal server.
+     * <p/>
+     *
+     * @throws Exception
+     */
+    @Test
+    public void test1SinglePut()
+            throws Exception
+            {
+        // SETUP
+        MockCompositeCacheManager compositeCacheManager = new MockCompositeCacheManager();
+
+        RemoteCacheAttributes attributes = new RemoteCacheAttributes();
+        attributes.setRemoteHost("localhost");
+        attributes.setLocalPort(1202);
+        attributes.setRemotePort(remotePort);
+
+        RemoteCacheManager remoteCacheManager = RemoteCacheManager.getInstance(attributes, compositeCacheManager, new MockCacheEventLogger(), new MockElementSerializer());
+        String regionName = "testSinglePut";
+        AuxiliaryCache<String, String> cache = remoteCacheManager.getCache(regionName);
+
+        // DO WORK
+        int numPutsPrior = server.getPutCount();
+        ICacheElement<String, String> element = new CacheElement<String, String>(regionName, "key", "value");
+        cache.update(element);
+        SleepUtil.sleepAtLeast(200);
+
+        // VERIFY
+        try
+        {
+            assertEquals("Cache is alive", CacheStatus.ALIVE, cache.getStatus());
+            assertEquals("Wrong number of puts", 1, server.getPutCount() - numPutsPrior);
+        }
+        catch (junit.framework.AssertionFailedError e)
+        {
+            System.out.println(cache.getStats());
+            System.out.println(server.getStats());
+            throw e;
+        }
+
+        // DO WORK
+        ICacheElement<String, String> result = cache.get("key");
+
+        // VERIFY
+        assertEquals("Wrong element.", element.getVal(), result.getVal());
+            }
+
+    /**
+     * Verify that we can remove an item via the remote server.
+     * <p/>
+     *
+     * @throws Exception
+     */
+    @Test
+    public void test2PutRemove()
+            throws Exception
+            {
+        // SETUP
+        MockCompositeCacheManager compositeCacheManager = new MockCompositeCacheManager();
+
+        RemoteCacheAttributes attributes = new RemoteCacheAttributes();
+        attributes.setRemoteHost("localhost");
+        attributes.setLocalPort(1202);
+        attributes.setRemotePort(remotePort);
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RemoteCacheManager remoteCacheManager = RemoteCacheManager.getInstance(attributes, compositeCacheManager, cacheEventLogger, null);
+        String regionName = "testPutRemove";
+        AuxiliaryCache<String, String> cache = remoteCacheManager.getCache(regionName);
+
+        // DO WORK
+        int numPutsPrior = server.getPutCount();
+        ICacheElement<String, String> element = new CacheElement<String, String>(regionName, "key", "value");
+        cache.update(element);
+        SleepUtil.sleepAtLeast(50);
+
+        // VERIFY
+        try
+        {
+            assertEquals("Cache is alive", CacheStatus.ALIVE, cache.getStatus());
+            assertEquals("Wrong number of puts", 1, server.getPutCount() - numPutsPrior);
+        }
+        catch (junit.framework.AssertionFailedError e)
+        {
+            System.out.println(cache.getStats());
+            System.out.println(server.getStats());
+            throw e;
+        }
+
+        // DO WORK
+        ICacheElement<String, String> result = cache.get("key");
+
+        // VERIFY
+        assertEquals("Wrong element.", element.getVal(), result.getVal());
+
+        // DO WORK
+        cache.remove("key");
+        SleepUtil.sleepAtLeast(200);
+        ICacheElement<String, String> resultAfterRemote = cache.get("key");
+
+        // VERIFY
+        assertNull("Element resultAfterRemote should be null.", resultAfterRemote);
+            }
+
+    /**
+     * Register a listener with the server. Send an update. Verify that the listener received it.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void test3PutAndListen()
+            throws Exception
+            {
+        // SETUP
+        MockCompositeCacheManager compositeCacheManager = new MockCompositeCacheManager();
+
+        RemoteCacheAttributes attributes = new RemoteCacheAttributes();
+        attributes.setRemoteHost("localhost");
+        attributes.setLocalPort(1202);
+        attributes.setRemotePort(remotePort);
+
+        RemoteCacheManager remoteCacheManager = RemoteCacheManager.getInstance(attributes, compositeCacheManager, new MockCacheEventLogger(), new MockElementSerializer());
+        String regionName = "testPutAndListen";
+        AuxiliaryCache<String, String> cache = remoteCacheManager.getCache(regionName);
+
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+        server.addCacheListener(regionName, listener);
+
+        // DO WORK
+        int numPutsPrior = server.getPutCount();
+        ICacheElement<String, String> element = new CacheElement<String, String>(regionName, "key", "value");
+        cache.update(element);
+        SleepUtil.sleepAtLeast(50);
+
+        // VERIFY
+        try
+        {
+            assertEquals("Cache is alive", CacheStatus.ALIVE, cache.getStatus());
+            assertEquals("Wrong number of puts", 1, server.getPutCount() - numPutsPrior);
+            assertEquals("Wrong number of puts to listener.", 1, listener.putCount);
+        }
+        catch (junit.framework.AssertionFailedError e)
+        {
+            System.out.println(cache.getStats());
+            System.out.println(server.getStats());
+            throw e;
+        }
+        finally
+        {
+            // remove from all regions.
+            server.removeCacheListener(listener);
+        }
+            }
+
+    /**
+     * Register a listener with the server. Send an update. Verify that the listener received it.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void test4PutaMultipleAndListen()
+            throws Exception
+            {
+        // SETUP
+        MockCompositeCacheManager compositeCacheManager = new MockCompositeCacheManager();
+
+        RemoteCacheAttributes attributes = new RemoteCacheAttributes();
+        attributes.setRemoteHost("localhost");
+        attributes.setLocalPort(1202);
+        attributes.setRemotePort(remotePort);
+
+        RemoteCacheManager remoteCacheManager = RemoteCacheManager.getInstance(attributes, compositeCacheManager, new MockCacheEventLogger(), new MockElementSerializer());
+        String regionName = "testPutaMultipleAndListen";
+        AuxiliaryCache<String, String> cache = remoteCacheManager.getCache(regionName);
+
+        MockRemoteCacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+        server.addCacheListener(regionName, listener);
+
+        // DO WORK
+        int numPutsPrior = server.getPutCount();
+        int numToPut = 100;
+        for (int i = 0; i < numToPut; i++)
+        {
+            ICacheElement<String, String> element = new CacheElement<String, String>(regionName, "key" + 1, "value" + i);
+            cache.update(element);
+        }
+        SleepUtil.sleepAtLeast(500);
+
+        // VERIFY
+        try
+        {
+            assertEquals("Cache is alive", CacheStatus.ALIVE, cache.getStatus());
+            assertEquals("Wrong number of puts", numToPut, server.getPutCount() - numPutsPrior);
+            assertEquals("Wrong number of puts to listener.", numToPut, listener.putCount);
+        }
+        catch (junit.framework.AssertionFailedError e)
+        {
+            System.out.println(cache.getStats());
+            System.out.println(server.getStats());
+            throw e;
+        }
+            }
+
+    @Test
+    public void testLocalHost() throws Exception
+    {
+        final InetAddress byName = InetAddress.getByName("localhost");
+        assertTrue("Expected localhost (" + byName.getHostAddress() + ") to be a loopback address", byName.isLoopbackAddress());
+        final InetAddress localHost = HostNameUtil.getLocalHostLANAddress();
+        assertTrue("Expected getLocalHostLANAddress() (" + localHost + ") to return a site local address", localHost.isSiteLocalAddress());
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/MockRMISocketFactory.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/MockRMISocketFactory.java
new file mode 100644
index 0000000..e007753
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/MockRMISocketFactory.java
@@ -0,0 +1,88 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.server.RMISocketFactory;
+
+/** For testing the custom socket factory configuration */
+public class MockRMISocketFactory
+    extends RMISocketFactory
+    implements Serializable
+{
+    /** Don't change */
+    private static final long serialVersionUID = 1056199478581218676L;
+
+    /** for testing automatic property configuration. */
+    private String testStringProperty;
+
+    /**
+     * @param host
+     * @param port
+     * @return Socket
+     * @throws IOException
+     */
+    @Override
+    public Socket createSocket( String host, int port )
+        throws IOException
+    {
+//        System.out.println( "Creating socket" );
+
+        Socket socket = new Socket();
+        socket.setSoTimeout( 1000 );
+        socket.setSoLinger( false, 0 );
+        socket.connect( new InetSocketAddress( host, port ), 1000 );
+        return socket;
+    }
+
+    /**
+     * @param port
+     * @return ServerSocket
+     * @throws IOException
+     */
+    @Override
+    public ServerSocket createServerSocket( int port )
+        throws IOException
+    {
+//        System.out.println( "Creating server socket" );
+
+        return new ServerSocket( port );
+    }
+
+    /**
+     * @param testStringProperty the testStringProperty to set
+     */
+    public void setTestStringProperty( String testStringProperty )
+    {
+        this.testStringProperty = testStringProperty;
+    }
+
+    /**
+     * @return the testStringProperty
+     */
+    public String getTestStringProperty()
+    {
+        return testStringProperty;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunnerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunnerUnitTest.java
new file mode 100644
index 0000000..ba07886
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunnerUnitTest.java
@@ -0,0 +1,49 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+
+/** Unit tests for the registry keep alive runner. */
+public class RegistryKeepAliveRunnerUnitTest
+    extends TestCase
+{
+    /** Verify that we get the appropriate event log */
+    public void testCheckAndRestoreIfNeeded_failure()
+    {
+        // SETUP
+        String host = "localhost";
+        int port = 1234;
+        String service = "doesn'texist";
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+
+        RegistryKeepAliveRunner runner = new RegistryKeepAliveRunner( host, port, service );
+        runner.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        runner.checkAndRestoreIfNeeded();
+
+        // VERIFY
+        // 1 for the lookup, one for the rebind since the server isn't created yet
+        assertEquals( "error tally", 2, cacheEventLogger.errorEventCalls );
+        //System.out.println( cacheEventLogger.errorMessages );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributesUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributesUnitTest.java
new file mode 100644
index 0000000..535f130
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributesUnitTest.java
@@ -0,0 +1,64 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+
+/**
+ * Tests for the remote cache server attributes.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheServerAttributesUnitTest
+    extends TestCase
+{
+
+    /**
+     * Verify that we get a string, even if not attributes are set.
+     */
+    public void testToString()
+    {
+        RemoteCacheServerAttributes attributes = new RemoteCacheServerAttributes();
+        assertNotNull( "Should have a string.", attributes.toString() );
+    }
+
+    /**
+     * Verify that the type is set correctly and that the correct name is returned for the type.
+     */
+    public void testSetRemoteTypeName_local()
+    {
+        RemoteCacheServerAttributes attributes = new RemoteCacheServerAttributes();
+        attributes.setRemoteTypeName( "LOCAL" );
+        assertEquals( "Wrong type.", RemoteType.LOCAL, attributes.getRemoteType() );
+        assertEquals( "Wrong name", "LOCAL", attributes.getRemoteTypeName() );
+    }
+
+    /**
+     * Verify that the type is set correctly and that the correct name is returned for the type.
+     */
+    public void testSetRemoteTypeName_cluster()
+    {
+        RemoteCacheServerAttributes attributes = new RemoteCacheServerAttributes();
+        attributes.setRemoteTypeName( "CLUSTER" );
+        assertEquals( "Wrong type.", RemoteType.CLUSTER, attributes.getRemoteType() );
+        assertEquals( "Wrong name", "CLUSTER", attributes.getRemoteTypeName() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactoryUnitTest.java
new file mode 100644
index 0000000..22ef757
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactoryUnitTest.java
@@ -0,0 +1,202 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.behavior.ICommonRemoteCacheAttributes;
+import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheConstants;
+
+import java.rmi.server.RMISocketFactory;
+import java.util.Properties;
+
+/** Unit tests for the factory */
+public class RemoteCacheServerFactoryUnitTest
+    extends TestCase
+{
+    /** verify that we get the timeout value */
+    public void testConfigureRemoteCacheServerAttributes_eventQueuePoolName()
+    {
+        // SETUP
+        String eventQueuePoolName = "specialName";
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".EventQueuePoolName", eventQueuePoolName );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong eventQueuePoolName", eventQueuePoolName, result.getEventQueuePoolName() );
+    }
+
+    /** verify that we get the timeout value */
+    public void testConfigureRemoteCacheServerAttributes_timeoutPresent()
+    {
+        // SETUP
+        int timeout = 123245;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.SOCKET_TIMEOUT_MILLIS, String.valueOf( timeout ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong timeout", timeout, result.getRmiSocketFactoryTimeoutMillis() );
+    }
+
+    /** verify that we get the timeout value */
+    public void testConfigureRemoteCacheServerAttributes_timeoutNotPresent()
+    {
+        // SETUP
+        Properties props = new Properties();
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong timeout", ICommonRemoteCacheAttributes.DEFAULT_RMI_SOCKET_FACTORY_TIMEOUT_MILLIS, result.getRmiSocketFactoryTimeoutMillis() );
+    }
+
+    /** verify that we get the registryKeepAliveDelayMillis value */
+    public void testConfigureRemoteCacheServerAttributes_registryKeepAliveDelayMillisPresent()
+    {
+        // SETUP
+        int registryKeepAliveDelayMillis = 123245;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".registryKeepAliveDelayMillis", String.valueOf( registryKeepAliveDelayMillis ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong registryKeepAliveDelayMillis", registryKeepAliveDelayMillis, result.getRegistryKeepAliveDelayMillis() );
+    }
+
+    /** verify that we get the useRegistryKeepAlive value */
+    public void testConfigureRemoteCacheServerAttributes_useRegistryKeepAlivePresent()
+    {
+        // SETUP
+        boolean useRegistryKeepAlive = false;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".useRegistryKeepAlive", String.valueOf( useRegistryKeepAlive ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong useRegistryKeepAlive", useRegistryKeepAlive, result.isUseRegistryKeepAlive() );
+    }
+
+    /** verify that we get the startRegistry value */
+    public void testConfigureRemoteCacheServerAttributes_startRegistryPresent()
+    {
+        // SETUP
+        boolean startRegistry = false;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".startRegistry", String.valueOf( startRegistry ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong startRegistry", startRegistry, result.isStartRegistry() );
+    }
+
+    /** verify that we get the registryKeepAliveDelayMillis value */
+    public void testConfigureRemoteCacheServerAttributes_rmiSocketFactoryTimeoutMillisPresent()
+    {
+        // SETUP
+        int rmiSocketFactoryTimeoutMillis = 123245;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".rmiSocketFactoryTimeoutMillis", String.valueOf( rmiSocketFactoryTimeoutMillis ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong rmiSocketFactoryTimeoutMillis", rmiSocketFactoryTimeoutMillis, result.getRmiSocketFactoryTimeoutMillis() );
+    }
+
+    /** verify that we get the startRegistry value */
+    public void testConfigureRemoteCacheServerAttributes_allowClusterGetPresent()
+    {
+        // SETUP
+        boolean allowClusterGet = false;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".allowClusterGet", String.valueOf( allowClusterGet ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong allowClusterGet", allowClusterGet, result.isAllowClusterGet() );
+    }
+
+    /** verify that we get the startRegistry value */
+    public void testConfigureRemoteCacheServerAttributes_localClusterConsistencyPresent()
+    {
+        // SETUP
+        boolean localClusterConsistency = false;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CACHE_SERVER_ATTRIBUTES_PROPERTY_PREFIX + ".localClusterConsistency", String.valueOf( localClusterConsistency ) );
+
+        // DO WORK
+        RemoteCacheServerAttributes result = RemoteCacheServerFactory.configureRemoteCacheServerAttributes( props );
+
+        // VERIFY
+        assertEquals( "Wrong localClusterConsistency", localClusterConsistency, result.isLocalClusterConsistency() );
+    }
+
+    /** verify that we get the timeout value */
+    public void testConfigureObjectSpecificCustomFactory_withProperty()
+    {
+        // SETUP
+        String testValue = "123245";
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, MockRMISocketFactory.class.getName() );
+        props.put( IRemoteCacheConstants.CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX + ".testStringProperty", testValue );
+
+        // DO WORK
+        RMISocketFactory result = RemoteCacheServerFactory.configureObjectSpecificCustomFactory( props );
+
+        // VERIFY
+        assertNotNull( "Should have a custom socket factory.", result );
+        assertEquals( "Wrong testValue", testValue, ((MockRMISocketFactory)result).getTestStringProperty() );
+    }
+
+    /** verify that we get the timeout value */
+    public void testConfigureObjectSpecificCustomFactory_withProperty_TimeoutConfigurableRMIScoketFactory()
+    {
+        // SETUP
+        int readTimeout = 1234;
+        int openTimeout = 1234;
+        Properties props = new Properties();
+        props.put( IRemoteCacheConstants.CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX, TimeoutConfigurableRMISocketFactory.class.getName() );
+        props.put( IRemoteCacheConstants.CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX + ".readTimeout", String.valueOf( readTimeout ) );
+        props.put( IRemoteCacheConstants.CUSTOM_RMI_SOCKET_FACTORY_PROPERTY_PREFIX + ".openTimeout", String.valueOf( openTimeout ) );
+
+        // DO WORK
+        RMISocketFactory result = RemoteCacheServerFactory.configureObjectSpecificCustomFactory( props );
+
+        // VERIFY
+        assertNotNull( "Should have a custom socket factory.", result );
+        assertEquals( "Wrong readTimeout", readTimeout, ((TimeoutConfigurableRMISocketFactory)result).getReadTimeout() );
+        assertEquals( "Wrong readTimeout", openTimeout, ((TimeoutConfigurableRMISocketFactory)result).getOpenTimeout() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerStartupUtil.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerStartupUtil.java
new file mode 100644
index 0000000..b6228e2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerStartupUtil.java
@@ -0,0 +1,104 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.utils.net.HostNameUtil;
+import org.apache.commons.jcs.utils.props.PropertyLoader;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.UnknownHostException;
+import java.util.Properties;
+
+/**
+ *Starts the registry and runs the server via the factory.
+ *<p>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheServerStartupUtil
+{
+    /** The logger */
+    private static final Log log = LogFactory.getLog( RemoteCacheServerStartupUtil.class );
+
+    /** Registry to use in the test. */
+    private static final int DEFAULT_REGISTRY_PORT = 1101;
+
+    /**
+     * Starts the registry on port "registry.port"
+     * <p>
+     * @param propsFileName
+     * @return RemoteCacheServer
+     */
+    public static <K extends Serializable, V extends Serializable> RemoteCacheServer<K, V> startServerUsingProperties( String propsFileName )
+    {
+        // TODO load from props file or get as init param or get from jndi, or
+        // all three
+        int registryPort = DEFAULT_REGISTRY_PORT;
+
+        Properties props = PropertyLoader.loadProperties( propsFileName );
+        if ( props != null )
+        {
+            String portS = props.getProperty( "registry.port", String.valueOf( DEFAULT_REGISTRY_PORT ) );
+
+            try
+            {
+                registryPort = Integer.parseInt( portS );
+            }
+            catch ( NumberFormatException e )
+            {
+                log.error( "Problem converting port to an int.", e );
+            }
+        }
+
+        // we will always use the local machine for the registry
+        try
+        {
+            String registryHost = HostNameUtil.getLocalHostAddress();
+
+            if ( log.isDebugEnabled() )
+            {
+                log.debug( "registryHost = [" + registryHost + "]" );
+            }
+
+            if ( "localhost".equals( registryHost ) || "127.0.0.1".equals( registryHost ) )
+            {
+                log.warn( "The local address [" + registryHost
+                    + "] is INVALID.  Other machines must be able to use the address to reach this server." );
+            }
+
+            try
+            {
+                RemoteCacheServerFactory.startup( registryHost, registryPort, "/" + propsFileName );
+            }
+            catch ( IOException e )
+            {
+                log.error( "Problem starting remote cache server.", e );
+            }
+        }
+        catch ( UnknownHostException e )
+        {
+            log.error( "Could not get local address to use for the registry!", e );
+        }
+
+        return RemoteCacheServerFactory.getRemoteCacheServer();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerUnitTest.java
new file mode 100644
index 0000000..1494016
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerUnitTest.java
@@ -0,0 +1,501 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.MockCacheEventLogger;
+import org.apache.commons.jcs.auxiliary.remote.MockRemoteCacheListener;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.IRemoteCacheServerAttributes;
+import org.apache.commons.jcs.auxiliary.remote.server.behavior.RemoteType;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Since the server does not know that it is a server, it is easy to unit test. The factory does all
+ * the rmi work.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class RemoteCacheServerUnitTest
+    extends TestCase
+{
+    /**
+     * Add a listener. Pass the id of 0, verify that the server sets a new listener id. Do another
+     * and verify that the second gets an id of 2.
+     * <p>
+     * @throws Exception
+     */
+    public void testAddListenerToCache_LOCALtype()
+        throws Exception
+    {
+        // SETUP
+        String expectedIp1 = "adfasdf";
+        String expectedIp2 = "adsfadsafaf";
+
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockRemoteCacheListener<String, String> mockListener1 = new MockRemoteCacheListener<String, String>();
+        mockListener1.remoteType = RemoteType.LOCAL;
+        mockListener1.localAddress = expectedIp1;
+        MockRemoteCacheListener<String, String> mockListener2 = new MockRemoteCacheListener<String, String>();
+        mockListener1.remoteType = RemoteType.LOCAL;
+        mockListener2.localAddress = expectedIp2;
+
+        String cacheName = "testAddListener";
+
+        // DO WORK
+        server.addCacheListener( cacheName, mockListener1 );
+        server.addCacheListener( cacheName, mockListener2 );
+
+        // VERIFY
+        assertEquals( "Wrong listener id.", 1, mockListener1.getListenerId() );
+        assertEquals( "Wrong listener id.", 2, mockListener2.getListenerId() );
+        assertEquals( "Wrong ip.", expectedIp1, server.getExtraInfoForRequesterId( 1 ) );
+        assertEquals( "Wrong ip.", expectedIp2, server.getExtraInfoForRequesterId( 2 ) );
+    }
+
+    /**
+     * Add a listener. Pass the id of 0, verify that the server sets a new listener id. Do another
+     * and verify that the second gets an id of 2.
+     * <p>
+     * @throws Exception
+     */
+    public void testAddListenerToCache_CLUSTERtype()
+        throws Exception
+    {
+        // SETUP
+        String expectedIp1 = "adfasdf";
+        String expectedIp2 = "adsfadsafaf";
+
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockRemoteCacheListener<String, String> mockListener1 = new MockRemoteCacheListener<String, String>();
+        mockListener1.remoteType = RemoteType.CLUSTER;
+        mockListener1.localAddress = expectedIp1;
+        MockRemoteCacheListener<String, String> mockListener2 = new MockRemoteCacheListener<String, String>();
+        mockListener1.remoteType = RemoteType.CLUSTER;
+        mockListener2.localAddress = expectedIp2;
+
+        String cacheName = "testAddListener";
+
+        // DO WORK
+        server.addCacheListener( cacheName, mockListener1 );
+        server.addCacheListener( cacheName, mockListener2 );
+
+        // VERIFY
+        assertEquals( "Wrong listener id.", 1, mockListener1.getListenerId() );
+        assertEquals( "Wrong listener id.", 2, mockListener2.getListenerId() );
+        assertEquals( "Wrong ip.", expectedIp1, server.getExtraInfoForRequesterId( 1 ) );
+        assertEquals( "Wrong ip.", expectedIp2, server.getExtraInfoForRequesterId( 2 ) );
+    }
+
+    /**
+     * Add a listener. Pass the id of 0, verify that the server sets a new listener id. Do another
+     * and verify that the second gets an id of 2.
+     * <p>
+     * @throws Exception
+     */
+    public void testAddListener_ToAll()
+        throws Exception
+    {
+        // SETUP
+        String expectedIp1 = "adfasdf";
+        String expectedIp2 = "adsfadsafaf";
+
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockRemoteCacheListener<String, String> mockListener1 = new MockRemoteCacheListener<String, String>();
+        mockListener1.localAddress = expectedIp1;
+        MockRemoteCacheListener<String, String> mockListener2 = new MockRemoteCacheListener<String, String>();
+        mockListener2.localAddress = expectedIp2;
+
+        // DO WORK
+        // don't specify the cache name
+        server.addCacheListener( mockListener1 );
+        server.addCacheListener( mockListener2 );
+
+        // VERIFY
+        assertEquals( "Wrong listener id.", 1, mockListener1.getListenerId() );
+        assertEquals( "Wrong listener id.", 2, mockListener2.getListenerId() );
+        assertEquals( "Wrong ip.", expectedIp1, server.getExtraInfoForRequesterId( 1 ) );
+        assertEquals( "Wrong ip.", expectedIp2, server.getExtraInfoForRequesterId( 2 ) );
+    }
+
+    /**
+     * Add a listener. Pass the id of 0, verify that the server sets a new listener id. Do another
+     * and verify that the second gets an id of 2. Call remove Listener and verify that it is
+     * removed.
+     * <p>
+     * @throws Exception
+     */
+    public void testAddListener_ToAllThenRemove()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockRemoteCacheListener<String, String> mockListener1 = new MockRemoteCacheListener<String, String>();
+        MockRemoteCacheListener<String, String> mockListener2 = new MockRemoteCacheListener<String, String>();
+
+        String cacheName = "testAddListenerToAllThenRemove";
+
+        // DO WORK
+        server.addCacheListener( cacheName, mockListener1 );
+        server.addCacheListener( cacheName, mockListener2 );
+
+        // VERIFY
+        assertEquals( "Wrong number of listeners.", 2, server.getCacheListeners( cacheName ).eventQMap.size() );
+        assertEquals( "Wrong listener id.", 1, mockListener1.getListenerId() );
+        assertEquals( "Wrong listener id.", 2, mockListener2.getListenerId() );
+
+        // DO WORK
+        server.removeCacheListener( cacheName, mockListener1.getListenerId() );
+        assertEquals( "Wrong number of listeners.", 1, server.getCacheListeners( cacheName ).eventQMap.size() );
+    }
+
+    /**
+     * Add a listener. Pass the id of 0, verify that the server sets a new listener id. Do another
+     * and verify that the second gets an id of 2. Call remove Listener and verify that it is
+     * removed.
+     * <p>
+     * @throws Exception
+     */
+    public void testAddListener_ToAllThenRemove_clusterType()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockRemoteCacheListener<String, String> mockListener1 = new MockRemoteCacheListener<String, String>();
+        mockListener1.remoteType = RemoteType.CLUSTER;
+        MockRemoteCacheListener<String, String> mockListener2 = new MockRemoteCacheListener<String, String>();
+        mockListener2.remoteType = RemoteType.CLUSTER;
+
+        String cacheName = "testAddListenerToAllThenRemove";
+
+        // DO WORK
+        server.addCacheListener( cacheName, mockListener1 );
+        server.addCacheListener( cacheName, mockListener2 );
+
+        // VERIFY
+        assertEquals( "Wrong number of listeners.", 0, server.getCacheListeners( cacheName ).eventQMap.size() );
+        assertEquals( "Wrong number of listeners.", 2, server.getClusterListeners( cacheName ).eventQMap.size() );
+        assertEquals( "Wrong listener id.", 1, mockListener1.getListenerId() );
+        assertEquals( "Wrong listener id.", 2, mockListener2.getListenerId() );
+
+        // DO WORK
+        server.removeCacheListener( cacheName, mockListener1.getListenerId() );
+        assertEquals( "Wrong number of listeners.", 1, server.getClusterListeners( cacheName ).eventQMap.size() );
+        assertNull( "Should be no entry in the ip map.", server.getExtraInfoForRequesterId( 1 ) );
+    }
+
+    /**
+     * Register a listener and then verify that it is called when we put using a different listener
+     * id.
+     * @throws Exception
+     */
+    public void testSimpleRegisterListenerAndPut()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+
+        MockRemoteCacheListener<String, Long> mockListener = new MockRemoteCacheListener<String, Long>();
+        RemoteCacheServer<String, Long> server = new RemoteCacheServer<String, Long>( rcsa );
+
+        String cacheName = "testSimpleRegisterListenerAndPut";
+        server.addCacheListener( cacheName, mockListener );
+
+        // DO WORK
+        List<ICacheElement<String, Long>> inputItems = new LinkedList<ICacheElement<String, Long>>();
+        int numToPut = 10;
+
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            ICacheElement<String, Long> element = new CacheElement<String, Long>( cacheName, String.valueOf( i ), Long.valueOf( i ) );
+            inputItems.add( element );
+            server.update( element, 9999 );
+        }
+
+        Thread.sleep( 100 );
+        Thread.yield();
+        Thread.sleep( 100 );
+
+        // VERIFY
+        assertEquals( "Wrong number of items put to listener.", numToPut, mockListener.putItems.size() );
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            assertEquals( "Wrong item.", inputItems.get( i ), mockListener.putItems.get( i ) );
+        }
+    }
+
+    /**
+     * Register a listener and then verify that it is called when we put using a different listener
+     * id. The updates should come from a cluster listener and local cluster consistency should be
+     * true.
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleRegisterListenerAndPut_FromClusterWithLCC()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setLocalClusterConsistency( true );
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, Long> server = new RemoteCacheServer<String, Long>( rcsa );
+
+        // this is to get the listener id for inserts.
+        MockRemoteCacheListener<String, Long> clusterListener = new MockRemoteCacheListener<String, Long>();
+        clusterListener.remoteType = RemoteType.CLUSTER;
+
+        // this should get the updates
+        MockRemoteCacheListener<String, Long> localListener = new MockRemoteCacheListener<String, Long>();
+        localListener.remoteType = RemoteType.LOCAL;
+
+        String cacheName = "testSimpleRegisterListenerAndPut_FromClusterWithLCC";
+        server.addCacheListener( cacheName, clusterListener );
+        server.addCacheListener( cacheName, localListener );
+
+        // DO WORK
+        List<ICacheElement<String, Long>> inputItems = new LinkedList<ICacheElement<String,Long>>();
+        int numToPut = 10;
+
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            ICacheElement<String, Long> element = new CacheElement<String, Long>( cacheName, String.valueOf( i ), Long.valueOf( i ) );
+            inputItems.add( element );
+            // update using the cluster listener id
+            server.update( element, clusterListener.getListenerId() );
+        }
+
+        SleepUtil.sleepAtLeast( 200 );
+        Thread.yield();
+        SleepUtil.sleepAtLeast( 200 );
+
+        // VERIFY
+        assertEquals( "Wrong number of items put to listener.", numToPut, localListener.putItems.size() );
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            assertEquals( "Wrong item.", inputItems.get( i ), localListener.putItems.get( i ) );
+        }
+    }
+
+    /**
+     * Register a listener and then verify that it is called when we put using a different listener
+     * id.
+     * @throws Exception
+     */
+    public void testSimpleRegisterListenerAndRemove()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+
+        MockRemoteCacheListener<String, String> mockListener = new MockRemoteCacheListener<String, String>();
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        String cacheName = "testSimpleRegisterListenerAndPut";
+        server.addCacheListener( cacheName, mockListener );
+
+        // DO WORK
+        int numToPut = 10;
+
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            // use a junk listener id
+            server.remove( cacheName, String.valueOf( i ), 9999 );
+        }
+
+        Thread.sleep( 100 );
+        Thread.yield();
+        Thread.sleep( 100 );
+
+        // VERIFY
+        assertEquals( "Wrong number of items removed from listener.", numToPut, mockListener.removedKeys.size() );
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            assertEquals( "Wrong key.", String.valueOf( i ), mockListener.removedKeys.get( i ) );
+        }
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdate_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        ICacheElement<String, String> item = new CacheElement<String, String>( "region", "key", "value" );
+
+        // DO WORK
+        server.update( item );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGet_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        server.get( "region", "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMatching_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        server.getMatching( "region", "pattern", 0 );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetMultiple_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        server.getMultiple( "region", new HashSet<String>() );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemove_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        server.remove( "region", "key" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+
+    /**
+     * Verify event log calls.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAll_simple()
+        throws Exception
+    {
+        // SETUP
+        IRemoteCacheServerAttributes rcsa = new RemoteCacheServerAttributes();
+        rcsa.setConfigFileName( "/TestRemoteCacheServer.ccf" );
+        RemoteCacheServer<String, String> server = new RemoteCacheServer<String, String>( rcsa );
+
+        MockCacheEventLogger cacheEventLogger = new MockCacheEventLogger();
+        server.setCacheEventLogger( cacheEventLogger );
+
+        // DO WORK
+        server.removeAll( "region" );
+
+        // VERIFY
+        assertEquals( "Start should have been called.", 1, cacheEventLogger.startICacheEventCalls );
+        assertEquals( "End should have been called.", 1, cacheEventLogger.endICacheEventCalls );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactoryUnitTest.java
new file mode 100644
index 0000000..ed76c0a
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/server/TimeoutConfigurableRMISocketFactoryUnitTest.java
@@ -0,0 +1,53 @@
+package org.apache.commons.jcs.auxiliary.remote.server;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/** Unit tests for the custom factory */
+public class TimeoutConfigurableRMISocketFactoryUnitTest
+    extends TestCase
+{
+    /**
+     * Simple test to see that we can create a server socket and connect.
+     * <p>
+     * @throws IOException
+     */
+    public void testCreateAndConnect() throws IOException
+    {
+        // SETUP
+        int port = 3455;
+        String host = "localhost";
+        TimeoutConfigurableRMISocketFactory factory = new TimeoutConfigurableRMISocketFactory();
+
+        // DO WORK
+        ServerSocket serverSocket = factory.createServerSocket( port );
+        Socket socket = factory.createSocket( host, port );
+        socket.close();
+        serverSocket.close();
+
+        // VERIFY
+        // passive, no errors
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactoryUnitTest.java
new file mode 100644
index 0000000..b12824f
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/remote/util/RemoteCacheRequestFactoryUnitTest.java
@@ -0,0 +1,144 @@
+package org.apache.commons.jcs.auxiliary.remote.util;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteCacheRequest;
+import org.apache.commons.jcs.auxiliary.remote.value.RemoteRequestType;
+import org.apache.commons.jcs.engine.CacheElement;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Set;
+
+/** Unit tests for the request creator. */
+public class RemoteCacheRequestFactoryUnitTest
+    extends TestCase
+{
+    /** Simple test */
+    public void testCreateGetRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        Serializable key = "key";
+        long requesterId = 2;
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result =
+            RemoteCacheRequestFactory.createGetRequest( cacheName, key, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.GET, result.getRequestType() );
+    }
+
+    /** Simple test */
+    public void testCreateGetMatchingRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        String pattern = "pattern";
+        long requesterId = 2;
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result =
+            RemoteCacheRequestFactory.createGetMatchingRequest( cacheName, pattern, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.GET_MATCHING, result.getRequestType() );
+    }
+
+    /** Simple test */
+    public void testCreateGetMultipleRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        Set<Serializable> keys = Collections.emptySet();
+        long requesterId = 2;
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result =
+            RemoteCacheRequestFactory.createGetMultipleRequest( cacheName, keys, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.GET_MULTIPLE, result.getRequestType() );
+    }
+
+    /** Simple test */
+    public void testCreateRemoveRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        Serializable key = "key";
+        long requesterId = 2;
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result = RemoteCacheRequestFactory
+            .createRemoveRequest( cacheName, key, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.REMOVE, result.getRequestType() );
+    }
+
+    /** Simple test */
+    public void testCreateRemoveAllRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        long requesterId = 2;
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result =
+            RemoteCacheRequestFactory.createRemoveAllRequest( cacheName, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.REMOVE_ALL, result.getRequestType() );
+    }
+
+    /** Simple test */
+    public void testCreateUpdateRequest_Normal()
+    {
+        // SETUP
+        String cacheName = "test";
+        Serializable key = "key";
+        long requesterId = 2;
+
+        CacheElement<Serializable, Serializable> element =
+            new CacheElement<Serializable, Serializable>( cacheName, key, null );
+
+        // DO WORK
+        RemoteCacheRequest<Serializable, Serializable> result =
+            RemoteCacheRequestFactory.createUpdateRequest( element, requesterId );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertEquals( "Wrong cacheName", cacheName, result.getCacheName() );
+        assertEquals( "Wrong type", RemoteRequestType.UPDATE, result.getRequestType() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/CacheEventQueueFactoryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/CacheEventQueueFactoryUnitTest.java
new file mode 100644
index 0000000..3925b63
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/CacheEventQueueFactoryUnitTest.java
@@ -0,0 +1,67 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.remote.MockRemoteCacheListener;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
+import org.apache.commons.jcs.engine.behavior.ICacheEventQueue.QueueType;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+
+/** Unit tests for the CacheEventQueueFactory */
+public class CacheEventQueueFactoryUnitTest
+    extends TestCase
+{
+    /** Test create */
+    public void testCreateCacheEventQueue_Single()
+    {
+        // SETUP
+        QueueType eventQueueType = QueueType.SINGLE;
+        ICacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+        long listenerId = 1;
+
+        CacheEventQueueFactory<String, String> factory = new CacheEventQueueFactory<String, String>();
+
+        // DO WORK
+        ICacheEventQueue<String, String> result = factory.createCacheEventQueue( listener, listenerId, "cacheName", "threadPoolName", eventQueueType );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertTrue( "Wrong type", result instanceof CacheEventQueue );
+    }
+
+    /** Test create */
+    public void testCreateCacheEventQueue_Pooled()
+    {
+        // SETUP
+        QueueType eventQueueType = QueueType.POOLED;
+        ICacheListener<String, String> listener = new MockRemoteCacheListener<String, String>();
+        long listenerId = 1;
+
+        CacheEventQueueFactory<String, String> factory = new CacheEventQueueFactory<String, String>();
+
+        // DO WORK
+        ICacheEventQueue<String, String> result = factory.createCacheEventQueue( listener, listenerId, "cacheName", "threadPoolName", eventQueueType );
+
+        // VERIFY
+        assertNotNull( "Should have a result", result );
+        assertTrue( "Wrong type", result instanceof PooledCacheEventQueue );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ElementAttributesUtils.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ElementAttributesUtils.java
new file mode 100644
index 0000000..5da0af0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ElementAttributesUtils.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.engine;
+
+/**
+ * Allow test access to set last access time without exposing public method
+ */
+public class ElementAttributesUtils {
+    public static void setLastAccessTime(ElementAttributes ea, long time) {
+        ea.setLastAccessTime(time);
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/EventQueueConcurrentLoadTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/EventQueueConcurrentLoadTest.java
new file mode 100644
index 0000000..039797b
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/EventQueueConcurrentLoadTest.java
@@ -0,0 +1,388 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheListener;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * This test case is designed to makes sure there are no deadlocks in the event queue. The time to
+ * live should be set to a very short interval to make a deadlock more likely.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class EventQueueConcurrentLoadTest
+    extends TestCase
+{
+    /** The queue implementation */
+    private static CacheEventQueue<String, String> queue = null;
+
+    /** The mock listener */
+    private static CacheListenerImpl<String, String> listen = null;
+
+    /** max failure setting */
+    private final int maxFailure = 3;
+
+    /** time to wait before retrying on failure. */
+    private final int waitBeforeRetry = 100;
+
+    /** very small idle time */
+    private final int idleTime = 2;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     * @param testName
+     */
+    public EventQueueConcurrentLoadTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { EventQueueConcurrentLoadTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runPutTest( 200, 200 );
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runPutTest( 1200, 1400 );
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runRemoveTest( 2200 );
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testStopProcessing1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runStopProcessingTest();
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest4" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runPutTest( 5200, 6600 );
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runRemoveTest( 5200 );
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testStopProcessing2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runStopProcessingTest();
+            }
+        } );
+
+        suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutDelayTest" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runPutDelayTest( 100, 6700 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup. Create the static queue to be used by all tests
+     */
+    @Override
+    public void setUp()
+    {
+        listen = new CacheListenerImpl<String, String>();
+        queue = new CacheEventQueue<String, String>( listen, 1L, "testCache1", maxFailure, waitBeforeRetry );
+
+        queue.setWaitToDieMillis( idleTime );
+    }
+
+    /**
+     * Adds put events to the queue.
+     * @param end
+     * @param expectedPutCount
+     * @throws Exception
+     */
+    public void runPutTest( int end, int expectedPutCount )
+        throws Exception
+    {
+        for ( int i = 0; i <= end; i++ )
+        {
+            CacheElement<String, String> elem = new CacheElement<String, String>( "testCache1", i + ":key", i + "data" );
+            queue.addPutEvent( elem );
+        }
+
+        while ( !queue.isEmpty() )
+        {
+            synchronized ( this )
+            {
+                System.out.println( "queue is still busy, waiting 250 millis" );
+                this.wait( 250 );
+            }
+        }
+        System.out.println( "queue is empty, comparing putCount" );
+
+        // this becomes less accurate with each test. It should never fail. If
+        // it does things are very off.
+        assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
+            + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
+
+    }
+
+    /**
+     * Add remove events to the event queue.
+     * @param end
+     * @throws Exception
+     */
+    public void runRemoveTest( int end )
+        throws Exception
+    {
+        for ( int i = 0; i <= end; i++ )
+        {
+            queue.addRemoveEvent( i + ":key" );
+        }
+
+    }
+
+    /**
+     * Add remove events to the event queue.
+     * @throws Exception
+     */
+    public void runStopProcessingTest()
+        throws Exception
+    {
+        queue.stopProcessing();
+    }
+
+    /**
+     * Test putting and a delay. Waits until queue is empty to start.
+     * @param end
+     * @param expectedPutCount
+     * @throws Exception
+     */
+    public void runPutDelayTest( int end, int expectedPutCount )
+        throws Exception
+    {
+        while ( !queue.isEmpty() )
+        {
+            synchronized ( this )
+            {
+                System.out.println( "queue is busy, waiting 250 millis to begin" );
+                this.wait( 250 );
+            }
+        }
+        System.out.println( "queue is empty, begin" );
+
+        // get it going
+        CacheElement<String, String> elem = new CacheElement<String, String>( "testCache1", "a:key", "adata" );
+        queue.addPutEvent( elem );
+
+        for ( int i = 0; i <= end; i++ )
+        {
+            synchronized ( this )
+            {
+                if ( i % 2 == 0 )
+                {
+                    this.wait( idleTime );
+                }
+                else
+                {
+                    this.wait( idleTime / 2 );
+                }
+            }
+            CacheElement<String, String> elem2 = new CacheElement<String, String>( "testCache1", i + ":key", i + "data" );
+            queue.addPutEvent( elem2 );
+        }
+
+        while ( !queue.isEmpty() )
+        {
+            synchronized ( this )
+            {
+                System.out.println( "queue is still busy, waiting 250 millis" );
+                this.wait( 250 );
+            }
+        }
+        System.out.println( "queue is empty, comparing putCount" );
+
+        Thread.sleep( 1000 );
+
+        // this becomes less accurate with each test. It should never fail. If
+        // it does things are very off.
+        assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
+            + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
+
+    }
+
+    /**
+     * This is a dummy cache listener to use when testing the event queue.
+     */
+    protected static class CacheListenerImpl<K extends Serializable, V extends Serializable>
+        implements ICacheListener<K, V>
+    {
+        /**
+         * <code>putCount</code>
+         */
+        protected int putCount = 0;
+
+        /**
+         * <code>removeCount</code>
+         */
+        protected int removeCount = 0;
+
+        /**
+         * @param item
+         * @throws IOException
+         */
+        @Override
+        public void handlePut( ICacheElement<K, V> item )
+            throws IOException
+        {
+            synchronized ( this )
+            {
+                putCount++;
+            }
+        }
+
+        /**
+         * @param cacheName
+         * @param key
+         * @throws IOException
+         */
+        @Override
+        public void handleRemove( String cacheName, K key )
+            throws IOException
+        {
+            synchronized ( this )
+            {
+                removeCount++;
+            }
+
+        }
+
+        /**
+         * @param cacheName
+         * @throws IOException
+         */
+        @Override
+        public void handleRemoveAll( String cacheName )
+            throws IOException
+        {
+            // TODO Auto-generated method stub
+
+        }
+
+        /**
+         * @param cacheName
+         * @throws IOException
+         */
+        @Override
+        public void handleDispose( String cacheName )
+            throws IOException
+        {
+            // TODO Auto-generated method stub
+
+        }
+
+        /**
+         * @param id
+         * @throws IOException
+         */
+        @Override
+        public void setListenerId( long id )
+            throws IOException
+        {
+            // TODO Auto-generated method stub
+
+        }
+
+        /**
+         * @return 0
+         * @throws IOException
+         */
+        @Override
+        public long getListenerId()
+            throws IOException
+        {
+            // TODO Auto-generated method stub
+            return 0;
+        }
+
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheEventQueue.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheEventQueue.java
new file mode 100644
index 0000000..b64f693
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheEventQueue.java
@@ -0,0 +1,35 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.auxiliary.remote.MockRemoteCacheListener;
+
+import java.io.Serializable;
+
+/** For testing the factory */
+public class MockCacheEventQueue<K extends Serializable, V extends Serializable>
+    extends CacheEventQueue<K, V>
+{
+    /** junk */
+    public MockCacheEventQueue()
+    {
+        super( new MockRemoteCacheListener<K, V>(), 1, null, 1, 1 );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheServiceNonLocal.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheServiceNonLocal.java
new file mode 100644
index 0000000..47407ff
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/MockCacheServiceNonLocal.java
@@ -0,0 +1,246 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a mock impl of the non local cache service.
+ */
+public class MockCacheServiceNonLocal<K extends Serializable, V extends Serializable>
+    implements ICacheServiceNonLocal<K, V>
+{
+    /** The key last passed to get */
+    public Serializable lastGetKey;
+
+    /** The pattern last passed to get */
+    public String lastGetMatchingPattern;
+
+    /** The keya last passed to getMatching */
+    public Set<K> lastGetMultipleKeys;
+
+    /** The object that was last passed to update. */
+    public ICacheElement<K, V> lastUpdate;
+
+    /** List of updates. */
+    public List<ICacheElement<K, V>> updateRequestList = new ArrayList<ICacheElement<K,V>>();
+
+    /** List of request ids. */
+    public List<Long> updateRequestIdList = new ArrayList<Long>();
+
+    /** The key that was last passed to remove. */
+    public K lastRemoveKey;
+
+    /** The cache name that was last passed to removeAll. */
+    public String lastRemoveAllCacheName;
+
+    /**
+     * @param cacheName
+     * @param key
+     * @param requesterId - identity of requester
+     * @return null
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
+    {
+        lastGetKey = key;
+        return null;
+    }
+
+    /**
+     * @param cacheName
+     * @return empty set
+     */
+    @Override
+    public Set<K> getKeySet( String cacheName )
+    {
+        return new HashSet<K>();
+    }
+
+    /**
+     * Set the last remove key.
+     * <p>
+     * @param cacheName
+     * @param key
+     * @param requesterId - identity of requester
+     */
+    @Override
+    public void remove( String cacheName, K key, long requesterId )
+    {
+        lastRemoveKey = key;
+    }
+
+    /**
+     * Set the lastRemoveAllCacheName to the cacheName.
+     * <p>
+     * @param cacheName - region name
+     * @param requesterId - identity of requester
+     * @throws IOException
+     */
+    @Override
+    public void removeAll( String cacheName, long requesterId )
+        throws IOException
+    {
+        lastRemoveAllCacheName = cacheName;
+    }
+
+    /**
+     * Set the last update item.
+     * <p>
+     * @param item
+     * @param requesterId - identity of requester
+     */
+    @Override
+    public void update( ICacheElement<K, V> item, long requesterId )
+    {
+        lastUpdate = item;
+        updateRequestList.add( item );
+        updateRequestIdList.add( Long.valueOf( requesterId ) );
+    }
+
+    /**
+     * Do nothing.
+     * <p>
+     * @param cacheName
+     */
+    @Override
+    public void dispose( String cacheName )
+    {
+        return;
+    }
+
+    /**
+     * @param cacheName
+     * @param key
+     * @return null
+     */
+    @Override
+    public ICacheElement<K, V> get( String cacheName, K key )
+    {
+        return get( cacheName, key, 0 );
+    }
+
+    /**
+     * Do nothing.
+     */
+    @Override
+    public void release()
+    {
+        return;
+    }
+
+    /**
+     * Set the last remove key.
+     * <p>
+     * @param cacheName
+     * @param key
+     */
+    @Override
+    public void remove( String cacheName, K key )
+    {
+        lastRemoveKey = key;
+    }
+
+    /**
+     * Set the last remove all cache name.
+     * <p>
+     * @param cacheName
+     */
+    @Override
+    public void removeAll( String cacheName )
+    {
+        lastRemoveAllCacheName = cacheName;
+    }
+
+    /**
+     * Set the last update item.
+     * <p>
+     * @param item
+     */
+    @Override
+    public void update( ICacheElement<K, V> item )
+    {
+        lastUpdate = item;
+    }
+
+    /**
+     * @param cacheName
+     * @param keys
+     * @param requesterId - identity of requester
+     * @return empty map
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
+    {
+        lastGetMultipleKeys = keys;
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+
+    /**
+     * @param cacheName
+     * @param keys
+     * @return empty map
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
+    {
+        return getMultiple( cacheName, keys, 0 );
+    }
+
+    /**
+     * Returns an empty map. Zombies have no internal data.
+     * <p>
+     * @param cacheName
+     * @param pattern
+     * @return an empty map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
+        throws IOException
+    {
+        return getMatching( cacheName, pattern, 0 );
+    }
+
+    /**
+     * @param cacheName
+     * @param pattern
+     * @param requesterId
+     * @return Map
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
+        throws IOException
+    {
+        lastGetMatchingPattern = pattern;
+        return new HashMap<K, ICacheElement<K, V>>();
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/SystemPropertyUsageUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/SystemPropertyUsageUnitTest.java
new file mode 100644
index 0000000..8c2de03
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/SystemPropertyUsageUnitTest.java
@@ -0,0 +1,98 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.utils.props.PropertyLoader;
+
+import java.util.Properties;
+
+/**
+ * Verify that system properties can override.
+ */
+public class SystemPropertyUsageUnitTest
+    extends TestCase
+{
+    private static final String JCS_DEFAULT_CACHEATTRIBUTES_MAX_OBJECTS = "jcs.default.cacheattributes.MaxObjects";
+    private static final int testValue = 6789;
+
+    private CompositeCacheManager manager = null;
+
+	/**
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	@Override
+	protected void tearDown() throws Exception
+	{
+		if (manager != null)
+		{
+			manager.shutDown();
+		}
+
+        System.clearProperty(JCS_DEFAULT_CACHEATTRIBUTES_MAX_OBJECTS);
+		super.tearDown();
+	}
+
+	/**
+     * Verify that the system properties are used.
+     * @throws Exception
+     *
+     */
+    public void testSystemPropertyUsage()
+        throws Exception
+    {
+        System.setProperty( JCS_DEFAULT_CACHEATTRIBUTES_MAX_OBJECTS, String.valueOf(testValue) );
+
+        JCS.setConfigFilename( "/TestSystemPropertyUsage.ccf" );
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "someCacheNotInFile" );
+
+        manager = CompositeCacheManager.getInstance();
+
+        assertEquals( "System property value is not reflected", testValue, jcs.getCacheAttributes().getMaxObjects());
+    }
+
+    /**
+     * Verify that the system properties are not used is specified.
+     *
+     * @throws Exception
+     *
+     */
+    public void testSystemPropertyUsage_inactive()
+        throws Exception
+    {
+        System.setProperty( JCS_DEFAULT_CACHEATTRIBUTES_MAX_OBJECTS, String.valueOf(testValue) );
+
+        manager = CompositeCacheManager.getUnconfiguredInstance();
+
+        Properties props = PropertyLoader.loadProperties( "TestSystemPropertyUsage.ccf" );
+
+        manager.configure( props, false );
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "someCacheNotInFile" );
+
+        assertEquals( "System property value should not be reflected",
+                      Integer.parseInt( props.getProperty( JCS_DEFAULT_CACHEATTRIBUTES_MAX_OBJECTS ) ),
+                      jcs.getCacheAttributes().getMaxObjects());
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocalUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocalUnitTest.java
new file mode 100644
index 0000000..81aae25
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/ZombieCacheServiceNonLocalUnitTest.java
@@ -0,0 +1,125 @@
+package org.apache.commons.jcs.engine;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+
+/**
+ * Tests for the zombie remote cache service.
+ */
+public class ZombieCacheServiceNonLocalUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that an update event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdateThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockCacheServiceNonLocal<String, String> service = new MockCacheServiceNonLocal<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        zombie.update( element, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", element, service.lastUpdate );
+    }
+
+    /**
+     * Verify that nothing is added if the max is set to 0.
+     * <p>
+     * @throws Exception
+     */
+    public void testUpdateThenWalk_zeroSize()
+        throws Exception
+    {
+        // SETUP
+        MockCacheServiceNonLocal<String, String> service = new MockCacheServiceNonLocal<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 0 );
+
+        String cacheName = "testUpdate";
+
+        // DO WORK
+        ICacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key", "value" );
+        zombie.update( element, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertNull( "Nothing should have been put to the service.", service.lastUpdate );
+    }
+
+    /**
+     * Verify that a remove event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockCacheServiceNonLocal<String, String> service = new MockCacheServiceNonLocal<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testRemoveThenWalk";
+        String key = "myKey";
+
+        // DO WORK
+        zombie.remove( cacheName, key, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", key, service.lastRemoveKey );
+    }
+
+    /**
+     * Verify that a removeAll event gets added and then is sent to the service passed to propagate.
+     * <p>
+     * @throws Exception
+     */
+    public void testRemoveAllThenWalk()
+        throws Exception
+    {
+        // SETUP
+        MockCacheServiceNonLocal<String, String> service = new MockCacheServiceNonLocal<String, String>();
+
+        ZombieCacheServiceNonLocal<String, String> zombie = new ZombieCacheServiceNonLocal<String, String>( 10 );
+
+        String cacheName = "testRemoveThenWalk";
+
+        // DO WORK
+        zombie.removeAll( cacheName, 123l );
+        zombie.propagateEvents( service );
+
+        // VERIFY
+        assertEquals( "Updated element is not as expected.", cacheName, service.lastRemoveAllCacheName );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CacheManagerStatsUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CacheManagerStatsUnitTest.java
new file mode 100644
index 0000000..9ae693e
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CacheManagerStatsUnitTest.java
@@ -0,0 +1,71 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+
+/**
+ * @author Aaron Smuts
+ *
+ */
+public class CacheManagerStatsUnitTest
+    extends TestCase
+{
+
+    /**
+     * Just get the stats after putting a couple entries in the cache.
+     *
+     * @throws Exception
+     */
+    public void testSimpleGetStats() throws Exception
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testCache1" );
+
+        // 1 miss, 1 hit, 1 put
+        cache.get( "testKey" );
+        cache.put( "testKey", "testdata" );
+        // should have 4 hits
+        cache.get( "testKey" );
+        cache.get( "testKey" );
+        cache.get( "testKey" );
+        cache.get( "testKey" );
+
+        CompositeCacheManager mgr = CompositeCacheManager.getInstance();
+        String statsString = mgr.getStats();
+
+//        System.out.println( statsString );
+
+        assertTrue( "Should have the cacheName in here.", statsString.indexOf("testCache1") != -1 );
+        assertTrue( "Should have the HitCountRam in here.", statsString.indexOf("HitCountRam") != -1 );
+        assertTrue( "Should have the 4 in here.", statsString.indexOf("4") != -1 );
+
+        ICacheStats[] stats = mgr.getStatistics();
+        int statsLen = stats.length;
+//        System.out.println( "statsLen = " + statsLen );
+        for ( int i = 0; i < statsLen; i++ )
+        {
+            // TODO finish
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheConfiguratorUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheConfiguratorUnitTest.java
new file mode 100644
index 0000000..669a242
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheConfiguratorUnitTest.java
@@ -0,0 +1,92 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheConfigurator;
+import org.apache.commons.jcs.auxiliary.MockAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.MockAuxiliaryCacheAttributes;
+import org.apache.commons.jcs.auxiliary.MockAuxiliaryCacheFactory;
+import org.apache.commons.jcs.engine.logging.MockCacheEventLogger;
+
+import java.util.Properties;
+
+/** Unit tests for the configurator. */
+public class CompositeCacheConfiguratorUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we can parse the event logger correctly
+     */
+    public void testParseAuxiliary_CacheEventLogger_Normal()
+    {
+        // SETUP
+        String regionName = "MyRegion";
+
+        String auxName = "MockAux";
+        String auxPrefix = "jcs.auxiliary." + auxName;
+        String auxiliaryClassName = MockAuxiliaryCacheFactory.class.getName();
+        String eventLoggerClassName = MockCacheEventLogger.class.getName();
+        String auxiliaryAttributeClassName = MockAuxiliaryCacheAttributes.class.getName();
+
+        Properties props = new Properties();
+        props.put( auxPrefix, auxiliaryClassName );
+        props.put( auxPrefix + CompositeCacheConfigurator.ATTRIBUTE_PREFIX, auxiliaryAttributeClassName );
+        props.put( auxPrefix + AuxiliaryCacheConfigurator.CACHE_EVENT_LOGGER_PREFIX, eventLoggerClassName );
+
+//        System.out.print( props );
+
+        CompositeCacheManager manager = CompositeCacheManager.getUnconfiguredInstance();
+
+        CompositeCacheConfigurator configurator = new CompositeCacheConfigurator( manager );
+
+        // DO WORK
+        AuxiliaryCache<String, String> aux = configurator.parseAuxiliary( props, auxName, regionName );
+        MockAuxiliaryCache<String, String> result = (MockAuxiliaryCache<String, String>)aux;
+
+        // VERIFY
+        assertNotNull( "Should have an auxcache.", result );
+        assertNotNull( "Should have an event logger.", result.getCacheEventLogger() );
+    }
+
+    /**
+     * Verify that we can parse the spool chunk size
+     */
+    public void testParseSpoolChunkSize_Normal()
+    {
+        // SETUP
+        String regionName = "MyRegion";
+        int chunkSize = 5;
+
+        Properties props = new Properties();
+        props.put( "jcs.default", "" );
+        props.put( "jcs.default.cacheattributes.SpoolChunkSize", String.valueOf( chunkSize ) );
+
+        CompositeCacheManager manager = CompositeCacheManager.getUnconfiguredInstance();
+
+        // DO WORK
+        manager.configure( props );
+
+        // VERIFY
+        CompositeCache<String, String> cache = manager.getCache( regionName );
+        assertEquals( "Wrong chunkSize", cache.getCacheAttributes().getSpoolChunkSize(), chunkSize );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheDiskUsageUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheDiskUsageUnitTest.java
new file mode 100644
index 0000000..49b5ec1
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheDiskUsageUnitTest.java
@@ -0,0 +1,513 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CacheStatus;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheType.CacheType;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests of the disk usage settings for the CompositeCache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class CompositeCacheDiskUsageUnitTest
+    extends TestCase
+{
+    private static final String CACHE_NAME = "testSpoolAllowed";
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestDiskCacheUsagePattern.ccf" );
+    }
+
+    /**
+     * Verify that the swap region is set to the correct pattern.
+     * <p>
+     * @throws CacheException
+     */
+    public void testSwapConfig()
+        throws CacheException
+    {
+        CacheAccess<String, String> swap = JCS.getInstance( "Swap" );
+        assertEquals( ICompositeCacheAttributes.DiskUsagePattern.SWAP, swap.getCacheAttributes()
+            .getDiskUsagePattern() );
+    }
+
+    /**
+     * Verify that the swap region is set to the correct pattern.
+     * <p>
+     * @throws CacheException
+     */
+    public void testUpdateConfig()
+        throws CacheException
+    {
+        CacheAccess<String, String> swap = JCS.getInstance( "Update" );
+        assertEquals( ICompositeCacheAttributes.DiskUsagePattern.UPDATE, swap.getCacheAttributes()
+            .getDiskUsagePattern() );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to swap. Call spool. Verify that the
+     * item is put to disk.
+     */
+    public void testSpoolAllowed()
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.SWAP );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.spoolToDisk( inputElement );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 1, mock.updateCount );
+        assertEquals( "Wrong element updated.", inputElement, mock.lastUpdatedItem );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to not swap. Call spool. Verify that the
+     * item is not put to disk.
+     */
+    public void testSpoolNotAllowed()
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.UPDATE );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.spoolToDisk( inputElement );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 0, mock.updateCount );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to UPDATE. Call updateAuxiliaries.
+     * Verify that the item is put to disk.
+     * <p>
+     * This tests that the items are put to disk on a normal put when the usage pattern is set
+     * appropriately.
+     * @throws IOException
+     */
+    public void testUpdateAllowed()
+        throws IOException
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.UPDATE );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.updateAuxiliaries( inputElement, true );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 1, mock.updateCount );
+        assertEquals( "Wrong element updated.", inputElement, mock.lastUpdatedItem );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to UPDATE. Call updateAuxiliaries with
+     * local only set to false. Verify that the item is put to disk.
+     * <p>
+     * This tests that the items are put to disk on a normal put when the usage pattern is set
+     * appropriately. The local setting should have no impact on whether the item goes to disk.
+     * <p>
+     * @throws IOException
+     */
+    public void testUpdateAllowed_localFalse()
+        throws IOException
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.UPDATE );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.updateAuxiliaries( inputElement, false );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 1, mock.updateCount );
+        assertEquals( "Wrong element updated.", inputElement, mock.lastUpdatedItem );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to SWAP. Call updateAuxiliaries. Verify
+     * that the item is not put to disk.
+     * <p>
+     * This tests that the items are not put to disk on a normal put when the usage pattern is set
+     * to SWAP.
+     * <p>
+     * @throws IOException
+     */
+    public void testUpdateNotAllowed()
+        throws IOException
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.SWAP );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.updateAuxiliaries( inputElement, true );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 0, mock.updateCount );
+    }
+
+    /**
+     * Setup a disk cache. Configure the disk usage pattern to UPDATE. Call updateAuxiliaries.
+     * Verify that the item is put to disk.
+     * <p>
+     * This tests that the items are put to disk on a normal put when the usage pattern is set
+     * appropriately.
+     * @throws IOException
+     */
+    public void testUpdateAllowed_withOtherCaches()
+        throws IOException
+    {
+        // SETUP
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(CACHE_NAME);
+        cattr.setDiskUsagePattern( ICompositeCacheAttributes.DiskUsagePattern.UPDATE );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>( cattr, attr );
+
+        MockAuxCache<String, String> mock = new MockAuxCache<String, String>();
+        mock.cacheType = CacheType.DISK_CACHE;
+
+        MockAuxCache<String, String> mockLateral = new MockAuxCache<String, String>();
+        mockLateral.cacheType = CacheType.LATERAL_CACHE;
+
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, String>[] auxArray = new AuxiliaryCache[] { mock, mockLateral };
+        cache.setAuxCaches( auxArray );
+
+        ICacheElement<String, String> inputElement = new CacheElement<String, String>( CACHE_NAME, "key", "value" );
+
+        // DO WORK
+        cache.updateAuxiliaries( inputElement, false );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls to the disk cache update.", 1, mock.updateCount );
+        assertEquals( "Wrong element updated.", inputElement, mock.lastUpdatedItem );
+
+        assertEquals( "Wrong number of calls to the lateral cache update.", 1, mockLateral.updateCount );
+        assertEquals( "Wrong element updated with lateral.", inputElement, mockLateral.lastUpdatedItem );
+    }
+
+    /**
+     * Used to test the disk cache functionality.
+     * <p>
+     * @author Aaron Smuts
+     */
+    public static class MockAuxCache<K extends Serializable, V extends Serializable>
+        extends AbstractAuxiliaryCache<K, V>
+    {
+        /** Don't change */
+        private static final long serialVersionUID = 1L;
+
+        /** The last item passed to update. */
+        public ICacheElement<K, V> lastUpdatedItem;
+
+        /** The number of times update was called. */
+        public int updateCount = 0;
+
+        /** The type that should be returned from getCacheType. */
+        public CacheType cacheType = CacheType.DISK_CACHE;
+
+        /** Resets counters and catchers. */
+        public void reset()
+        {
+            updateCount = 0;
+            lastUpdatedItem = null;
+        }
+
+        /**
+         * @param ce
+         * @throws IOException
+         */
+        @Override
+        public void update( ICacheElement<K, V> ce )
+            throws IOException
+        {
+            lastUpdatedItem = ce;
+            updateCount++;
+        }
+
+        /**
+         * @param key
+         * @return ICacheElement
+         * @throws IOException
+         */
+        @Override
+        public ICacheElement<K, V> get( K key )
+            throws IOException
+        {
+            return null;
+        }
+
+        /**
+         * Gets multiple items from the cache based on the given set of keys.
+         * <p>
+         * @param keys
+         * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is
+         *         no data in cache for any of these keys
+         */
+        @Override
+        public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+        {
+            return new HashMap<K, ICacheElement<K, V>>();
+        }
+
+        /**
+         * @param key
+         * @return false
+         * @throws IOException
+         */
+        @Override
+        public boolean remove( K key )
+            throws IOException
+        {
+            return false;
+        }
+
+        /** @throws IOException */
+        @Override
+        public void removeAll()
+            throws IOException
+        {
+            // noop
+        }
+
+        /** @throws IOException */
+        @Override
+        public void dispose()
+            throws IOException
+        {
+            // noop
+        }
+
+        /** @return 0 */
+        @Override
+        public int getSize()
+        {
+            return 0;
+        }
+
+        /** @return 0 */
+        @Override
+        public CacheStatus getStatus()
+        {
+            return CacheStatus.ALIVE;
+        }
+
+        /** @return null */
+        @Override
+        public String getCacheName()
+        {
+            return null;
+        }
+
+        /**
+         * @return null
+         * @throws IOException
+         */
+        @Override
+        public Set<K> getKeySet( )
+            throws IOException
+        {
+            return null;
+        }
+
+        /** @return null */
+        @Override
+        public IStats getStatistics()
+        {
+            return null;
+        }
+
+        /** @return null */
+        @Override
+        public String getStats()
+        {
+            return null;
+        }
+
+        /**
+         * Returns the setup cache type. This allows you to use this mock as multiple cache types.
+         * <p>
+         * @see org.apache.commons.jcs.engine.behavior.ICacheType#getCacheType()
+         * @return cacheType
+         */
+        @Override
+        public CacheType getCacheType()
+        {
+            return cacheType;
+        }
+
+        /**
+         * @return Returns the AuxiliaryCacheAttributes.
+         */
+        @Override
+        public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
+        {
+            return null;
+        }
+
+        /**
+         * @param cacheEventLogger
+         */
+        @Override
+        public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
+        {
+            // TODO Auto-generated method stub
+
+        }
+
+        /**
+         * @param elementSerializer
+         */
+        @Override
+        public void setElementSerializer( IElementSerializer elementSerializer )
+        {
+            // TODO Auto-generated method stub
+
+        }
+
+        /** @return null */
+        @Override
+        public String getEventLoggingExtraInfo()
+        {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        /**
+         * @param pattern
+         * @return Collections.EMPTY_MAP;
+         * @throws IOException
+         */
+        @Override
+        public Map<K, ICacheElement<K, V>> getMatching(String pattern)
+            throws IOException
+        {
+            return Collections.emptyMap();
+        }
+
+
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheUnitTest.java
new file mode 100644
index 0000000..7f012c6
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/CompositeCacheUnitTest.java
@@ -0,0 +1,245 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
+import org.apache.commons.jcs.auxiliary.MockAuxiliaryCache;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheType.CacheType;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.memory.MockMemoryCache;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Tests that directly engage the composite cache.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class CompositeCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the freeMemoryElements method on the memory cache is called on shutdown if there
+     * is a disk cache.
+     * <p>
+     * @throws IOException
+     */
+    public void testShutdownMemoryFlush()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testCacheName";
+        String mockMemoryCacheClassName = "org.apache.commons.jcs.engine.memory.MockMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMemoryCacheName( mockMemoryCacheClassName );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, Integer> cache = new CompositeCache<String, Integer>( cattr, attr );
+
+        MockAuxiliaryCache<String, Integer> diskMock = new MockAuxiliaryCache<String, Integer>();
+        diskMock.cacheType = CacheType.DISK_CACHE;
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, Integer>[] aux = new AuxiliaryCache[] { diskMock };
+        cache.setAuxCaches( aux );
+
+        // DO WORK
+        int numToInsert = 10;
+        for ( int i = 0; i < numToInsert; i++ )
+        {
+            ICacheElement<String, Integer> element = new CacheElement<String, Integer>( cacheName, String.valueOf( i ), Integer.valueOf( i ) );
+            cache.update( element, false );
+        }
+
+        cache.dispose();
+
+        // VERIFY
+        MockMemoryCache<String, Integer> memoryCache = (MockMemoryCache<String, Integer>) cache.getMemoryCache();
+        assertEquals( "Wrong number freed.", numToInsert, memoryCache.lastNumberOfFreedElements );
+    }
+
+    /**
+     * Verify that the freeMemoryElements method on the memory cache is NOT called on shutdown if
+     * there is NOT a disk cache.
+     * <p>
+     * @throws IOException
+     */
+    public void testShutdownMemoryFlush_noDisk()
+        throws IOException
+    {
+        // SETUP
+        String cacheName = "testCacheName";
+        String mockMemoryCacheClassName = "org.apache.commons.jcs.engine.memory.MockMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMemoryCacheName( mockMemoryCacheClassName );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, Integer> cache = new CompositeCache<String, Integer>( cattr, attr );
+
+        MockAuxiliaryCache<String, Integer> diskMock = new MockAuxiliaryCache<String, Integer>();
+        diskMock.cacheType = CacheType.REMOTE_CACHE;
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, Integer>[] aux = new AuxiliaryCache[] { diskMock };
+        cache.setAuxCaches( aux );
+
+        // DO WORK
+        int numToInsert = 10;
+        for ( int i = 0; i < numToInsert; i++ )
+        {
+            ICacheElement<String, Integer> element = new CacheElement<String, Integer>( cacheName, String.valueOf( i ), Integer.valueOf( i ) );
+            cache.update( element, false );
+        }
+
+        cache.dispose();
+
+        // VERIFY
+        MockMemoryCache<String, Integer> memoryCache = (MockMemoryCache<String, Integer>) cache.getMemoryCache();
+        assertEquals( "Wrong number freed.", 0, memoryCache.lastNumberOfFreedElements );
+    }
+
+    /**
+     * Verify we can get some matching elements..
+     * <p>
+     * @throws IOException
+     */
+    public void testGetMatching_Normal()
+        throws IOException
+    {
+        // SETUP
+        int maxMemorySize = 1000;
+        String keyprefix1 = "MyPrefix1";
+        String keyprefix2 = "MyPrefix2";
+        String cacheName = "testGetMatching_Normal";
+        String memoryCacheClassName = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMemoryCacheName( memoryCacheClassName );
+        cattr.setMaxObjects( maxMemorySize );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, Integer> cache = new CompositeCache<String, Integer>( cattr, attr );
+
+        MockAuxiliaryCache<String, Integer> diskMock = new MockAuxiliaryCache<String, Integer>();
+        diskMock.cacheType = CacheType.DISK_CACHE;
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, Integer>[] aux = new AuxiliaryCache[] { diskMock };
+        cache.setAuxCaches( aux );
+
+        // DO WORK
+        int numToInsertPrefix1 = 10;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            ICacheElement<String, Integer> element = new CacheElement<String, Integer>( cacheName, keyprefix1 + String.valueOf( i ), Integer.valueOf( i ) );
+            cache.update( element, false );
+        }
+
+        int numToInsertPrefix2 = 50;
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix2; i++ )
+        {
+            ICacheElement<String, Integer> element = new CacheElement<String, Integer>( cacheName, keyprefix2 + String.valueOf( i ), Integer.valueOf( i ) );
+            cache.update( element, false );
+        }
+
+        Map<?, ?> result1 = cache.getMatching( keyprefix1 + "\\S+" );
+        Map<?, ?> result2 = cache.getMatching( keyprefix2 + "\\S+" );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1:", numToInsertPrefix1, result1.size() );
+        assertEquals( "Wrong number returned 2:", numToInsertPrefix2, result2.size() );
+    }
+
+    /**
+     * Verify we try a disk aux on a getMatching call.
+     * <p>
+     * @throws IOException
+     */
+    public void testGetMatching_NotOnDisk()
+        throws IOException
+    {
+        // SETUP
+        int maxMemorySize = 0;
+        String cacheName = "testGetMatching_NotOnDisk";
+        String memoryCacheClassName = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(cacheName);
+        cattr.setMemoryCacheName( memoryCacheClassName );
+        cattr.setMaxObjects( maxMemorySize );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, Integer> cache = new CompositeCache<String, Integer>( cattr, attr );
+
+        MockAuxiliaryCache<String, Integer> diskMock = new MockAuxiliaryCache<String, Integer>();
+        diskMock.cacheType = CacheType.DISK_CACHE;
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, Integer>[] aux = new AuxiliaryCache[] { diskMock };
+        cache.setAuxCaches( aux );
+
+        // DO WORK
+        cache.getMatching( "junk" );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls", 1, diskMock.getMatchingCallCount );
+    }
+
+    /**
+     * Verify we try a remote  aux on a getMatching call.
+     * <p>
+     * @throws IOException
+     */
+    public void testGetMatching_NotOnRemote()
+        throws IOException
+    {
+        // SETUP
+        int maxMemorySize = 0;
+        String cacheName = "testGetMatching_NotOnDisk";
+        String memoryCacheClassName = "org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache";
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setCacheName(cacheName);
+        cattr.setMemoryCacheName( memoryCacheClassName );
+        cattr.setMaxObjects( maxMemorySize );
+
+        IElementAttributes attr = new ElementAttributes();
+
+        CompositeCache<String, Integer> cache = new CompositeCache<String, Integer>( cattr, attr );
+
+        MockAuxiliaryCache<String, Integer> diskMock = new MockAuxiliaryCache<String, Integer>();
+        diskMock.cacheType = CacheType.REMOTE_CACHE;
+        @SuppressWarnings("unchecked")
+        AuxiliaryCache<String, Integer>[] aux = new AuxiliaryCache[] { diskMock };
+        cache.setAuxCaches( aux );
+
+        // DO WORK
+        cache.getMatching( "junk" );
+
+        // VERIFY
+        assertEquals( "Wrong number of calls", 1, diskMock.getMatchingCallCount );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockCompositeCacheManager.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockCompositeCacheManager.java
new file mode 100644
index 0000000..5dcbeb5
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockCompositeCacheManager.java
@@ -0,0 +1,119 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
+import org.apache.commons.jcs.engine.behavior.IShutdownObserver;
+
+import java.util.Properties;
+
+/** For testing. */
+public class MockCompositeCacheManager
+    implements ICompositeCacheManager
+{
+    /** The cache that was returned. */
+    private CompositeCache<?, ?> cache;
+
+    /** Properties with which this manager was configured. This is exposed for other managers. */
+    private Properties configurationProperties;
+
+    /**
+     * @param cacheName
+     * @return Returns a CompositeCache
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <K, V> CompositeCache<K, V> getCache( String cacheName )
+    {
+        if ( cache == null )
+        {
+//            System.out.println( "Creating mock cache" );
+            CompositeCache<K, V> newCache =
+                new CompositeCache<K, V>( new CompositeCacheAttributes(), new ElementAttributes() );
+            this.setCache( newCache );
+        }
+
+        return (CompositeCache<K, V>)cache;
+    }
+
+    /**
+     * @param cache The cache to set.
+     */
+    public void setCache( CompositeCache<?, ?> cache )
+    {
+        this.cache = cache;
+    }
+
+    /**
+     * @return Returns the cache.
+     */
+    public CompositeCache<?, ?> getCache()
+    {
+        return cache;
+    }
+
+    /**
+     * This is exposed so other manager can get access to the props.
+     * <p>
+     * @param props
+     */
+    public void setConfigurationProperties( Properties props )
+    {
+        this.configurationProperties = props;
+    }
+
+    /**
+     * This is exposed so other manager can get access to the props.
+     * <p>
+     * @return the configurationProperties
+     */
+    @Override
+    public Properties getConfigurationProperties()
+    {
+        return configurationProperties;
+    }
+
+    /** @return Mock */
+    @Override
+    public String getStats()
+    {
+        return "Mock";
+    }
+
+	/**
+	 * @see org.apache.commons.jcs.engine.behavior.IShutdownObservable#registerShutdownObserver(org.apache.commons.jcs.engine.behavior.IShutdownObserver)
+	 */
+	@Override
+	public void registerShutdownObserver(IShutdownObserver observer)
+	{
+		// Do nothing
+	}
+
+	/**
+	 * @see org.apache.commons.jcs.engine.behavior.IShutdownObservable#deregisterShutdownObserver(org.apache.commons.jcs.engine.behavior.IShutdownObserver)
+	 */
+	@Override
+	public void deregisterShutdownObserver(IShutdownObserver observer)
+	{
+		// Do nothing
+	}
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockElementSerializer.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockElementSerializer.java
new file mode 100644
index 0000000..49885db
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/MockElementSerializer.java
@@ -0,0 +1,87 @@
+package org.apache.commons.jcs.engine.control;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+import java.io.IOException;
+
+/** For mocking. */
+public class MockElementSerializer
+    implements IElementSerializer
+{
+    /** test property */
+    private String testProperty;
+
+    /** What's used in the background */
+    private final StandardSerializer serializer = new StandardSerializer();
+
+    /** times out was called */
+    public int deSerializeCount = 0;
+
+    /** times in was called */
+    public int serializeCount = 0;
+
+    /**
+     * @param bytes
+     * @return Object
+     * @throws IOException
+     * @throws ClassNotFoundException
+     *
+     */
+    @Override
+    public <T> T deSerialize( byte[] bytes, ClassLoader loader )
+        throws IOException, ClassNotFoundException
+    {
+        deSerializeCount++;
+        return serializer.deSerialize( bytes, loader );
+    }
+
+    /**
+     * @param obj
+     * @return byte[]
+     * @throws IOException
+     *
+     */
+    @Override
+    public <T> byte[] serialize( T obj )
+        throws IOException
+    {
+        serializeCount++;
+        return serializer.serialize( obj );
+    }
+
+    /**
+     * @param testProperty
+     */
+    public void setTestProperty( String testProperty )
+    {
+        this.testProperty = testProperty;
+    }
+
+    /**
+     * @return testProperty
+     */
+    public String getTestProperty()
+    {
+        return testProperty;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/ElementEventHandlerMockImpl.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/ElementEventHandlerMockImpl.java
new file mode 100644
index 0000000..c3b09a2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/ElementEventHandlerMockImpl.java
@@ -0,0 +1,186 @@
+package org.apache.commons.jcs.engine.control.event;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.control.event.behavior.ElementEventType;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @author aaronsm
+ */
+public class ElementEventHandlerMockImpl
+    implements IElementEventHandler
+{
+    /** Times called. */
+    private int callCount = 0;
+
+    /** The logger */
+    private static final Log log = LogFactory.getLog( ElementEventHandlerMockImpl.class );
+
+    /** ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE */
+    private int spoolCount = 0;
+
+    /** ELEMENT_EVENT_SPOOLED_NOT_ALLOWED */
+    private int spoolNotAllowedCount = 0;
+
+    /** ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE */
+    private int spoolNoDiskCount = 0;
+
+    /** ELEMENT_EVENT_EXCEEDED_MAXLIFE_BACKGROUND */
+    private int exceededMaxLifeBackgroundCount = 0;
+
+    /** ELEMENT_EVENT_EXCEEDED_IDLETIME_BACKGROUND */
+    private int exceededIdleTimeBackgroundCount = 0;
+
+    /**
+     * @param event
+     */
+    @Override
+    public synchronized void handleElementEvent( IElementEvent event )
+    {
+        setCallCount( getCallCount() + 1 );
+
+        if ( log.isDebugEnabled() )
+        {
+            log.debug( "HANDLER -- HANDLER -- HANDLER -- ---EVENT CODE = " + event.getElementEvent() );
+            log.debug( "/n/n EVENT CODE = " + event.getElementEvent() + " ***************************" );
+        }
+
+        if ( event.getElementEvent() == ElementEventType.SPOOLED_DISK_AVAILABLE )
+        {
+            setSpoolCount( getSpoolCount() + 1 );
+        }
+        else if ( event.getElementEvent() == ElementEventType.SPOOLED_NOT_ALLOWED )
+        {
+            setSpoolNotAllowedCount( getSpoolNotAllowedCount() + 1 );
+        }
+        else if ( event.getElementEvent() == ElementEventType.SPOOLED_DISK_NOT_AVAILABLE )
+        {
+            setSpoolNoDiskCount( getSpoolNoDiskCount() + 1 );
+        }
+        else if ( event.getElementEvent() == ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND )
+        {
+            setExceededMaxLifeBackgroundCount( getExceededMaxLifeBackgroundCount() + 1 );
+        }
+        else if ( event.getElementEvent() == ElementEventType.EXCEEDED_IDLETIME_BACKGROUND )
+        {
+            setExceededIdleTimeBackgroundCount( getExceededIdleTimeBackgroundCount() + 1 );
+        }
+    }
+
+    /**
+     * @param spoolCount The spoolCount to set.
+     */
+    public void setSpoolCount( int spoolCount )
+    {
+        this.spoolCount = spoolCount;
+    }
+
+    /**
+     * @return Returns the spoolCount.
+     */
+    public int getSpoolCount()
+    {
+        return spoolCount;
+    }
+
+    /**
+     * @param spoolNotAllowedCount The spoolNotAllowedCount to set.
+     */
+    public void setSpoolNotAllowedCount( int spoolNotAllowedCount )
+    {
+        this.spoolNotAllowedCount = spoolNotAllowedCount;
+    }
+
+    /**
+     * @return Returns the spoolNotAllowedCount.
+     */
+    public int getSpoolNotAllowedCount()
+    {
+        return spoolNotAllowedCount;
+    }
+
+    /**
+     * @param spoolNoDiskCount The spoolNoDiskCount to set.
+     */
+    public void setSpoolNoDiskCount( int spoolNoDiskCount )
+    {
+        this.spoolNoDiskCount = spoolNoDiskCount;
+    }
+
+    /**
+     * @return Returns the spoolNoDiskCount.
+     */
+    public int getSpoolNoDiskCount()
+    {
+        return spoolNoDiskCount;
+    }
+
+    /**
+     * @param exceededMaxLifeBackground The exceededMaxLifeBackground to set.
+     */
+    public void setExceededMaxLifeBackgroundCount( int exceededMaxLifeBackground )
+    {
+        this.exceededMaxLifeBackgroundCount = exceededMaxLifeBackground;
+    }
+
+    /**
+     * @return Returns the exceededMaxLifeBackground.
+     */
+    public int getExceededMaxLifeBackgroundCount()
+    {
+        return exceededMaxLifeBackgroundCount;
+    }
+
+    /**
+     * @param callCount The callCount to set.
+     */
+    public void setCallCount( int callCount )
+    {
+        this.callCount = callCount;
+    }
+
+    /**
+     * @return Returns the callCount.
+     */
+    public int getCallCount()
+    {
+        return callCount;
+    }
+
+    /**
+     * @param exceededIdleTimeBackground The exceededIdleTimeBackground to set.
+     */
+    public void setExceededIdleTimeBackgroundCount( int exceededIdleTimeBackground )
+    {
+        this.exceededIdleTimeBackgroundCount = exceededIdleTimeBackground;
+    }
+
+    /**
+     * @return Returns the exceededIdleTimeBackground.
+     */
+    public int getExceededIdleTimeBackgroundCount()
+    {
+        return exceededIdleTimeBackgroundCount;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/SimpleEventHandlingUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/SimpleEventHandlingUnitTest.java
new file mode 100644
index 0000000..a973745
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/control/event/SimpleEventHandlingUnitTest.java
@@ -0,0 +1,354 @@
+package org.apache.commons.jcs.engine.control.event;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+
+/**
+ * This test suite verifies that the basic ElementEvent are called as they should be.
+ */
+public class SimpleEventHandlingUnitTest
+    extends TestCase
+{
+    /** Items to test with */
+    private static int items = 20000;
+
+    /**
+     * Test setup with expected configuration parameters.
+     */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestSimpleEventHandling.ccf" );
+    }
+
+    /**
+     * Verify that the spooled event is called as expected.
+     * <p>
+     * @throws Exception Description of the Exception
+     */
+    public void testSpoolEvent()
+        throws Exception
+    {
+        // SETUP
+        MyEventHandler meh = new MyEventHandler();
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "WithDisk" );
+        // this should add the event handler to all items as they are created.
+        IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        attributes.addElementEventHandler( meh );
+        jcs.setDefaultElementAttributes( attributes );
+
+        // DO WORK
+        // put them in
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( items / 20 );
+
+        // VERIFY
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE events [" + meh.getSpoolCount()
+            + "] does not equal the number expected [" + items + "]", meh.getSpoolCount() >= items );
+    }
+
+    /**
+     * Test overflow with no disk configured for the region.
+     * <p>
+     * @throws Exception
+     */
+    public void testSpoolNoDiskEvent()
+        throws Exception
+    {
+        CacheAccess<String, String> jcs = JCS.getInstance( "NoDisk" );
+
+        MyEventHandler meh = new MyEventHandler();
+
+        // this should add the event handler to all items as they are created.
+        IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        attributes.addElementEventHandler( meh );
+        jcs.setDefaultElementAttributes( attributes );
+
+        // put them in
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( items / 20 );
+
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE events  [" + meh.getSpoolNoDiskCount()
+            + "] does not equal the number expected.", meh.getSpoolNoDiskCount() >= items );
+
+    }
+
+    /**
+     * Test the ELEMENT_EVENT_SPOOLED_NOT_ALLOWED event.
+     * @throws Exception
+     */
+    public void testSpoolNotAllowedEvent()
+        throws Exception
+    {
+        MyEventHandler meh = new MyEventHandler();
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "DiskButNotAllowed" );
+        // this should add the event handler to all items as they are created.
+        IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        attributes.addElementEventHandler( meh );
+        jcs.setDefaultElementAttributes( attributes );
+
+        // put them in
+        for ( int i = 0; i <= items; i++ )
+        {
+            jcs.put( i + ":key", "data" + i );
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( items / 20 );
+
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_SPOOLED_NOT_ALLOWED events [" + meh.getSpoolNotAllowedCount()
+            + "] does not equal the number expected.", meh.getSpoolNotAllowedCount() >= items );
+
+    }
+
+    /**
+     * Test the ELEMENT_EVENT_SPOOLED_NOT_ALLOWED event.
+     * @throws Exception
+     */
+    public void testSpoolNotAllowedEventOnItem()
+        throws Exception
+    {
+        MyEventHandler meh = new MyEventHandler();
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "DiskButNotAllowed" );
+        // this should add the event handler to all items as they are created.
+        //IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        //attributes.addElementEventHandler( meh );
+        //jcs.setDefaultElementAttributes( attributes );
+
+        // put them in
+        for ( int i = 0; i <= items; i++ )
+        {
+            IElementAttributes attributes = jcs.getDefaultElementAttributes();
+            attributes.addElementEventHandler( meh );
+            jcs.put( i + ":key", "data" + i, attributes );
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( items / 20 );
+
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_SPOOLED_NOT_ALLOWED events [" + meh.getSpoolNotAllowedCount()
+            + "] does not equal the number expected.", meh.getSpoolNotAllowedCount() >= items );
+
+    }
+
+    /**
+     * Test the ELEMENT_EVENT_EXCEEDED_MAXLIFE_ONREQUEST event.
+     * @throws Exception
+     */
+    public void testExceededMaxlifeOnrequestEvent()
+        throws Exception
+    {
+        MyEventHandler meh = new MyEventHandler();
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "Maxlife" );
+        // this should add the event handler to all items as they are created.
+        IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        attributes.addElementEventHandler( meh );
+        jcs.setDefaultElementAttributes( attributes );
+
+        // put them in
+        for ( int i = 0; i < 200; i++ )
+        {
+            jcs.put( i + ":key", "data" + i);
+        }
+
+        // wait a bit for the items to expire
+        Thread.sleep( 3000 );
+
+        for ( int i = 0; i < 200; i++ )
+        {
+            String value = jcs.get( i + ":key");
+            assertNull("Item should be null for key " + i + ":key, but is " + value, value);
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( 100 );
+
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_EXCEEDED_MAXLIFE_ONREQUEST events [" + meh.getExceededMaxlifeCount()
+            + "] does not equal the number expected.", meh.getExceededMaxlifeCount() >= 200 );
+    }
+
+    /**
+     * Test the ELEMENT_EVENT_EXCEEDED_IDLETIME_ONREQUEST event.
+     * @throws Exception
+     */
+    public void testExceededIdletimeOnrequestEvent()
+        throws Exception
+    {
+        MyEventHandler meh = new MyEventHandler();
+
+        CacheAccess<String, String> jcs = JCS.getInstance( "Idletime" );
+        // this should add the event handler to all items as they are created.
+        IElementAttributes attributes = jcs.getDefaultElementAttributes();
+        attributes.addElementEventHandler( meh );
+        jcs.setDefaultElementAttributes( attributes );
+
+        // put them in
+        for ( int i = 0; i < 200; i++ )
+        {
+            jcs.put( i + ":key", "data" + i);
+        }
+
+        // update access time
+        for ( int i = 0; i < 200; i++ )
+        {
+            String value = jcs.get( i + ":key");
+            assertNotNull("Item should not be null for key " + i + ":key", value);
+        }
+
+        // wait a bit for the items to expire
+        Thread.sleep( 1500 );
+
+        for ( int i = 0; i < 200; i++ )
+        {
+            String value = jcs.get( i + ":key");
+            assertNull("Item should be null for key " + i + ":key, but is " + value, value);
+        }
+
+        // wait a bit for it to finish
+        Thread.sleep( 100 );
+
+        // test to see if the count is right
+        assertTrue( "The number of ELEMENT_EVENT_EXCEEDED_IDLETIME_ONREQUEST events [" + meh.getExceededIdletimeCount()
+            + "] does not equal the number expected.", meh.getExceededIdletimeCount() >= 200 );
+    }
+
+    /**
+     * Simple event counter used to verify test results.
+     */
+    public static class MyEventHandler
+        implements IElementEventHandler
+    {
+        /** times spool called */
+        private int spoolCount = 0;
+
+        /** times spool not allowed */
+        private int spoolNotAllowedCount = 0;
+
+        /** times spool without disk */
+        private int spoolNoDiskCount = 0;
+
+        /** times exceeded maxlife */
+        private int exceededMaxlifeCount = 0;
+
+        /** times exceeded idle time */
+        private int exceededIdletimeCount = 0;
+
+        /**
+         * @param event
+         */
+        @Override
+        public synchronized void handleElementEvent( IElementEvent event )
+        {
+            //System.out.println( "Handling Event of Type " +
+            // event.getElementEvent() );
+
+            switch (event.getElementEvent())
+            {
+                case SPOOLED_DISK_AVAILABLE:
+                //System.out.println( "Handling Event of Type
+                // ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE, " + getSpoolCount() );
+                spoolCount++;
+                break;
+
+                case SPOOLED_NOT_ALLOWED:
+                spoolNotAllowedCount++;
+                break;
+
+                case SPOOLED_DISK_NOT_AVAILABLE:
+                spoolNoDiskCount++;
+                break;
+
+                case EXCEEDED_MAXLIFE_ONREQUEST:
+                exceededMaxlifeCount++;
+                break;
+
+                case EXCEEDED_IDLETIME_ONREQUEST:
+                exceededIdletimeCount++;
+                break;
+            }
+        }
+
+        /**
+         * @return Returns the spoolCount.
+         */
+        protected int getSpoolCount()
+        {
+            return spoolCount;
+        }
+
+        /**
+         * @return Returns the spoolNotAllowedCount.
+         */
+        protected int getSpoolNotAllowedCount()
+        {
+            return spoolNotAllowedCount;
+        }
+
+        /**
+         * @return Returns the spoolNoDiskCount.
+         */
+        protected int getSpoolNoDiskCount()
+        {
+            return spoolNoDiskCount;
+        }
+
+        /**
+         * @return the exceededMaxlifeCount
+         */
+        protected int getExceededMaxlifeCount()
+        {
+            return exceededMaxlifeCount;
+        }
+
+        /**
+         * @return the exceededIdletimeCount
+         */
+        protected int getExceededIdletimeCount()
+        {
+            return exceededIdletimeCount;
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLoggerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLoggerUnitTest.java
new file mode 100644
index 0000000..33903ca
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/CacheEventLoggerDebugLoggerUnitTest.java
@@ -0,0 +1,143 @@
+package org.apache.commons.jcs.engine.logging;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.WriterAppender;
+
+import java.io.StringWriter;
+
+/** Unit tests for the debug implementation */
+public class CacheEventLoggerDebugLoggerUnitTest
+    extends TestCase
+{
+
+    /** verify that we can log */
+    public void testLogICacheEvent_normal()
+    {
+        // SETUP
+        String logCategoryName = "testLogEvent_normal";
+
+        String source = "mySource";
+        String region = "my region";
+        String eventName = "MyEventName";
+        String optionalDetails = "SomeExtraData";
+        String key = "my key";
+
+        StringWriter stringWriter = new StringWriter();
+        configureLogger( stringWriter, logCategoryName );
+
+        CacheEventLoggerDebugLogger logger = new CacheEventLoggerDebugLogger();
+        logger.setLogCategoryName( logCategoryName );
+
+        ICacheEvent<String> event = logger.createICacheEvent( source, region, eventName, optionalDetails, key );
+
+        // DO WORK
+        logger.logICacheEvent( event );
+
+        // VERIFY
+        String result = stringWriter.toString();
+        assertTrue( "An event with the source should have been logged:" + result, result.indexOf( source ) != -1 );
+        assertTrue( "An event with the region should have been logged:" + result, result.indexOf( region ) != -1 );
+        assertTrue( "An event with the event name should have been logged:" + result, result.indexOf( eventName ) != -1 );
+        assertTrue( "An event with the optionalDetails should have been logged:" + result, result.indexOf( optionalDetails ) != -1 );
+        assertTrue( "An event with the key should have been logged:" + result, result.indexOf( key ) != -1 );
+    }
+
+    /** verify that we can log */
+    public void testLogApplicationEvent_normal()
+    {
+        // SETUP
+        String logCategoryName = "testLogApplicationEvent_normal";
+
+        String source = "mySource";
+        String eventName = "MyEventName";
+        String optionalDetails = "SomeExtraData";
+
+        StringWriter stringWriter = new StringWriter();
+        configureLogger( stringWriter, logCategoryName );
+
+        CacheEventLoggerDebugLogger logger = new CacheEventLoggerDebugLogger();
+        logger.setLogCategoryName( logCategoryName );
+
+        // DO WORK
+        logger.logApplicationEvent( source, eventName, optionalDetails );
+
+        // VERIFY
+        String result = stringWriter.toString();
+        assertTrue( "An event with the source should have been logged:" + result, result.indexOf( source ) != -1 );
+        assertTrue( "An event with the event name should have been logged:" + result, result.indexOf( eventName ) != -1 );
+        assertTrue( "An event with the optionalDetails should have been logged:" + result, result.indexOf( optionalDetails ) != -1 );
+    }
+
+    /** verify that we can log */
+    public void testLogError_normal()
+    {
+        // SETUP
+        String logCategoryName = "testLogApplicationEvent_normal";
+
+        String source = "mySource";
+        String eventName = "MyEventName";
+        String errorMessage = "SomeExtraData";
+
+        StringWriter stringWriter = new StringWriter();
+        configureLogger( stringWriter, logCategoryName );
+
+        CacheEventLoggerDebugLogger logger = new CacheEventLoggerDebugLogger();
+        logger.setLogCategoryName( logCategoryName );
+
+        // DO WORK
+        logger.logError( source, eventName, errorMessage );
+
+        // VERIFY
+        String result = stringWriter.toString();
+        assertTrue( "An event with the source should have been logged:" + result, result.indexOf( source ) != -1 );
+        assertTrue( "An event with the event name should have been logged:" + result, result.indexOf( eventName ) != -1 );
+        assertTrue( "An event with the errorMessage should have been logged:" + result, result.indexOf( errorMessage ) != -1 );
+    }
+
+    /**
+     * Configures a logger for the given name. This allows us to check the log output.
+     * <p>
+     * @param stringWriter
+     * @param loggerName
+     */
+    private void configureLogger( StringWriter stringWriter, String loggerName )
+    {
+        Logger logger = Logger.getLogger( loggerName );
+        WriterAppender appender = null;
+
+        try
+        {
+            appender = new WriterAppender( new PatternLayout(), stringWriter );
+        }
+        catch ( Exception e )
+        {
+            // NOOP
+        }
+
+        logger.addAppender( appender );
+        logger.setLevel( Level.DEBUG );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/MockCacheEventLogger.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/MockCacheEventLogger.java
new file mode 100644
index 0000000..6e7c35f
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/logging/MockCacheEventLogger.java
@@ -0,0 +1,95 @@
+package org.apache.commons.jcs.engine.logging;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
+import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
+
+/**
+ * For testing the configurator.
+ */
+public class MockCacheEventLogger
+    implements ICacheEventLogger
+{
+    /** test property */
+    private String testProperty;
+
+    /**
+     * @param source
+     * @param eventName
+     * @param optionalDetails
+     */
+    @Override
+    public void logApplicationEvent( String source, String eventName, String optionalDetails )
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * @param source
+     * @param eventName
+     * @param errorMessage
+     */
+    @Override
+    public void logError( String source, String eventName, String errorMessage )
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * @param source
+     * @param region
+     * @param eventName
+     * @param optionalDetails
+     * @param key
+     * @return ICacheEvent
+     */
+    @Override
+    public <T> ICacheEvent<T> createICacheEvent( String source, String region, String eventName, String optionalDetails,
+                                          T key )
+    {
+        return new CacheEvent<T>();
+    }
+
+    /**
+     * @param event
+     */
+    @Override
+    public <T> void logICacheEvent( ICacheEvent<T> event )
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * @param testProperty
+     */
+    public void setTestProperty( String testProperty )
+    {
+        this.testProperty = testProperty;
+    }
+
+    /**
+     * @return testProperty
+     */
+    public String getTestProperty()
+    {
+        return testProperty;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpllUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpllUnitTest.java
new file mode 100644
index 0000000..5b8ee37
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/match/KeyMatcherPatternImpllUnitTest.java
@@ -0,0 +1,118 @@
+package org.apache.commons.jcs.engine.match;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Unit tests for the key matcher. */
+public class KeyMatcherPatternImpllUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the matching method works.
+     */
+    public void testGetMatchingKeysFromArray_AllMatch()
+    {
+        // SETUP
+        int numToInsertPrefix1 = 10;
+        Set<String> keyArray = new HashSet<String>();
+
+        String keyprefix1 = "MyPrefixC";
+
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            keyArray.add(keyprefix1 + String.valueOf( i ));
+        }
+
+        KeyMatcherPatternImpl<String> keyMatcher = new KeyMatcherPatternImpl<String>();
+
+        // DO WORK
+        Set<String> result1 = keyMatcher.getMatchingKeysFromArray( keyprefix1 + ".", keyArray );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1: " + result1, numToInsertPrefix1, result1.size() );
+    }
+
+    /**
+     * Verify that the matching method works.
+     */
+    public void testGetMatchingKeysFromArray_AllMatchFirstNull()
+    {
+        // SETUP
+        int numToInsertPrefix1 = 10;
+        Set<String> keyArray = new HashSet<String>();
+
+        String keyprefix1 = "MyPrefixC";
+
+        // insert with prefix1
+        for ( int i = 1; i < numToInsertPrefix1 + 1; i++ )
+        {
+            keyArray.add(keyprefix1 + String.valueOf( i ));
+        }
+
+        KeyMatcherPatternImpl<String> keyMatcher = new KeyMatcherPatternImpl<String>();
+
+        // DO WORK
+        Set<String> result1 = keyMatcher.getMatchingKeysFromArray( keyprefix1 + "\\S+", keyArray );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1: " + result1, numToInsertPrefix1, result1.size() );
+    }
+
+    /**
+     * Verify that the matching method works.
+     */
+    public void testGetMatchingKeysFromArray_TwoTypes()
+    {
+        // SETUP
+        int numToInsertPrefix1 = 10;
+        int numToInsertPrefix2 = 50;
+        Set<String> keyArray = new HashSet<String>();
+
+        String keyprefix1 = "MyPrefixA";
+        String keyprefix2 = "MyPrefixB";
+
+        // insert with prefix1
+        for ( int i = 0; i < numToInsertPrefix1; i++ )
+        {
+            keyArray.add(keyprefix1 + String.valueOf( i ));
+        }
+
+        // insert with prefix2
+        for ( int i = numToInsertPrefix1; i < numToInsertPrefix2 + numToInsertPrefix1; i++ )
+        {
+            keyArray.add(keyprefix2 + String.valueOf( i ));
+        }
+
+        KeyMatcherPatternImpl<String> keyMatcher = new KeyMatcherPatternImpl<String>();
+
+        // DO WORK
+        Set<String> result1 = keyMatcher.getMatchingKeysFromArray( keyprefix1 + ".+", keyArray );
+        Set<String> result2 = keyMatcher.getMatchingKeysFromArray( keyprefix2 + ".+", keyArray );
+
+        // VERIFY
+        assertEquals( "Wrong number returned 1: " + result1, numToInsertPrefix1, result1.size() );
+        assertEquals( "Wrong number returned 2: " + result2, numToInsertPrefix2, result2.size() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/MockMemoryCache.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/MockMemoryCache.java
new file mode 100644
index 0000000..f3a86d8
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/MockMemoryCache.java
@@ -0,0 +1,256 @@
+package org.apache.commons.jcs.engine.memory;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.memory.behavior.IMemoryCache;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Mock implementation of a memory cache for testing things like the memory shrinker.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MockMemoryCache<K extends Serializable, V extends Serializable>
+    implements IMemoryCache<K, V>
+{
+    /** Config */
+    private ICompositeCacheAttributes cacheAttr;
+
+    /** Internal map */
+    private final HashMap<K, ICacheElement<K, V>> map = new HashMap<K, ICacheElement<K, V>>();
+
+    /** The number of times waterfall was called. */
+    public int waterfallCallCount = 0;
+
+    /** The number passed to the last call of free elements. */
+    public int lastNumberOfFreedElements = 0;
+
+    /**
+     * Does nothing
+     * @param cache
+     */
+    @Override
+    public void initialize( CompositeCache<K, V> cache )
+    {
+        // nothing
+    }
+
+    /**
+     * Destroy the memory cache
+     * <p>
+     * @throws IOException
+     */
+    @Override
+    public void dispose()
+        throws IOException
+    {
+        // nothing
+    }
+
+    /** @return size */
+    @Override
+    public int getSize()
+    {
+        return map.size();
+    }
+
+    /** @return stats */
+    @Override
+    public IStats getStatistics()
+    {
+        return null;
+    }
+
+    /**
+     * @return map.keySet().toArray( */
+    @Override
+    public Set<K> getKeySet()
+    {
+        return new LinkedHashSet<K>(map.keySet());
+    }
+
+    /**
+     * @param key
+     * @return map.remove( key ) != null
+     * @throws IOException
+     */
+    @Override
+    public boolean remove( K key )
+        throws IOException
+    {
+        return map.remove( key ) != null;
+    }
+
+    /**
+     * @throws IOException
+     */
+    @Override
+    public void removeAll()
+        throws IOException
+    {
+        map.clear();
+    }
+
+    /**
+     * @param key
+     * @return (ICacheElement) map.get( key )
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> get( K key )
+        throws IOException
+    {
+        return map.get( key );
+    }
+
+    /**
+     * @param keys
+     * @return elements
+     * @throws IOException
+     */
+    @Override
+    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
+        throws IOException
+    {
+        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
+
+        if ( keys != null && !keys.isEmpty() )
+        {
+            Iterator<K> iterator = keys.iterator();
+
+            while ( iterator.hasNext() )
+            {
+                K key = iterator.next();
+
+                ICacheElement<K, V> element = get( key );
+
+                if ( element != null )
+                {
+                    elements.put( key, element );
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    /**
+     * @param key
+     * @return (ICacheElement) map.get( key )
+     * @throws IOException
+     */
+    @Override
+    public ICacheElement<K, V> getQuiet( K key )
+        throws IOException
+    {
+        return map.get( key );
+    }
+
+    /**
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void waterfal( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        waterfallCallCount++;
+    }
+
+    /**
+     * @param ce
+     * @throws IOException
+     */
+    @Override
+    public void update( ICacheElement<K, V> ce )
+        throws IOException
+    {
+        if ( ce != null )
+        {
+            map.put( ce.getKey(), ce );
+        }
+    }
+
+    /**
+     * @return ICompositeCacheAttributes
+     */
+    @Override
+    public ICompositeCacheAttributes getCacheAttributes()
+    {
+        return cacheAttr;
+    }
+
+    /**
+     * @param cattr
+     */
+    @Override
+    public void setCacheAttributes( ICompositeCacheAttributes cattr )
+    {
+        this.cacheAttr = cattr;
+    }
+
+    /** @return null */
+    @Override
+    public CompositeCache<K, V> getCompositeCache()
+    {
+        return null;
+    }
+
+    /**
+     * @param group
+     * @return null
+     */
+    public Set<K> getGroupKeys( String group )
+    {
+        return null;
+    }
+
+    /**
+     * @return null
+     */
+    public Set<String> getGroupNames()
+    {
+        return null;
+    }
+
+    /**
+     * @param numberToFree
+     * @return 0
+     * @throws IOException
+     */
+    @Override
+    public int freeElements( int numberToFree )
+        throws IOException
+    {
+        lastNumberOfFreedElements = numberToFree;
+        return 0;
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCacheUnitTest.java
new file mode 100644
index 0000000..024ab6f
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/fifo/FIFOMemoryCacheUnitTest.java
@@ -0,0 +1,109 @@
+package org.apache.commons.jcs.engine.memory.fifo;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+
+import java.io.IOException;
+
+/** Unit tests for the fifo implementation. */
+public class FIFOMemoryCacheUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that the oldest inserted item is removed
+     * <p>
+     * @throws IOException
+     */
+    public void testExpirationPolicy_oneExtra()
+        throws IOException
+    {
+        // SETUP
+        int maxObjects = 10;
+        String cacheName = "testExpirationPolicy_oneExtra";
+
+        ICompositeCacheAttributes attributes = new CompositeCacheAttributes();
+        attributes.setCacheName(cacheName);
+        attributes.setMaxObjects( maxObjects );
+        attributes.setSpoolChunkSize( 1 );
+
+        FIFOMemoryCache<String, String> cache = new FIFOMemoryCache<String, String>();
+        cache.initialize( new CompositeCache<String, String>( attributes, new ElementAttributes() ) );
+
+        for ( int i = 0; i <= maxObjects; i++ )
+        {
+            CacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key" + i, "value" + i );
+            cache.update( element );
+        }
+
+        CacheElement<String, String> oneMoreElement = new CacheElement<String, String>( cacheName, "onemore", "onemore" );
+
+        // DO WORK
+        cache.update( oneMoreElement );
+
+        // VERIFY
+        assertEquals( "Should have max elements", maxObjects, cache.getSize() );
+        for ( int i = maxObjects; i > maxObjects; i-- )
+        {
+            assertNotNull( "Shjould have elemnt " + i, cache.get( "key" + i ) );
+        }
+        assertNotNull( "Shjould have oneMoreElement", cache.get( "onemore" ) );
+    }
+
+    /**
+     * Verify that the oldest inserted item is removed
+     * <p>
+     * @throws IOException
+     */
+    public void testExpirationPolicy_doubleOver()
+        throws IOException
+    {
+        // SETUP
+        int maxObjects = 10;
+        String cacheName = "testExpirationPolicy_oneExtra";
+
+        ICompositeCacheAttributes attributes = new CompositeCacheAttributes();
+        attributes.setCacheName(cacheName);
+        attributes.setMaxObjects( maxObjects );
+        attributes.setSpoolChunkSize( 1 );
+
+        FIFOMemoryCache<String, String> cache = new FIFOMemoryCache<String, String>();
+        cache.initialize( new CompositeCache<String, String>( attributes, new ElementAttributes() ) );
+
+        // DO WORK
+        for ( int i = 0; i <= (maxObjects * 2); i++ )
+        {
+            CacheElement<String, String> element = new CacheElement<String, String>( cacheName, "key" + i, "value" + i );
+            cache.update( element );
+        }
+
+        // VERIFY
+        assertEquals( "Should have max elements", maxObjects, cache.getSize() );
+        for ( int i = (maxObjects * 2); i > maxObjects; i-- )
+        {
+            assertNotNull( "Shjould have elemnt " + i, cache.get( "key" + i ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheConcurrentUnitTest.java
new file mode 100644
index 0000000..733b3c7
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheConcurrentUnitTest.java
@@ -0,0 +1,175 @@
+package org.apache.commons.jcs.engine.memory.lru;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the LRUMemory cache. This one uses three different
+ * regions for three threads.
+ */
+public class LHMLRUMemoryCacheConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory
+     * cache regions.
+     */
+    private static int items = 200;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     *
+     * @param testName
+     */
+    public LHMLRUMemoryCacheConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     *
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { LHMLRUMemoryCacheConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new LHMLRUMemoryCacheConcurrentUnitTest( "testLHMLRUMemoryCache" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "indexedRegion1" );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        //JCS.setConfigFilename( "/TestLHMLRUCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should be dumped.
+     * <p>
+     * @param region
+     *            Name of the region to access
+     *
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CompositeCacheManager cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+        cacheMgr.configure( "/TestLHMLRUCache.ccf" );
+        CompositeCache<String, String> cache = cacheMgr.getCache( region );
+
+        LRUMemoryCache<String, String> lru = new LRUMemoryCache<String, String>();
+        lru.initialize( cache );
+
+        // Add items to cache
+
+        for ( int i = 0; i < items; i++ )
+        {
+            ICacheElement<String, String> ice = new CacheElement<String, String>( cache.getCacheName(), i + ":key", region + " data " + i );
+            ice.setElementAttributes( cache.getElementAttributes() );
+            lru.update( ice );
+        }
+
+        // Test that initial items have been purged
+        for ( int i = 0; i < 100; i++ )
+        {
+            assertNull( "Should not have " + i + ":key", lru.get( i + ":key" ) );
+        }
+
+        // Test that last items are in cache
+        for ( int i = 100; i < items; i++ )
+        {
+            String value = lru.get( i + ":key" ).getVal();
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getMultiple returns all the items remaining in cache and none of the missing ones
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i < items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = lru.getMultiple( keys );
+        for ( int i = 0; i < 100; i++ )
+        {
+            assertNull( "Should not have " + i + ":key", elements.get( i + ":key" ) );
+        }
+        for ( int i = 100; i < items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+
+        for ( int i = 0; i < items; i++ )
+        {
+            lru.remove( i + ":key" );
+        }
+
+        // Verify removal
+
+        for ( int i = 0; i < items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", lru.get( i + ":key" ) );
+        }
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheUnitTest.java
new file mode 100644
index 0000000..c8595b0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LHMLRUMemoryCacheUnitTest.java
@@ -0,0 +1,312 @@
+package org.apache.commons.jcs.engine.memory.lru;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for the test LHMLRU implementation.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class LHMLRUMemoryCacheUnitTest
+    extends TestCase
+{
+    /** Test setup */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestLHMLRUCache.ccf" );
+    }
+
+    /**
+     * Verify that the mru gets used by a non-defined region when it is set as the default in the
+     * default region.
+     * <p>
+     * @throws CacheException
+     */
+    public void testLoadFromCCF()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testLoadFromCCF" );
+        String memoryCacheName = cache.getCacheAttributes().getMemoryCacheName();
+        assertTrue( "Cache name should have LHMLRU in it.", memoryCacheName.indexOf( "LHMLRUMemoryCache" ) != -1 );
+    }
+
+    /**
+     * put twice as many as the max.  verify that the second half is in the cache.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutGetThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max -1; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache." + cache.getStats(), value );
+        }
+
+        // Test that last items are in cache
+        // skip 2 for the buffer.
+        for ( int i = max + 2; i < items; i++ )
+        {
+            String value = cache.get( i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+        // Test that getMultiple returns all the items remaining in cache and none of the missing ones
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i < items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = cache.getCacheElements( keys );
+        for ( int i = max-1; i >= 0; i-- )
+        {
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache." + cache.getStats(), elements.get( i + ":key" ) );
+        }
+        for ( int i = max + 2; i < items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", "myregion" + " data " + i, element.getVal() );
+        }
+    }
+
+    /**
+     * Put twice as many as the max, twice. verify that the second half is in the cache.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutGetThroughHubTwice()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHubTwice" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max -1; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+
+        // Test that last items are in cache
+        // skip 2 for the buffer.
+        for ( int i = max + 2; i < items; i++ )
+        {
+            String value = cache.get( i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+    }
+
+    /**
+     * put the max and remove each. verify that they are all null.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutRemoveThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutRemoveThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.remove( i + ":key" );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+    }
+
+    /**
+     * put the max and clear. verify that no elements remain.
+     * <p>
+     * @throws CacheException
+     */
+    public void testClearThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testClearThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        cache.clear();
+
+        // Test that first items are not in the cache
+        for ( int i = max; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+    }
+
+    /**
+     * Get stats.
+     * <p>
+     * @throws CacheException
+     */
+    public void testGetStatsThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testGetStatsThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        String stats = cache.getStats();
+
+        //System.out.println( stats );
+
+        // TODO improve stats check
+        assertTrue( "Should have 200 puts" + stats, stats.indexOf( "200" ) != -1 );
+    }
+
+    /**
+     * Put half the max and clear. get the key array and verify that it has the correct number of
+     * items.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetKeyArray()
+        throws Exception
+    {
+        CompositeCacheManager cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+        cacheMgr.configure( "/TestLHMLRUCache.ccf" );
+        CompositeCache<String, String> cache = cacheMgr.getCache( "testGetKeyArray" );
+
+        LHMLRUMemoryCache<String, String> mru = new LHMLRUMemoryCache<String, String>();
+        mru.initialize( cache );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max / 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            ICacheElement<String, String> ice = new CacheElement<String, String>( cache.getCacheName(), i + ":key", cache.getCacheName() + " data " + i );
+            ice.setElementAttributes( cache.getElementAttributes() );
+            mru.update( ice );
+        }
+
+        Set<String> keys = mru.getKeySet();
+
+        assertEquals( "Wrong number of keys.", items, keys.size() );
+    }
+
+    /**
+     * Add a few keys with the delimiter. Remove them.
+     * <p>
+     * @throws CacheException
+     */
+    public void testRemovePartialThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testRemovePartialThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max / 2;
+
+        cache.put( "test", "data" );
+
+        String root = "myroot";
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( root + ":" + i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that last items are in cache
+        for ( int i = 0; i < items; i++ )
+        {
+            String value = cache.get( root + ":" + i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+        // remove partial
+        cache.remove( root + ":" );
+
+        for ( int i = 0; i < items; i++ )
+        {
+            assertNull( "Should have been removed by partial loop.", cache.get( root + ":" + i + ":key" ) );
+        }
+
+        assertNotNull( "Other item should be in the cache.", cache.get( "test" ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCacheConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCacheConcurrentUnitTest.java
new file mode 100644
index 0000000..3efcc77
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/lru/LRUMemoryCacheConcurrentUnitTest.java
@@ -0,0 +1,171 @@
+package org.apache.commons.jcs.engine.memory.lru;
+
+/*
+ * 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.
+ */
+
+import junit.extensions.ActiveTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test which exercises the LRUMemory cache. This one uses three different
+ * regions for three threads.
+ */
+public class LRUMemoryCacheConcurrentUnitTest
+    extends TestCase
+{
+    /**
+     * Number of items to cache, twice the configured maxObjects for the memory
+     * cache regions.
+     */
+    private static int items = 200;
+
+    /**
+     * Constructor for the TestDiskCache object.
+     * <p>
+     * @param testName
+     */
+    public LRUMemoryCacheConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * Main method passes this test to the text test runner.
+     * <p>
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        String[] testCaseName = { LRUMemoryCacheConcurrentUnitTest.class.getName() };
+        junit.textui.TestRunner.main( testCaseName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        ActiveTestSuite suite = new ActiveTestSuite();
+
+        suite.addTest( new LRUMemoryCacheConcurrentUnitTest( "testLRUMemoryCache" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runTestForRegion( "testRegion1" );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Test setup
+     */
+    @Override
+    public void setUp()
+    {
+        //JCS.setConfigFilename( "/TestDiskCache.ccf" );
+    }
+
+    /**
+     * Adds items to cache, gets them, and removes them. The item count is more
+     * than the size of the memory cache, so items should be dumped.
+     * <p>
+     * @param region
+     *            Name of the region to access
+     * @throws Exception
+     *                If an error occurs
+     */
+    public void runTestForRegion( String region )
+        throws Exception
+    {
+        CompositeCacheManager cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+        cacheMgr.configure( "/TestDiskCache.ccf" );
+        CompositeCache<String, String> cache = cacheMgr.getCache( region );
+
+        LRUMemoryCache<String, String> lru = new LRUMemoryCache<String, String>();
+        lru.initialize( cache );
+
+        // Add items to cache
+
+        for ( int i = 0; i < items; i++ )
+        {
+            ICacheElement<String, String> ice = new CacheElement<String, String>( cache.getCacheName(), i + ":key", region + " data " + i );
+            ice.setElementAttributes( cache.getElementAttributes() );
+            lru.update( ice );
+        }
+
+        // Test that initial items have been purged
+        for ( int i = 0; i < 100; i++ )
+        {
+            assertNull( "Should not have " + i + ":key", lru.get( i + ":key" ) );
+        }
+
+        // Test that last items are in cache
+        for ( int i = 100; i < items; i++ )
+        {
+            String value = lru.get( i + ":key" ).getVal();
+            assertEquals( region + " data " + i, value );
+        }
+
+        // Test that getMultiple returns all the items remaining in cache and none of the missing ones
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i < items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = lru.getMultiple( keys );
+        for ( int i = 0; i < 100; i++ )
+        {
+            assertNull( "Should not have " + i + ":key", elements.get( i + ":key" ) );
+        }
+        for ( int i = 100; i < items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", region + " data " + i, element.getVal() );
+        }
+
+        // Remove all the items
+        for ( int i = 0; i < items; i++ )
+        {
+            lru.remove( i + ":key" );
+        }
+
+        // Verify removal
+        for ( int i = 0; i < items; i++ )
+        {
+            assertNull( "Removed key should be null: " + i + ":key", lru.get( i + ":key" ) );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/LRUvsMRUPerformanceTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/LRUvsMRUPerformanceTest.java
new file mode 100644
index 0000000..df65be5
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/LRUvsMRUPerformanceTest.java
@@ -0,0 +1,183 @@
+package org.apache.commons.jcs.engine.memory.mru;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Tests the performance difference between the LRU and the MRU. There should be very little.
+ */
+public class LRUvsMRUPerformanceTest
+    extends TestCase
+{
+    /** ration we want */
+    float ratioPut = 0;
+
+    /** ration we want */
+    float ratioGet = 0;
+
+    /** ration we want */
+    float target = 1.20f;
+
+    /** times to run */
+    int loops = 20;
+
+    /** item per run */
+    int tries = 10000;
+
+    /**
+     * A unit test for JUnit
+     * @throws Exception Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        Log log1 = LogFactory.getLog( LRUMemoryCache.class );
+        if ( log1.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+        Log log2 = LogFactory.getLog( MRUMemoryCache.class );
+        if ( log2.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+        doWork();
+
+        // these were when the mru was implemented with the jdk linked list
+        //assertTrue( "Ratio is unacceptible.", this.ratioPut < target );
+        ///assertTrue( "Ratio is unacceptible.", this.ratioGet < target );
+    }
+
+    /**
+     * Runs the test
+     */
+    public void doWork()
+    {
+
+        long start = 0;
+        long end = 0;
+        long time = 0;
+        float tPer = 0;
+
+        long putTotalLRU = 0;
+        long getTotalLRU = 0;
+        long putTotalMRU = 0;
+        long getTotalMRU = 0;
+
+        try
+        {
+
+            JCS.setConfigFilename( "/TestMRUCache.ccf" );
+            CacheAccess<String, String> cache = JCS.getInstance( "lruDefined" );
+            CacheAccess<String, String> mru = JCS.getInstance( "mruDefined" );
+
+            System.out.println( "LRU = " + cache );
+
+            for ( int j = 0; j < loops; j++ )
+            {
+
+                System.out.println( "Beginning loop " + j );
+
+                String name = "LRU      ";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalLRU += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalLRU += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                // /////////////////////////////////////////////////////////////
+                name = "MRU";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    mru.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalMRU += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    mru.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalMRU += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                System.out.println( "\n" );
+            }
+
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace( System.out );
+            System.out.println( e );
+        }
+
+        long putAvJCS = putTotalLRU / loops;
+        long getAvJCS = getTotalLRU / loops;
+        long putAvHashtable = putTotalMRU / loops;
+        long getAvHashtable = getTotalMRU / loops;
+
+        System.out.println( "Finished " + loops + " loops of " + tries + " gets and puts" );
+
+        System.out.println( "\n" );
+        System.out.println( "Put average for JCS       = " + putAvJCS );
+        System.out.println( "Put average for MRU = " + putAvHashtable );
+        ratioPut = Float.intBitsToFloat( (int) putAvJCS ) / Float.intBitsToFloat( (int) putAvHashtable );
+        System.out.println( "JCS puts took " + ratioPut + " times the Hashtable, the goal is <" + target + "x" );
+
+        System.out.println( "\n" );
+        System.out.println( "Get average for JCS       = " + getAvJCS );
+        System.out.println( "Get average for MRU = " + getAvHashtable );
+        ratioGet = Float.intBitsToFloat( (int) getAvJCS ) / Float.intBitsToFloat( (int) getAvHashtable );
+        System.out.println( "JCS gets took " + ratioGet + " times the Hashtable, the goal is <" + target + "x" );
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCacheUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCacheUnitTest.java
new file mode 100644
index 0000000..093c14e
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/mru/MRUMemoryCacheUnitTest.java
@@ -0,0 +1,312 @@
+package org.apache.commons.jcs.engine.memory.mru;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tests for the test MRU implementation.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class MRUMemoryCacheUnitTest
+    extends TestCase
+{
+    /** Test setup */
+    @Override
+    public void setUp()
+    {
+        JCS.setConfigFilename( "/TestMRUCache.ccf" );
+    }
+
+    /**
+     * Verify that the mru gets used by a non-defined region when it is set as the default in the
+     * default region.
+     * <p>
+     * @throws CacheException
+     */
+    public void testLoadFromCCF()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGet" );
+        String memoryCacheName = cache.getCacheAttributes().getMemoryCacheName();
+        assertTrue( "Cache name should have MRU in it.", memoryCacheName.indexOf( "MRUMemoryCache" ) != -1 );
+    }
+
+    /**
+     * put twice as many as the max.  verify that the second half is in the cache.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutGetThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max -1; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache." + cache.getStats(), value );
+        }
+
+        // Test that last items are in cache
+        // skip 2 for the buffer.
+        for ( int i = max + 2; i < items; i++ )
+        {
+            String value = cache.get( i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+        // Test that getMultiple returns all the items remaining in cache and none of the missing ones
+        Set<String> keys = new HashSet<String>();
+        for ( int i = 0; i < items; i++ )
+        {
+            keys.add( i + ":key" );
+        }
+
+        Map<String, ICacheElement<String, String>> elements = cache.getCacheElements( keys );
+        for ( int i = max-1; i >= 0; i-- )
+        {
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache." + cache.getStats(), elements.get( i + ":key" ) );
+        }
+        for ( int i = max + 2; i < items; i++ )
+        {
+            ICacheElement<String, String> element = elements.get( i + ":key" );
+            assertNotNull( "element " + i + ":key is missing", element );
+            assertEquals( "value " + i + ":key", "myregion" + " data " + i, element.getVal() );
+        }
+    }
+
+    /**
+     * Put twice as many as the max, twice. verify that the second half is in the cache.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutGetThroughHubTwice()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max-1; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+
+        // Test that last items are in cache
+        // skip 2 for the buffer.
+        for ( int i = max + 2; i < items; i++ )
+        {
+            String value = cache.get( i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+    }
+
+    /**
+     * put the max and remove each. verify that they are all null.
+     * <p>
+     * @throws CacheException
+     */
+    public void testPutRemoveThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.remove( i + ":key" );
+        }
+
+        // Test that first items are not in the cache
+        for ( int i = max; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+    }
+
+    /**
+     * put the max and clear. verify that no elements remain.
+     * <p>
+     * @throws CacheException
+     */
+    public void testClearThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testPutGetThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        cache.clear();
+
+        // Test that first items are not in the cache
+        for ( int i = max; i >= 0; i-- )
+        {
+            String value = cache.get( i + ":key" );
+            assertNull( "Should not have value for key [" + i + ":key" + "] in the cache.", value );
+        }
+    }
+
+    /**
+     * Get stats.
+     * <p>
+     * @throws CacheException
+     */
+    public void testGetStatsThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testGetStatsThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max * 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( i + ":key", "myregion" + " data " + i );
+        }
+
+        String stats = cache.getStats();
+
+//        System.out.println( stats );
+
+        // TODO improve stats check
+        assertTrue( "Should have 200 puts", stats.indexOf( "2000" ) != -1 );
+    }
+
+    /**
+     * Put half the max and clear. get the key array and verify that it has the correct number of
+     * items.
+     * <p>
+     * @throws Exception
+     */
+    public void testGetKeyArray()
+        throws Exception
+    {
+        CompositeCacheManager cacheMgr = CompositeCacheManager.getUnconfiguredInstance();
+        cacheMgr.configure( "/TestMRUCache.ccf" );
+        CompositeCache<String, String> cache = cacheMgr.getCache( "testGetKeyArray" );
+
+        MRUMemoryCache<String, String> mru = new MRUMemoryCache<String, String>();
+        mru.initialize( cache );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max / 2;
+
+        for ( int i = 0; i < items; i++ )
+        {
+            ICacheElement<String, String> ice = new CacheElement<String, String>( cache.getCacheName(), i + ":key", cache.getCacheName() + " data " + i );
+            ice.setElementAttributes( cache.getElementAttributes() );
+            mru.update( ice );
+        }
+
+        Set<String> keys = mru.getKeySet();
+
+        assertEquals( "Wrong number of keys.", items, keys.size() );
+    }
+
+    /**
+     * Add a few keys with the delimiter. Remove them.
+     * <p>
+     * @throws CacheException
+     */
+    public void testRemovePartialThroughHub()
+        throws CacheException
+    {
+        CacheAccess<String, String> cache = JCS.getInstance( "testGetStatsThroughHub" );
+
+        int max = cache.getCacheAttributes().getMaxObjects();
+        int items = max / 2;
+
+        cache.put( "test", "data" );
+
+        String root = "myroot";
+
+        for ( int i = 0; i < items; i++ )
+        {
+            cache.put( root + ":" + i + ":key", "myregion" + " data " + i );
+        }
+
+        // Test that last items are in cache
+        for ( int i = 0; i < items; i++ )
+        {
+            String value = cache.get( root + ":" + i + ":key" );
+            assertEquals( "myregion" + " data " + i, value );
+        }
+
+        // remove partial
+        cache.remove( root + ":" );
+
+        for ( int i = 0; i < items; i++ )
+        {
+            assertNull( "Should have been removed by partial loop.", cache.get( root + ":" + i + ":key" ) );
+        }
+
+        assertNotNull( "Other item should be in the cache.", cache.get( "test" ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThreadUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThreadUnitTest.java
new file mode 100644
index 0000000..f38e9f8
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/engine/memory/shrinking/ShrinkerThreadUnitTest.java
@@ -0,0 +1,336 @@
+package org.apache.commons.jcs.engine.memory.shrinking;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.ElementAttributesUtils;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.event.ElementEventHandlerMockImpl;
+import org.apache.commons.jcs.engine.control.event.behavior.ElementEventType;
+import org.apache.commons.jcs.engine.memory.MockMemoryCache;
+
+import java.io.IOException;
+
+/**
+ * This tests the functionality of the shrinker thread.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ShrinkerThreadUnitTest
+    extends TestCase
+{
+    /** verify the check for removal
+     * <p>
+     * @throws IOException */
+    public void testCheckForRemoval_Expired() throws IOException
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
+        cacheAttr.setMaxSpoolPerRun( 10 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+
+        String key = "key";
+        String value = "value";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+        ElementAttributes elementAttr = new ElementAttributes();
+        elementAttr.setIsEternal( false );
+        element.setElementAttributes( elementAttr );
+        element.getElementAttributes().setMaxLife(1);
+
+        long now = System.currentTimeMillis();
+        // add two seconds
+        now += 2000;
+
+        // DO WORK
+        boolean result = cache.isExpired( element, now,
+                ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
+                ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
+
+        // VERIFY
+        assertTrue( "Item should have expired.", result );
+    }
+
+    /** verify the check for removal
+     * <p>
+     * @throws IOException */
+    public void testCheckForRemoval_NotExpired() throws IOException
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
+        cacheAttr.setMaxSpoolPerRun( 10 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+
+        String key = "key";
+        String value = "value";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+        ElementAttributes elementAttr = new ElementAttributes();
+        elementAttr.setIsEternal( false );
+        element.setElementAttributes( elementAttr );
+        element.getElementAttributes().setMaxLife(1);
+
+        long now = System.currentTimeMillis();
+        // subtract two seconds
+        now -= 2000;
+
+        // DO WORK
+        boolean result = cache.isExpired( element, now,
+                ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
+                ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
+
+        // VERIFY
+        assertFalse( "Item should not have expired.", result );
+    }
+
+    /** verify the check for removal
+     * <p>
+     * @throws IOException */
+    public void testCheckForRemoval_IdleTooLong() throws IOException
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
+        cacheAttr.setMaxSpoolPerRun( 10 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+
+        String key = "key";
+        String value = "value";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+        ElementAttributes elementAttr = new ElementAttributes();
+        elementAttr.setIsEternal( false );
+        element.setElementAttributes( elementAttr );
+        element.getElementAttributes().setMaxLife(100);
+        element.getElementAttributes().setIdleTime( 1 );
+
+        long now = System.currentTimeMillis();
+        // add two seconds
+        now += 2000;
+
+        // DO WORK
+        boolean result = cache.isExpired( element, now,
+                ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
+                ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
+
+        // VERIFY
+        assertTrue( "Item should have expired.", result );
+    }
+
+    /** verify the check for removal
+     * <p>
+     * @throws IOException */
+    public void testCheckForRemoval_NotIdleTooLong() throws IOException
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
+        cacheAttr.setMaxSpoolPerRun( 10 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+
+        String key = "key";
+        String value = "value";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+        ElementAttributes elementAttr = new ElementAttributes();
+        elementAttr.setIsEternal( false );
+        element.setElementAttributes( elementAttr );
+        element.getElementAttributes().setMaxLife(100);
+        element.getElementAttributes().setIdleTime( 1 );
+
+        long now = System.currentTimeMillis();
+        // subtract two seconds
+        now -= 2000;
+
+        // DO WORK
+        boolean result = cache.isExpired( element, now,
+                ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
+                ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
+
+        // VERIFY
+        assertFalse( "Item should not have expired.", result );
+    }
+
+    /**
+     * Setup cache attributes in mock. Create the shrinker with the mock. Add some elements into the
+     * mock memory cache see that they get spooled.
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleShrink()
+        throws Exception
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMemoryCacheName("org.apache.commons.jcs.engine.memory.MockMemoryCache");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
+        cacheAttr.setMaxSpoolPerRun( 10 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+        MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
+
+        String key = "key";
+        String value = "value";
+
+        ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+
+        ElementAttributes elementAttr = new ElementAttributes();
+        elementAttr.setIsEternal( false );
+        element.setElementAttributes( elementAttr );
+        element.getElementAttributes().setMaxLife(1);
+        memory.update( element );
+
+        ICacheElement<String, String> returnedElement1 = memory.get( key );
+        assertNotNull( "We should have received an element", returnedElement1 );
+
+        // set this to 2 seconds ago.
+        ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
+
+        // DO WORK
+        ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( cache );
+        shrinker.run();
+
+        Thread.sleep( 500 );
+
+        // VERIFY
+        ICacheElement<String, String> returnedElement2 = memory.get( key );
+        assertTrue( "Waterfall should have been called.", memory.waterfallCallCount > 0 );
+        assertNull( "We not should have received an element.  It should have been spooled.", returnedElement2 );
+    }
+
+    /**
+     * Add 10 to the memory cache. Set the spool per run limit to 3.
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleShrinkMultiple()
+        throws Exception
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMemoryCacheName("org.apache.commons.jcs.engine.memory.MockMemoryCache");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
+        cacheAttr.setMaxSpoolPerRun( 3 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+        MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
+
+        for ( int i = 0; i < 10; i++ )
+        {
+            String key = "key" + i;
+            String value = "value";
+
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+
+            ElementAttributes elementAttr = new ElementAttributes();
+            elementAttr.setIsEternal( false );
+            element.setElementAttributes( elementAttr );
+            element.getElementAttributes().setMaxLife(1);
+            memory.update( element );
+
+            ICacheElement<String, String> returnedElement1 = memory.get( key );
+            assertNotNull( "We should have received an element", returnedElement1 );
+
+            // set this to 2 seconds ago.
+            ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
+        }
+
+        // DO WORK
+        ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( cache );
+        shrinker.run();
+
+        // VERIFY
+        Thread.sleep( 500 );
+        assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
+        assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
+    }
+
+    /**
+     * Add a mock event handler to the items. Verify that it gets called.
+     * <p>
+     * This is only testing the spooled background event
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleShrinkMultipleWithEventHandler()
+        throws Exception
+    {
+        // SETUP
+        CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
+        cacheAttr.setCacheName("testRegion");
+        cacheAttr.setMemoryCacheName("org.apache.commons.jcs.engine.memory.MockMemoryCache");
+        cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
+        cacheAttr.setMaxSpoolPerRun( 3 );
+
+        CompositeCache<String, String> cache = new CompositeCache<String, String>(cacheAttr, new ElementAttributes());
+        MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
+
+        ElementEventHandlerMockImpl handler = new ElementEventHandlerMockImpl();
+
+        for ( int i = 0; i < 10; i++ )
+        {
+            String key = "key" + i;
+            String value = "value";
+
+            ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
+
+            ElementAttributes elementAttr = new ElementAttributes();
+            elementAttr.addElementEventHandler( handler );
+            elementAttr.setIsEternal( false );
+            element.setElementAttributes( elementAttr );
+            element.getElementAttributes().setMaxLife(1);
+            memory.update( element );
+
+            ICacheElement<String, String> returnedElement1 = memory.get( key );
+            assertNotNull( "We should have received an element", returnedElement1 );
+
+            // set this to 2 seconds ago.
+            ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
+        }
+
+        // DO WORK
+        ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( cache );
+        shrinker.run();
+
+        // VERIFY
+        Thread.sleep( 500 );
+        assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
+        // the shrinker delegates the the composite cache on the memory cache to put the
+        // event on the queue.  This make it hard to test.  TODO we need to change this to make it easier to verify.
+        //assertEquals( "Event handler ExceededIdleTimeBackground called the wrong number of times.", 3, handler.getExceededIdleTimeBackgroundCount() );
+        assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/access/JCSWorkerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/access/JCSWorkerUnitTest.java
new file mode 100644
index 0000000..c4ff5e0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/access/JCSWorkerUnitTest.java
@@ -0,0 +1,71 @@
+package org.apache.commons.jcs.utils.access;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for the JCS worker.
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class JCSWorkerUnitTest
+    extends TestCase
+{
+
+    /**
+     * Test basic worker functionality.  This is a serial not a concurrent test.
+     * <p>
+     * Just verify that the worker will go to the cache before asking the helper.
+     *
+     * @throws Exception
+     *
+     */
+    public void testSimpleGet()
+        throws Exception
+    {
+        JCSWorker<String, Long> cachingWorker = new JCSWorker<String, Long>( "example region" );
+
+        // This is the helper.
+        JCSWorkerHelper helper = new AbstractJCSWorkerHelper()
+        {
+            int timesCalled = 0;
+
+            @Override
+            public Object doWork()
+            {
+                Object results = Long.valueOf( ++timesCalled );
+                return results;
+            }
+        };
+
+        String key = "abc";
+
+        Long result = cachingWorker.getResult( key, helper );
+        assertEquals( "Called the wrong number of times", Long.valueOf( 1 ), result );
+
+        // should get it from the cache.
+        Long result2 = cachingWorker.getResult( key, helper );
+        assertEquals( "Called the wrong number of times", Long.valueOf( 1 ), result2 );
+
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/config/PropertySetterUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/config/PropertySetterUnitTest.java
new file mode 100644
index 0000000..bb363f7
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/config/PropertySetterUnitTest.java
@@ -0,0 +1,61 @@
+package org.apache.commons.jcs.utils.config;
+
+/*
+ * 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.
+ */
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * Test property settings
+ *
+ * @author Thomas Vandahl
+ *
+ */
+public class PropertySetterUnitTest
+{
+    enum EnumTest { ONE, TWO, THREE };
+
+    @Test
+    public void testConvertArg()
+    {
+        PropertySetter ps = new PropertySetter(this);
+        Object s = ps.convertArg("test", String.class);
+        assertEquals("Should be a string", "test", s);
+
+        Object i = ps.convertArg("1", Integer.TYPE);
+        assertEquals("Should be an integer", Integer.valueOf(1), i);
+
+        Object l = ps.convertArg("1", Long.TYPE);
+        assertEquals("Should be a long", Long.valueOf(1), l);
+
+        Object b = ps.convertArg("true", Boolean.TYPE);
+        assertEquals("Should be a boolean", Boolean.TRUE, b);
+
+        Object e = ps.convertArg("TWO", EnumTest.class);
+        assertEquals("Should be an enum", EnumTest.TWO, e);
+
+        Object f = ps.convertArg("test.conf", File.class);
+        assertTrue("Should be a file", f instanceof File);
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/MockDiscoveryListener.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/MockDiscoveryListener.java
new file mode 100644
index 0000000..a8b5f75
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/MockDiscoveryListener.java
@@ -0,0 +1,56 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.jcs.utils.discovery.behavior.IDiscoveryListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Mock listener, for testing. */
+public class MockDiscoveryListener
+    implements IDiscoveryListener
+{
+    /** discovered services. */
+    public List<DiscoveredService> discoveredServices = new ArrayList<DiscoveredService>();
+
+    /**
+     * Adds the entry to a list. I'm not using a set. I want to see if we get dupes.
+     * <p>
+     * @param service
+     */
+    @Override
+    public void addDiscoveredService( DiscoveredService service )
+    {
+        discoveredServices.add( service );
+    }
+
+    /**
+     * Removes it from the list.
+     * <p>
+     * @param service
+     */
+    @Override
+    public void removeDiscoveredService( DiscoveredService service )
+    {
+        discoveredServices.remove( service );
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderUnitTest.java
new file mode 100644
index 0000000..7e53ecd
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoverySenderUnitTest.java
@@ -0,0 +1,154 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.utils.discovery.UDPDiscoveryMessage.BroadcastType;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the sender.
+ */
+public class UDPDiscoverySenderUnitTest
+    extends TestCase
+{
+    /** multicast address to send/receive on */
+    private static final String ADDRESS = "228.4.5.9";
+
+    /** multicast address to send/receive on */
+    private static final int PORT = 5556;
+
+    /** imaginary host address for sending */
+    private static final String SENDING_HOST = "imaginary host address";
+
+    /** imaginary port for sending */
+    private static final int SENDING_PORT = 1;
+
+    /** receiver instance for tests */
+    private UDPDiscoveryReceiver receiver;
+
+    /** sender instance for tests */
+    private UDPDiscoverySender sender;
+
+    /**
+     * Set up the receiver. Maybe better to just code sockets here? Set up the sender for sending
+     * the message.
+     * <p>
+     * @throws Exception on error
+     */
+    @Override
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        receiver = new UDPDiscoveryReceiver( null, ADDRESS, PORT );
+        sender = new UDPDiscoverySender( ADDRESS, PORT );
+    }
+
+    /**
+     * Kill off the sender and receiver.
+     * <p>
+     * @throws Exception on error
+     */
+    @Override
+    protected void tearDown()
+        throws Exception
+    {
+        receiver.shutdown();
+        sender.destroy();
+        super.tearDown();
+    }
+
+    /**
+     * Test sending a live messages.
+     * <p>
+     * @throws Exception on error
+     */
+    public void testPassiveBroadcast()
+        throws Exception
+    {
+        // SETUP
+        ArrayList<String> cacheNames = new ArrayList<String>();
+
+        // DO WORK
+        sender.passiveBroadcast( SENDING_HOST, SENDING_PORT, cacheNames, 1L );
+
+        // VERIFY
+        // grab the sent message
+        Object obj = receiver.waitForMessage() ;
+
+        assertTrue( "unexpected crap received", obj instanceof UDPDiscoveryMessage );
+
+        UDPDiscoveryMessage msg = (UDPDiscoveryMessage) obj;
+        // disabled test because of JCS-89
+        // assertEquals( "wrong host", SENDING_HOST, msg.getHost() );
+        assertEquals( "wrong port", SENDING_PORT, msg.getPort() );
+        assertEquals( "wrong message type", BroadcastType.PASSIVE, msg.getMessageType() );
+    }
+
+    /**
+     * Test sending a remove broadcast.
+     * <p>
+     * @throws Exception on error
+     */
+    public void testRemoveBroadcast()
+        throws Exception
+    {
+        // SETUP
+        ArrayList<String> cacheNames = new ArrayList<String>();
+
+        // DO WORK
+        sender.removeBroadcast( SENDING_HOST, SENDING_PORT, cacheNames, 1L );
+
+        // VERIFY
+        // grab the sent message
+        Object obj = receiver.waitForMessage();
+
+        assertTrue( "unexpected crap received", obj instanceof UDPDiscoveryMessage );
+
+        UDPDiscoveryMessage msg = (UDPDiscoveryMessage) obj;
+        // disabled test because of JCS-89
+        // assertEquals( "wrong host", SENDING_HOST, msg.getHost() );
+        assertEquals( "wrong port", SENDING_PORT, msg.getPort() );
+        assertEquals( "wrong message type", BroadcastType.REMOVE, msg.getMessageType() );
+    }
+
+    /**
+     * Test sending a request broadcast.
+     * <p>
+     * @throws Exception on error
+     */
+    public void testRequestBroadcast()
+        throws Exception
+    {
+        // DO WORK
+        sender.requestBroadcast();
+
+        // VERIFY
+        // grab the sent message
+        Object obj = receiver.waitForMessage();
+
+        assertTrue( "unexpected crap received", obj instanceof UDPDiscoveryMessage );
+
+        UDPDiscoveryMessage msg = (UDPDiscoveryMessage) obj;
+        assertEquals( "wrong message type", BroadcastType.REQUEST, msg.getMessageType() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryServiceUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryServiceUnitTest.java
new file mode 100644
index 0000000..5b18ae0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryServiceUnitTest.java
@@ -0,0 +1,222 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+/** Unit tests for the service. */
+public class UDPDiscoveryServiceUnitTest
+    extends TestCase
+{
+    /** Verify that the list is updated. */
+    public void testAddOrUpdateService_NotInList()
+    {
+        // SETUP
+        String host = "228.5.6.7";
+        int port = 6789;
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setUdpDiscoveryAddr( host );
+        attributes.setUdpDiscoveryPort( port );
+        attributes.setServicePort( 1000 );
+
+        // create the service
+        UDPDiscoveryService service = new UDPDiscoveryService( attributes );
+        service.addParticipatingCacheName( "testCache1" );
+
+        MockDiscoveryListener discoveryListener = new MockDiscoveryListener();
+        service.addDiscoveryListener( discoveryListener );
+
+        DiscoveredService discoveredService = new DiscoveredService();
+        discoveredService.setServiceAddress( host );
+        discoveredService.setCacheNames( new ArrayList<String>() );
+        discoveredService.setServicePort( 1000 );
+        discoveredService.setLastHearFromTime( 100 );
+
+        // DO WORK
+        service.addOrUpdateService( discoveredService );
+
+        // VERIFY
+        assertTrue( "Service should be in the service list.", service.getDiscoveredServices()
+            .contains( discoveredService ) );
+        assertTrue( "Service should be in the listener list.", discoveryListener.discoveredServices
+            .contains( discoveredService ) );
+    }
+
+    /** Verify that the list is updated. */
+    public void testAddOrUpdateService_InList_NamesDoNotChange()
+    {
+        // SETUP
+        String host = "228.5.6.7";
+        int port = 6789;
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setUdpDiscoveryAddr( host );
+        attributes.setUdpDiscoveryPort( port );
+        attributes.setServicePort( 1000 );
+
+        // create the service
+        UDPDiscoveryService service = new UDPDiscoveryService( attributes );
+        service.addParticipatingCacheName( "testCache1" );
+
+        MockDiscoveryListener discoveryListener = new MockDiscoveryListener();
+        service.addDiscoveryListener( discoveryListener );
+
+        ArrayList<String> sametCacheNames = new ArrayList<String>();
+        sametCacheNames.add( "name1" );
+
+        DiscoveredService discoveredService = new DiscoveredService();
+        discoveredService.setServiceAddress( host );
+        discoveredService.setCacheNames( sametCacheNames );
+        discoveredService.setServicePort( 1000 );
+        discoveredService.setLastHearFromTime( 100 );
+
+
+        DiscoveredService discoveredService2 = new DiscoveredService();
+        discoveredService2.setServiceAddress( host );
+        discoveredService2.setCacheNames( sametCacheNames );
+        discoveredService2.setServicePort( 1000 );
+        discoveredService2.setLastHearFromTime( 500 );
+
+        // DO WORK
+        service.addOrUpdateService( discoveredService );
+        // again
+        service.addOrUpdateService( discoveredService2 );
+
+        // VERIFY
+        assertEquals( "Should only be one in the set.", 1, service.getDiscoveredServices().size() );
+        assertTrue( "Service should be in the service list.", service.getDiscoveredServices()
+            .contains( discoveredService ) );
+        assertTrue( "Service should be in the listener list.", discoveryListener.discoveredServices
+            .contains( discoveredService ) );
+
+        // need to update the time this sucks. add has no effect convert to a map
+        for (DiscoveredService service1 : service.getDiscoveredServices())
+        {
+            if ( discoveredService.equals( service1 ) )
+            {
+                assertEquals( "The match should have the new last heard from time.", service1.getLastHearFromTime(),
+                              discoveredService2.getLastHearFromTime() );
+            }
+        }
+        // the mock has a list from all add calls.
+        // it should have been called when the list changed.
+        //assertEquals( "Mock should have been called once.", 1, discoveryListener.discoveredServices.size() );
+        // logic changed.  it's called every time.
+        assertEquals( "Mock should have been called twice.", 2, discoveryListener.discoveredServices.size() );
+    }
+
+    /** Verify that the list is updated. */
+    public void testAddOrUpdateService_InList_NamesChange()
+    {
+        // SETUP
+        String host = "228.5.6.7";
+        int port = 6789;
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setUdpDiscoveryAddr( host );
+        attributes.setUdpDiscoveryPort( port );
+        attributes.setServicePort( 1000 );
+
+        // create the service
+        UDPDiscoveryService service = new UDPDiscoveryService( attributes );
+        service.addParticipatingCacheName( "testCache1" );
+
+        MockDiscoveryListener discoveryListener = new MockDiscoveryListener();
+        service.addDiscoveryListener( discoveryListener );
+
+        DiscoveredService discoveredService = new DiscoveredService();
+        discoveredService.setServiceAddress( host );
+        discoveredService.setCacheNames( new ArrayList<String>() );
+        discoveredService.setServicePort( 1000 );
+        discoveredService.setLastHearFromTime( 100 );
+
+        ArrayList<String> differentCacheNames = new ArrayList<String>();
+        differentCacheNames.add( "name1" );
+        DiscoveredService discoveredService2 = new DiscoveredService();
+        discoveredService2.setServiceAddress( host );
+        discoveredService2.setCacheNames( differentCacheNames );
+        discoveredService2.setServicePort( 1000 );
+        discoveredService2.setLastHearFromTime( 500 );
+
+        // DO WORK
+        service.addOrUpdateService( discoveredService );
+        // again
+        service.addOrUpdateService( discoveredService2 );
+
+        // VERIFY
+        assertEquals( "Should only be one in the set.", 1, service.getDiscoveredServices().size() );
+        assertTrue( "Service should be in the service list.", service.getDiscoveredServices()
+            .contains( discoveredService ) );
+        assertTrue( "Service should be in the listener list.", discoveryListener.discoveredServices
+            .contains( discoveredService ) );
+
+        // need to update the time this sucks. add has no effect convert to a map
+        for (DiscoveredService service1 : service.getDiscoveredServices())
+        {
+            if ( discoveredService.equals( service1 ) )
+            {
+                assertEquals( "The match should have the new last heard from time.", service1.getLastHearFromTime(),
+                              discoveredService2.getLastHearFromTime() );
+                assertEquals( "The names should be updated.", service1.getCacheNames() + "", differentCacheNames + "" );
+            }
+        }
+        // the mock has a list from all add calls.
+        // it should have been called when the list changed.
+        assertEquals( "Mock should have been called twice.", 2, discoveryListener.discoveredServices.size() );
+        assertEquals( "The second mock listener add should be discoveredService2", discoveredService2,
+                      discoveryListener.discoveredServices.get( 1 ) );
+    }
+
+    /** Verify that the list is updated. */
+    public void testRemoveDiscoveredService()
+    {
+        // SETUP
+        String host = "228.5.6.7";
+        int port = 6789;
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setUdpDiscoveryAddr( host );
+        attributes.setUdpDiscoveryPort( port );
+        attributes.setServicePort( 1000 );
+
+        // create the service
+        UDPDiscoveryService service = new UDPDiscoveryService( attributes );
+        service.addParticipatingCacheName( "testCache1" );
+
+        MockDiscoveryListener discoveryListener = new MockDiscoveryListener();
+        service.addDiscoveryListener( discoveryListener );
+
+        DiscoveredService discoveredService = new DiscoveredService();
+        discoveredService.setServiceAddress( host );
+        discoveredService.setCacheNames( new ArrayList<String>() );
+        discoveredService.setServicePort( 1000 );
+        discoveredService.setLastHearFromTime( 100 );
+
+        service.addOrUpdateService( discoveredService );
+
+        // DO WORK
+        service.removeDiscoveredService( discoveredService );
+
+        // VERIFY
+        assertFalse( "Service should not be in the service list.", service.getDiscoveredServices()
+            .contains( discoveredService ) );
+        assertFalse( "Service should not be in the listener list.", discoveryListener.discoveredServices
+            .contains( discoveredService ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryUnitTest.java
new file mode 100644
index 0000000..8ebe583
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/discovery/UDPDiscoveryUnitTest.java
@@ -0,0 +1,90 @@
+package org.apache.commons.jcs.utils.discovery;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.utils.timing.SleepUtil;
+
+import java.util.ArrayList;
+
+/**
+ * Unit tests for discovery
+ */
+public class UDPDiscoveryUnitTest
+    extends TestCase
+{
+    /**
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleUDPDiscovery()
+        throws Exception
+    {
+        UDPDiscoveryAttributes attributes = new UDPDiscoveryAttributes();
+        attributes.setUdpDiscoveryAddr( "228.5.6.7" );
+        attributes.setUdpDiscoveryPort( 6789 );
+        attributes.setServicePort( 1000 );
+
+        // create the service
+        UDPDiscoveryService service = new UDPDiscoveryService( attributes );
+        service.addParticipatingCacheName( "testCache1" );
+
+        MockDiscoveryListener discoveryListener = new MockDiscoveryListener();
+        service.addDiscoveryListener( discoveryListener );
+
+        // create a receiver with the service
+        UDPDiscoveryReceiver receiver = new UDPDiscoveryReceiver( service, attributes.getUdpDiscoveryAddr(), attributes
+            .getUdpDiscoveryPort() );
+        Thread t = new Thread( receiver );
+        t.start();
+
+        // create a sender
+        UDPDiscoverySender sender = new UDPDiscoverySender( attributes.getUdpDiscoveryAddr(), attributes
+            .getUdpDiscoveryPort() );
+
+        // create more names than we have no wait facades for
+        // the only one that gets added should be testCache1
+        ArrayList<String> cacheNames = new ArrayList<String>();
+        int numJunk = 10;
+        for ( int i = 0; i < numJunk; i++ )
+        {
+            cacheNames.add( "junkCacheName" + i );
+        }
+        cacheNames.add( "testCache1" );
+
+        // send max messages
+        int max = 10;
+        int cnt = 0;
+        for ( ; cnt < max; cnt++ )
+        {
+            sender.passiveBroadcast( "localhost", 1111, cacheNames, 1 );
+            SleepUtil.sleepAtLeast( 20 );
+        }
+
+        SleepUtil.sleepAtLeast( 200 );
+
+        // check to see that we got 10 messages
+        //System.out.println( "Receiver count = " + receiver.getCnt() );
+
+        // request braodcasts change things.
+        assertTrue( "Receiver count [" + receiver.getCnt() + "] should be the at least the number sent [" + cnt + "].",
+                    cnt <= receiver.getCnt() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/net/HostNameUtilUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/net/HostNameUtilUnitTest.java
new file mode 100644
index 0000000..4f45704
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/net/HostNameUtilUnitTest.java
@@ -0,0 +1,44 @@
+package org.apache.commons.jcs.utils.net;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.net.UnknownHostException;
+
+/** Tests for the host name util. */
+public class HostNameUtilUnitTest
+    extends TestCase
+{
+    /**
+     * It's nearly impossible to unit test the getLocalHostLANAddress method.
+     * <p>
+     * @throws UnknownHostException
+     */
+    public void testGetLocalHostAddress_Simple() throws UnknownHostException
+    {
+        // DO WORK
+        String result = HostNameUtil.getLocalHostAddress();
+
+        // VERIFY
+        //System.out.print( result );
+        assertNotNull( "Should have a host address.", result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/CompressingSerializerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/CompressingSerializerUnitTest.java
new file mode 100644
index 0000000..bb58233
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/CompressingSerializerUnitTest.java
@@ -0,0 +1,119 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+/**
+ * Tests the compressing serializer.
+ */
+public class CompressingSerializerUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we don't get any erorrs for null input.
+     * <p>
+     * @throws ClassNotFoundException
+     * @throws IOException
+     */
+    public void testDeserialize_NullInput()
+        throws IOException, ClassNotFoundException
+    {
+        // SETUP
+        CompressingSerializer serializer = new CompressingSerializer();
+
+        // DO WORK
+        Object result = serializer.deSerialize( null, null );
+
+        // VERIFY
+        assertNull( "Should have nothing.", result );
+    }
+
+    /**
+     * Test simple back and forth with a string.
+     * <p>
+     * ))<=>((
+     * <p>
+     * @throws Exception on error
+     */
+    public void testSimpleBackAndForth()
+        throws Exception
+    {
+        // SETUP
+        CompressingSerializer serializer = new CompressingSerializer();
+
+        // DO WORK
+        String before = "adsfdsafdsafdsafdsafdsafdsafdsagfdsafdsafdsfdsafdsafsa333 31231";
+        String after = (String) serializer.deSerialize( serializer.serialize( before ), null );
+
+        // VERIFY
+        assertEquals( "Before and after should be the same.", before, after );
+    }
+
+    /**
+     * Test serialization with a null object. Verify that we don't get an error.
+     * <p>
+     * @throws Exception on error
+     */
+    public void testSerialize_NullInput()
+        throws Exception
+    {
+        // SETUP
+        CompressingSerializer serializer = new CompressingSerializer();
+
+        String before = null;
+
+        // DO WORK
+        byte[] serialized = serializer.serialize( before );
+        String after = (String) serializer.deSerialize( serialized, null );
+
+        // VERIFY
+        assertNull( "Should have nothing. after =" + after, after );
+    }
+
+    /**
+     * Verify that the compressed is smaller.
+     * <p>
+     * @throws Exception on error
+     */
+    public void testSerialize_CompareCompressedAndUncompressed()
+        throws Exception
+    {
+        // SETUP
+        CompressingSerializer serializer = new CompressingSerializer();
+
+        // I hate for loops.
+        String before = "adsfdsafdsafdsafdsafdsafdsafdsagfdsafdsafdssaf dsaf sadf dsaf dsaf dsaf "
+            + "dsafdsa fdsaf dsaf dsafdsa dsaf dsaf dsaf dsaf dsafdsa76f dsa798f dsa6fdsa 087f  "
+            + "gh 987dsahb dsahbuhbfnui nufdsa hbv87 f8vhdsgbnfv h8fdg8dfjvn8fdwgj fdsgjb9fdsjbv"
+            + "jvhjv hg98f-dsaghj j9fdsb gfsb 9fdshjbgb987fdsbfdwgh ujbhjbhb hbfdsgh fdshb "
+            + "Ofdsgyfesgyfdsafdsafsa333 31231";
+
+        // DO WORK
+        byte[] compressed = serializer.serialize( before );
+        byte[] nonCompressed = serializer.serializeObject( before );
+
+        // VERIFY
+        assertTrue( "Compressed should be smaller. compressed size = " + compressed.length + "nonCompressed size = "
+            + nonCompressed.length, compressed.length < nonCompressed.length );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtilUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtilUnitTest.java
new file mode 100644
index 0000000..df5cb2d
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/SerializationConversionUtilUnitTest.java
@@ -0,0 +1,195 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+
+import java.io.IOException;
+
+/**
+ * Tests the serialization conversion util.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class SerializationConversionUtilUnitTest
+    extends TestCase
+{
+    /**
+     * Verify null for null.
+     * <p>
+     * @throws IOException
+     */
+    public void testgGetSerializedCacheElement_null()
+        throws IOException
+    {
+        // SETUP
+        IElementSerializer elementSerializer = new StandardSerializer();
+        ICacheElement<String, String> before = null;
+
+        // DO WORK
+        ICacheElementSerialized<String, String> result =
+            SerializationConversionUtil.getSerializedCacheElement( before, elementSerializer );
+
+        // VERIFY
+        assertNull( "Should get null for null", result );
+    }
+
+    /**
+     * Verify null for null.
+     * <p>
+     * @throws Exception
+     */
+    public void testgGetDeSerializedCacheElement_null()
+        throws Exception
+    {
+        // SETUP
+        IElementSerializer elementSerializer = new StandardSerializer();
+        ICacheElementSerialized<String, String> before = null;
+
+        // DO WORK
+        ICacheElement<String, String> result =
+            SerializationConversionUtil.getDeSerializedCacheElement( before, elementSerializer );
+
+        // VERIFY
+        assertNull( "Should get null for null", result );
+    }
+
+    /**
+     * Verify that we can go back and forth with the simplest of objects.
+     * <p>
+     * @throws Exception
+     */
+    public void testSimpleConversion()
+        throws Exception
+    {
+        // SETUP
+        String cacheName = "testName";
+        String key = "key";
+        String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
+
+        IElementSerializer elementSerializer = new StandardSerializer();
+
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(34);
+
+        ICacheElement<String, String> before = new CacheElement<String, String>( cacheName, key, value );
+        before.setElementAttributes( attr );
+
+        // DO WORK
+        ICacheElementSerialized<String, String> serialized =
+            SerializationConversionUtil.getSerializedCacheElement( before, elementSerializer );
+
+        // VERIFY
+        assertNotNull( "Should have a serialized object.", serialized );
+
+        // DO WORK
+        ICacheElement<String, String> after =
+            SerializationConversionUtil.getDeSerializedCacheElement( serialized, elementSerializer );
+
+        // VERIFY
+        assertNotNull( "Should have a deserialized object.", after );
+        assertEquals( "Values should be the same.", before.getVal(), after.getVal() );
+        assertEquals( "Attributes should be the same.", before.getElementAttributes().getMaxLife(), after
+            .getElementAttributes().getMaxLife() );
+        assertEquals( "Keys should be the same.", before.getKey(), after.getKey() );
+        assertEquals( "Cache name should be the same.", before.getCacheName(), after.getCacheName() );
+    }
+
+    /**
+     * Verify that we can go back and forth with the simplest of objects.
+     *<p>
+     * @throws Exception
+     */
+    public void testAccidentalDoubleConversion()
+        throws Exception
+    {
+        // SETUP
+        String cacheName = "testName";
+        String key = "key";
+        String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
+
+        IElementSerializer elementSerializer = new StandardSerializer();
+
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(34);
+
+        ICacheElement<String, String> before = new CacheElement<String, String>( cacheName, key, value );
+        before.setElementAttributes( attr );
+
+        // DO WORK
+        ICacheElementSerialized<String, String> alreadySerialized =
+            SerializationConversionUtil.getSerializedCacheElement( before, elementSerializer );
+        ICacheElementSerialized<String, String> serialized =
+            SerializationConversionUtil.getSerializedCacheElement( alreadySerialized, elementSerializer );
+
+        // VERIFY
+        assertNotNull( "Should have a serialized object.", serialized );
+
+        // DO WORK
+        ICacheElement<String, String> after =
+            SerializationConversionUtil.getDeSerializedCacheElement( serialized, elementSerializer );
+
+        // VERIFY
+        assertNotNull( "Should have a deserialized object.", after );
+        assertEquals( "Values should be the same.", before.getVal(), after.getVal() );
+        assertEquals( "Attributes should be the same.", before.getElementAttributes().getMaxLife(), after
+            .getElementAttributes().getMaxLife() );
+        assertEquals( "Keys should be the same.", before.getKey(), after.getKey() );
+        assertEquals( "Cache name should be the same.", before.getCacheName(), after.getCacheName() );
+    }
+
+    /**
+     * Verify that we get an IOException for a null serializer.
+     */
+    public void testNullSerializerConversion()
+    {
+        // SETUP
+        String cacheName = "testName";
+        String key = "key";
+        String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
+
+        IElementSerializer elementSerializer = null;// new StandardSerializer();
+
+        IElementAttributes attr = new ElementAttributes();
+        attr.setMaxLife(34);
+
+        ICacheElement<String, String> before = new CacheElement<String, String>( cacheName, key, value );
+        before.setElementAttributes( attr );
+
+        // DO WORK
+        try
+        {
+            SerializationConversionUtil.getSerializedCacheElement( before, elementSerializer );
+
+            // VERIFY
+            fail( "We should have received an IOException." );
+        }
+        catch ( IOException e )
+        {
+            // expected
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/StandardSerializerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/StandardSerializerUnitTest.java
new file mode 100644
index 0000000..0411fec
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/serialization/StandardSerializerUnitTest.java
@@ -0,0 +1,102 @@
+package org.apache.commons.jcs.utils.serialization;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the standard serializer.
+ *<p>
+ * @author Aaron Smuts
+ */
+public class StandardSerializerUnitTest
+    extends TestCase
+{
+    /**
+     * Test simple back and forth with a string.
+     *<p>
+     * @throws Exception
+     */
+    public void testSimpleBackAndForth()
+        throws Exception
+    {
+        // SETUP
+        StandardSerializer serializer = new StandardSerializer();
+
+        String before = "adsfdsafdsafdsafdsafdsafdsafdsagfdsafdsafdsfdsafdsafsa333 31231";
+
+        // DO WORK
+        String after = (String) serializer.deSerialize( serializer.serialize( before ), null );
+
+        // VERIFY
+        assertEquals( "Before and after should be the same.", before, after );
+    }
+
+    /**
+     * Test serialization with a null object. Verify that we don't get an error.
+     *<p>
+     * @throws Exception
+     */
+    public void testNullInput()
+        throws Exception
+    {
+        // SETUP
+        StandardSerializer serializer = new StandardSerializer();
+
+        String before = null;
+
+        // DO WORK
+        byte[] serialized = serializer.serialize( before );
+        //System.out.println( "testNullInput " + serialized );
+
+        String after = (String) serializer.deSerialize( serialized, null );
+        //System.out.println( "testNullInput " + after );
+
+        // VERIFY
+        assertNull( "Should have nothing.", after );
+    }
+
+    /**
+     * Test simple back and forth with a string.
+     *<p>
+     * @throws Exception
+     */
+    public void testBigStringBackAndForth()
+        throws Exception
+    {
+        // SETUP
+        StandardSerializer serializer = new StandardSerializer();
+
+        String string = "This is my big string ABCDEFGH";
+        StringBuilder sb = new StringBuilder();
+        sb.append( string );
+        for ( int i = 0; i < 4; i++ )
+        {
+            sb.append( " " + i + sb.toString() ); // big string
+        }
+        String before = sb.toString();
+
+        // DO WORK
+        String after = (String) serializer.deSerialize( serializer.serialize( before ), null );
+
+        // VERIFY
+        assertEquals( "Before and after should be the same.", before, after );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/BoundedQueueUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/BoundedQueueUnitTest.java
new file mode 100644
index 0000000..c8fcff0
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/BoundedQueueUnitTest.java
@@ -0,0 +1,93 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for the bounded queue.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BoundedQueueUnitTest
+    extends TestCase
+{
+    /**
+     * Verify null returned for empty.
+     */
+    public void testTakeLastEmpty()
+    {
+        // SETUP
+        int maxSize = 10;
+        BoundedQueue<Object> queue = new BoundedQueue<Object>( maxSize );
+
+        // DO WORK
+        Object result = queue.take();
+
+        // VERIFY
+        assertNull( "Result should be null", result );
+    }
+
+    /**
+     * Verify that the queue returns the number of elements and the it does not exceed the max.
+     */
+    public void testSize()
+    {
+        // SETUP
+        int maxSize = 10;
+        BoundedQueue<String> queue = new BoundedQueue<String>( maxSize );
+
+        // DO WORK
+        for ( int i = 0; i < maxSize * 2; i++ )
+        {
+            queue.add( "adfadsf sad " + i );
+        }
+
+        int result = queue.size();
+
+        // VERIFY
+        assertEquals( "Result size not as expected", maxSize, result );
+    }
+
+    /**
+     * Verify that the items come back in the order put in.
+     */
+    public void testFIFOOrderedTake()
+    {
+        // SETUP
+        int maxSize = 10;
+        BoundedQueue<String> queue = new BoundedQueue<String>( maxSize );
+
+        // DO WORK
+        for ( int i = 0; i < maxSize; i++ )
+        {
+            queue.add( String.valueOf( i ) );
+        }
+
+
+        // VERIFY
+
+        for ( int i = 0; i < maxSize; i++ )
+        {
+            String result = queue.take();
+            assertEquals( "Result not as expected",  String.valueOf( i ) ,  result  );
+        }
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListUnitTest.java
new file mode 100644
index 0000000..46bad53
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/DoubleLinkedListUnitTest.java
@@ -0,0 +1,189 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.TestLogConfigurationUtil;
+
+import java.io.StringWriter;
+
+/** Unit tests for the double linked list. */
+public class DoubleLinkedListUnitTest
+    extends TestCase
+{
+    /** verify that the last is added when the list is empty. */
+    public void testAddLast_Empty()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        // WO WORK
+        list.addLast( node1 );
+
+        // VERIFY
+        assertEquals( "Wrong last", node1, list.getLast() );
+    }
+
+    /** verify that the last is added when the list is empty. */
+    public void testAddLast_NotEmpty()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        String payload2 = "payload2";
+        DoubleLinkedListNode<String> node2 = new DoubleLinkedListNode<String>( payload2 );
+
+        // WO WORK
+        list.addLast( node1 );
+        list.addLast( node2 );
+
+        // VERIFY
+        assertEquals( "Wrong last", node2, list.getLast() );
+    }
+
+    /** verify that it's added last. */
+    public void testMakeLast_wasFirst()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        String payload2 = "payload2";
+        DoubleLinkedListNode<String> node2 = new DoubleLinkedListNode<String>( payload2 );
+
+        list.addFirst( node2 );
+        list.addFirst(  node1 );
+
+        // DO WORK
+        list.makeLast( node1 );
+
+        // VERIFY
+        assertEquals( "Wrong size", 2, list.size() );
+        assertEquals( "Wrong last", node1, list.getLast() );
+        assertEquals( "Wrong first", node2, list.getFirst() );
+    }
+
+    /** verify that it's added last. */
+    public void testMakeLast_wasLast()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        String payload2 = "payload2";
+        DoubleLinkedListNode<String> node2 = new DoubleLinkedListNode<String>( payload2 );
+
+        list.addFirst( node1 );
+        list.addFirst(  node2 );
+
+        // DO WORK
+        list.makeLast( node1 );
+
+        // VERIFY
+        assertEquals( "Wrong size", 2, list.size() );
+        assertEquals( "Wrong last", node1, list.getLast() );
+        assertEquals( "Wrong first", node2, list.getFirst() );
+    }
+
+    /** verify that it's added last. */
+    public void testMakeLast_wasAlone()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        list.addFirst( node1 );
+
+        // DO WORK
+        list.makeLast( node1 );
+
+        // VERIFY
+        assertEquals( "Wrong size", 1, list.size() );
+        assertEquals( "Wrong last", node1, list.getLast() );
+        assertEquals( "Wrong first", node1, list.getFirst() );
+    }
+
+    /** verify that it's added last. */
+    public void testMakeLast_wasInMiddle()
+    {
+        // SETUP
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        String payload2 = "payload2";
+        DoubleLinkedListNode<String> node2 = new DoubleLinkedListNode<String>( payload2 );
+
+        String payload3 = "payload3";
+        DoubleLinkedListNode<String> node3 = new DoubleLinkedListNode<String>( payload3 );
+
+        list.addFirst( node2 );
+        list.addFirst(  node1 );
+        list.addFirst(  node3 );
+
+        // DO WORK
+        list.makeLast( node1 );
+
+        // VERIFY
+        assertEquals( "Wrong size", 3, list.size() );
+        assertEquals( "Wrong last", node1, list.getLast() );
+        assertEquals( "Wrong first", node3, list.getFirst() );
+    }
+
+    /** verify that the entries are dumped. */
+    public void testDumpEntries_DebugTrue()
+    {
+        // SETUP
+        StringWriter stringWriter = new StringWriter();
+        TestLogConfigurationUtil.configureLogger( stringWriter, DoubleLinkedList.class.getName() );
+
+        DoubleLinkedList<DoubleLinkedListNode<String>> list = new DoubleLinkedList<DoubleLinkedListNode<String>>();
+
+        String payload1 = "payload1";
+        DoubleLinkedListNode<String> node1 = new DoubleLinkedListNode<String>( payload1 );
+
+        String payload2 = "payload2";
+        DoubleLinkedListNode<String> node2 = new DoubleLinkedListNode<String>( payload2 );
+
+        list.addLast( node1 );
+        list.addLast( node2 );
+        list.debugDumpEntries();
+
+        // WO WORK
+        String result = stringWriter.toString();
+
+        // VERIFY
+        assertTrue( "Missing node in log dump", result.indexOf( payload1 ) != -1 );
+        assertTrue( "Missing node in log dump", result.indexOf( payload2 ) != -1 );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/JCSvsCommonsLRUMapPerformanceTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/JCSvsCommonsLRUMapPerformanceTest.java
new file mode 100644
index 0000000..7db83fc
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/JCSvsCommonsLRUMapPerformanceTest.java
@@ -0,0 +1,214 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.commons.jcs.JCSvsHashtablePerformanceTest;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Map;
+
+/**
+ * This ensures that the jcs version of the LRU map is as fast as the commons
+ * version. It has been testing at .6 to .7 times the commons LRU.
+ *
+ */
+public class JCSvsCommonsLRUMapPerformanceTest
+    extends TestCase
+{
+    /** jcs / commons */
+    float ratioPut = 0;
+
+    /** jcs / commons */
+    float ratioGet = 0;
+
+    /** goal */
+    float target = 1.0f;
+
+    /** loops */
+    int loops = 20;
+
+    /** number to test with */
+    int tries = 50000;
+
+    /**
+     * @param testName
+     */
+    public JCSvsCommonsLRUMapPerformanceTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     *
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        return new TestSuite( JCSvsCommonsLRUMapPerformanceTest.class );
+    }
+
+    /**
+     * A unit test for JUnit
+     *
+     * @throws Exception
+     *                Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        Log log = LogFactory.getLog( LRUMap.class );
+        if ( log.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+
+        doWork();
+        assertTrue( this.ratioPut < target );
+        assertTrue( this.ratioGet < target );
+    }
+
+    /**
+     *
+     */
+    public void doWork()
+    {
+
+        long start = 0;
+        long end = 0;
+        long time = 0;
+        float tPer = 0;
+
+        long putTotalJCS = 0;
+        long getTotalJCS = 0;
+        long putTotalHashtable = 0;
+        long getTotalHashtable = 0;
+
+        String name = "LRUMap";
+        String cache2Name = "";
+
+        try
+        {
+
+            Map<String, String> cache = new LRUMap<String, String>( tries );
+
+            for ( int j = 0; j < loops; j++ )
+            {
+
+                name = "JCS      ";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                // /////////////////////////////////////////////////////////////
+                cache2Name = "Commons  ";
+                // or LRUMapJCS
+                Map<String, String> cache2 = new org.apache.commons.collections4.map.LRUMap<String, String>( tries );
+                // cache2Name = "Hashtable";
+                // Hashtable cache2 = new Hashtable();
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( cache2Name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( cache2Name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                System.out.println( "\n" );
+            }
+
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace( System.out );
+            System.out.println( e );
+        }
+
+        long putAvJCS = putTotalJCS / loops;
+        long getAvJCS = getTotalJCS / loops;
+        long putAvHashtable = putTotalHashtable / loops;
+        long getAvHashtable = getTotalHashtable / loops;
+
+        System.out.println( "Finished " + loops + " loops of " + tries + " gets and puts" );
+
+        System.out.println( "\n" );
+        System.out.println( "Put average for LRUMap       = " + putAvJCS );
+        System.out.println( "Put average for " + cache2Name + " = " + putAvHashtable );
+        ratioPut = Float.intBitsToFloat( (int) putAvJCS ) / Float.intBitsToFloat( (int) putAvHashtable );
+        System.out.println( name + " puts took " + ratioPut + " times the " + cache2Name + ", the goal is <" + target
+            + "x" );
+
+        System.out.println( "\n" );
+        System.out.println( "Get average for LRUMap       = " + getAvJCS );
+        System.out.println( "Get average for " + cache2Name + " = " + getAvHashtable );
+        ratioGet = Float.intBitsToFloat( (int) getAvJCS ) / Float.intBitsToFloat( (int) getAvHashtable );
+        System.out.println( name + " gets took " + ratioGet + " times the " + cache2Name + ", the goal is <" + target
+            + "x" );
+
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        JCSvsHashtablePerformanceTest test = new JCSvsHashtablePerformanceTest( "command" );
+        test.doWork();
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentTest.java
new file mode 100644
index 0000000..a0c9947
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentTest.java
@@ -0,0 +1,257 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.Iterator;
+
+/**
+ * Tests the LRUMap
+ */
+public class LRUMapConcurrentTest
+    extends TestCase
+{
+    /** the number of items to use in a test */
+    private static int items = 20000;
+
+    /**
+     * Constructor for the TestSimpleLoad object
+     * @param testName Description of the Parameter
+     */
+    public LRUMapConcurrentTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        // run the basic tests
+        TestSuite suite = new TestSuite( LRUMapConcurrentTest.class );
+
+        // run concurrent tests
+        final LRUMap<String, String> map = new LRUMap<String, String>( 2000 );
+        suite.addTest( new LRUMapConcurrentTest( "conc1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentTest( "conc2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentTest( "conc3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+
+        // run more concurrent tests
+        final int max2 = 20000;
+        final LRUMap<String, String> map2 = new LRUMap<String, String>( max2 );
+        suite.addTest( new LRUMapConcurrentTest( "concB1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentRangeTests( map2, 10000, max2 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentTest( "concB1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentRangeTests( map2, 0, 9999 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Just test that we can put, get and remove as expected.
+     * @throws Exception Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        LRUMap<String, String> map = new LRUMap<String, String>( items );
+
+        for ( int i = 0; i < items; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        // test removal
+        map.remove( "300:key" );
+        assertNull( map.get( "300:key" ) );
+
+    }
+
+    /**
+     * Just make sure that the LRU functions int he most simple case.
+     * @throws Exception Description of the Exception
+     */
+    public void testLRURemoval()
+        throws Exception
+    {
+        int total = 10;
+        LRUMap<String, String> map = new LRUMap<String, String>( total );
+        map.setChunkSize( 1 );
+
+        // put the max in
+        for ( int i = 0; i < total; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        Iterator<?> it = map.entrySet().iterator();
+        while ( it.hasNext() )
+        {
+            assertNotNull( it.next() );
+        }
+//        System.out.println( map.getStatistics() );
+
+        // get the max out backwards
+        for ( int i = total - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+//        System.out.println( map.getStatistics() );
+
+        //since we got them backwards the total should be at the end.
+        // add one confirm that total is gone.
+        map.put( ( total ) + ":key", "data" + ( total ) );
+        assertNull( map.get( ( total - 1 ) + ":key" ) );
+
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testLRURemovalAgain()
+        throws Exception
+    {
+        int total = 10000;
+        LRUMap<String, String> map = new LRUMap<String, String>( total );
+        map.setChunkSize( 1 );
+
+        // put the max in
+        for ( int i = 0; i < total * 2; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        // get the total number, these should be null
+        for ( int i = total - 1; i >= 0; i-- )
+        {
+            assertNull( map.get( i + ":key" ) );
+
+        }
+
+        // get the total to total *2 items out, these should be foufn.
+        for ( int i = ( total * 2 ) - 1; i >= total; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+//        System.out.println( map.getStatistics() );
+
+    }
+
+    /**
+     * Just make sure that we can put and get concurrently
+     * @param map
+     * @param items
+     * @throws Exception
+     */
+    public void runConcurrentPutGetTests( LRUMap<String, String> map, int items )
+        throws Exception
+    {
+        for ( int i = 0; i < items; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+    }
+
+    /**
+     * Put, get, and remove from a range. This should occur at a range that is not touched by other
+     * tests.
+     * @param map
+     * @param start
+     * @param end
+     * @throws Exception
+     */
+    public void runConcurrentRangeTests( LRUMap<String, String> map, int start, int end )
+        throws Exception
+    {
+        for ( int i = start; i < end; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = end - 1; i >= start; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        // test removal
+        map.remove( start + ":key" );
+        assertNull( map.get( start + ":key" ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentUnitTest.java
new file mode 100644
index 0000000..09b6005
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapConcurrentUnitTest.java
@@ -0,0 +1,267 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.Iterator;
+
+/**
+ * Tests the LRUMap
+ *
+ */
+public class LRUMapConcurrentUnitTest
+    extends TestCase
+{
+    /** number to test with */
+    private static int items = 20000;
+
+    /**
+     * Constructor for the TestSimpleLoad object
+     * <p>
+     * @param testName
+     *            Description of the Parameter
+     */
+    public LRUMapConcurrentUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        // run the basic tests
+        TestSuite suite = new TestSuite( LRUMapConcurrentUnitTest.class );
+
+        // run concurrent tests
+        final LRUMap<String, String> map = new LRUMap<String, String>( 2000 );
+        suite.addTest( new LRUMapConcurrentUnitTest( "conc1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentUnitTest( "conc2" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentUnitTest( "conc3" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentPutGetTests( map, 2000 );
+            }
+        } );
+
+        // run more concurrent tests
+        final int max2 = 20000;
+        final LRUMap<String, String> map2 = new LRUMap<String, String>( max2 );
+        suite.addTest( new LRUMapConcurrentUnitTest( "concB1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentRangeTests( map2, 10000, max2 );
+            }
+        } );
+        suite.addTest( new LRUMapConcurrentUnitTest( "concB1" )
+        {
+            @Override
+            public void runTest()
+                throws Exception
+            {
+                this.runConcurrentRangeTests( map2, 0, 9999 );
+            }
+        } );
+
+        return suite;
+    }
+
+    /**
+     * Just test that we can put, get and remove as expected.
+     * <p>
+     * @throws Exception
+     *                Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        LRUMap<String, String> map = new LRUMap<String, String>( items );
+
+        for ( int i = 0; i < items; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        // test removal
+        map.remove( "300:key" );
+        assertNull( map.get( "300:key" ) );
+
+    }
+
+    /**
+     * Just make sure that the LRU functions in he most simple case.
+     *
+     * @throws Exception
+     *                Description of the Exception
+     */
+    public void testLRURemoval()
+        throws Exception
+    {
+        int total = 10;
+        LRUMap<String, String> map = new LRUMap<String, String>( total );
+        map.setChunkSize( 1 );
+
+        // put the max in
+        for ( int i = 0; i < total; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        Iterator<?> it = map.entrySet().iterator();
+        while ( it.hasNext() )
+        {
+            assertNotNull( it.next() );
+        }
+//        System.out.println( map.getStatistics() );
+
+        // get the max out backwards
+        for ( int i = total - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+//        System.out.println( map.getStatistics() );
+
+        //since we got them backwards the total should be at the end.
+        // add one confirm that total is gone.
+        map.put( ( total ) + ":key", "data" + ( total ) );
+        assertNull( map.get( ( total - 1 ) + ":key" ) );
+
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testLRURemovalAgain()
+        throws Exception
+    {
+        int total = 10000;
+        LRUMap<String, String> map = new LRUMap<String, String>( total );
+        map.setChunkSize( 1 );
+
+        // put the max in
+        for ( int i = 0; i < total * 2; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        // get the total number, these should be null
+        for ( int i = total - 1; i >= 0; i-- )
+        {
+            assertNull( map.get( i + ":key" ) );
+
+        }
+
+        // get the total to total *2 items out, these should be found.
+        for ( int i = ( total * 2 ) - 1; i >= total; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+//        System.out.println( map.getStatistics() );
+
+    }
+
+    /**
+     * Just make sure that we can put and get concurrently
+     *
+     * @param map
+     * @param items
+     * @throws Exception
+     */
+    public void runConcurrentPutGetTests( LRUMap<String, String> map, int items )
+        throws Exception
+    {
+        for ( int i = 0; i < items; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = items - 1; i >= 0; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+    }
+
+    /**
+     * Put, get, and remove from a range. This should occur at a range that is
+     * not touched by other tests.
+     * <p>
+     * @param map
+     * @param start
+     * @param end
+     * @throws Exception
+     */
+    public void runConcurrentRangeTests( LRUMap<String, String> map, int start, int end )
+        throws Exception
+    {
+        for ( int i = start; i < end; i++ )
+        {
+            map.put( i + ":key", "data" + i );
+        }
+
+        for ( int i = end - 1; i >= start; i-- )
+        {
+            String res = map.get( i + ":key" );
+            assertNotNull( "[" + i + ":key] should not be null", res );
+        }
+
+        // test removal
+        map.remove( start + ":key" );
+        assertNull( map.get( start + ":key" ) );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapPerformanceTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapPerformanceTest.java
new file mode 100644
index 0000000..47cd760
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapPerformanceTest.java
@@ -0,0 +1,204 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.jcs.JCSvsHashtablePerformanceTest;
+
+import java.util.Map;
+
+/**
+ * This ensures that the jcs version of the LRU map is as fast as the commons
+ * version. It has been testing at .6 to .7 times the commons LRU.
+ * <p>
+ * @author aaronsm
+ *
+ */
+public class LRUMapPerformanceTest
+    extends TestCase
+{
+    /** The put put ration after the test */
+    float ratioPut = 0;
+
+    /** The ratio after the test */
+    float ratioGet = 0;
+
+    /** put jcs / commons ratio */
+    float targetPut = 1.2f;
+
+    /** get jcs / commons ratio */
+    float targetGet = .5f;
+
+    /** Time to loop */
+    int loops = 20;
+
+    /** items to put and get per loop */
+    int tries = 50000;
+
+    /**
+     * @param testName
+     */
+    public LRUMapPerformanceTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * A unit test suite for JUnit
+     * <p>
+     * @return The test suite
+     */
+    public static Test suite()
+    {
+        return new TestSuite( LRUMapPerformanceTest.class );
+    }
+
+    /**
+     * A unit test for JUnit
+     *
+     * @throws Exception
+     *                Description of the Exception
+     */
+    public void testSimpleLoad()
+        throws Exception
+    {
+        doWork();
+        assertTrue( this.ratioPut < targetPut );
+        assertTrue( this.ratioGet < targetGet );
+    }
+
+    /**
+     *
+     */
+    public void doWork()
+    {
+        long start = 0;
+        long end = 0;
+        long time = 0;
+        float tPer = 0;
+
+        long putTotalJCS = 0;
+        long getTotalJCS = 0;
+        long putTotalHashtable = 0;
+        long getTotalHashtable = 0;
+
+        String name = "LRUMap";
+        String cache2Name = "";
+
+        try
+        {
+            Map<String, String> cache = new LRUMap<String, String>( tries );
+
+            for ( int j = 0; j < loops; j++ )
+            {
+                name = "JCS      ";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                ///////////////////////////////////////////////////////////////
+                cache2Name = "LRUMapJCS (commons)";
+                //or LRUMapJCS
+                Map<String, String> cache2 = new org.apache.commons.collections4.map.LRUMap<String, String>( tries );
+                //cache2Name = "Hashtable";
+                //Hashtable cache2 = new Hashtable();
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( cache2Name + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    cache2.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalHashtable += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( cache2Name + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                System.out.println( "\n" );
+            }
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace( System.out );
+            System.out.println( e );
+        }
+
+        long putAvJCS = putTotalJCS / loops;
+        long getAvJCS = getTotalJCS / loops;
+        long putAvHashtable = putTotalHashtable / loops;
+        long getAvHashtable = getTotalHashtable / loops;
+
+        System.out.println( "Finished " + loops + " loops of " + tries + " gets and puts" );
+
+        System.out.println( "\n" );
+        System.out.println( "Put average for LRUMap       = " + putAvJCS );
+        System.out.println( "Put average for " + cache2Name + " = " + putAvHashtable );
+        ratioPut = Float.intBitsToFloat( (int) putAvJCS ) / Float.intBitsToFloat( (int) putAvHashtable );
+        System.out.println( name + " puts took " + ratioPut + " times the " + cache2Name + ", the goal is <" + targetPut
+            + "x" );
+
+        System.out.println( "\n" );
+        System.out.println( "Get average for LRUMap       = " + getAvJCS );
+        System.out.println( "Get average for " + cache2Name + " = " + getAvHashtable );
+        ratioGet = Float.intBitsToFloat( (int) getAvJCS ) / Float.intBitsToFloat( (int) getAvHashtable );
+        System.out.println( name + " gets took " + ratioGet + " times the " + cache2Name + ", the goal is <" + targetGet
+            + "x" );
+    }
+
+    /**
+     * @param args
+     */
+    public static void main( String args[] )
+    {
+        JCSvsHashtablePerformanceTest test = new JCSvsHashtablePerformanceTest( "command" );
+        test.doWork();
+    }
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapUnitTest.java
new file mode 100644
index 0000000..49bd1b2
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/LRUMapUnitTest.java
@@ -0,0 +1,133 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Basic unit tests for the LRUMap
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class LRUMapUnitTest
+    extends TestCase
+{
+
+    /**
+     * Put up to the size limit and then make sure they are all there.
+     *
+     */
+    public void testPutWithSizeLimit()
+    {
+        int size = 10;
+        Map<String, String> cache = new LRUMap<String, String>( size );
+
+        for ( int i = 0; i < size; i++ )
+        {
+            cache.put( "key:" + i, "data:" + i );
+        }
+
+        for ( int i = 0; i < size; i++ )
+        {
+            String data = cache.get( "key:" + i );
+            assertEquals( "Data is wrong.", "data:" + i, data );
+        }
+    }
+
+    /**
+     * Put into the lru with no limit and then make sure they are all there.
+     *
+     */
+    public void testPutWithNoSizeLimit()
+    {
+        int size = 10;
+        Map<String, String> cache = new LRUMap<String, String>( );
+
+        for ( int i = 0; i < size; i++ )
+        {
+            cache.put( "key:" + i, "data:" + i );
+        }
+
+        for ( int i = 0; i < size; i++ )
+        {
+            String data = cache.get( "key:" + i );
+            assertEquals( "Data is wrong.", "data:" + i, data );
+        }
+    }
+
+    /**
+     * Put and then remove.  Make sure the element is returned.
+     *
+     */
+    public void testPutAndRemove()
+    {
+        int size = 10;
+        Map<String, String> cache = new LRUMap<String, String>( size );
+
+        cache.put( "key:" + 1, "data:" + 1 );
+        String data = cache.remove( "key:" + 1 );
+        assertEquals( "Data is wrong.", "data:" + 1, data );
+    }
+
+    /**
+     * Call remove on an empty map
+     *
+     */
+    public void testRemoveEmpty()
+    {
+        int size = 10;
+        Map<String, String> cache = new LRUMap<String, String>( size );
+
+        Object returned = cache.remove( "key:" + 1 );
+        assertNull( "Shouldn't hvae anything.", returned );
+    }
+
+
+    /**
+     * Add items to the map and then test to see that they come back in the entry set.
+     *
+     */
+    public void testGetEntrySet()
+    {
+        int size = 10;
+        Map<String, String> cache = new LRUMap<String, String>( size );
+
+        for ( int i = 0; i < size; i++ )
+        {
+            cache.put( "key:" + i, "data:" + i );
+        }
+
+        Set<Entry<String, String>> entries = cache.entrySet();
+        assertEquals( "Set contains the wrong number of items.", size, entries.size() );
+
+        // check minimal correctness
+        for (Entry<String, String> data : entries)
+        {
+            assertTrue( "Data is wrong.", data.getValue().toString().indexOf( "data:") != -1  );
+        }
+    }
+
+
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SingleLinkedListUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SingleLinkedListUnitTest.java
new file mode 100644
index 0000000..e18bf16
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SingleLinkedListUnitTest.java
@@ -0,0 +1,104 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the simple linked list.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class SingleLinkedListUnitTest
+    extends TestCase
+{
+    /**
+     * Verify that we get a null and that there are no exceptions.
+     */
+    public void testTakeFromEmptyList()
+    {
+        // SETUP
+        SingleLinkedList<Object> list = new SingleLinkedList<Object>();
+
+        // DO WORK
+        Object result = list.takeFirst();
+
+        // VERIFY
+        assertNull( "Shouldn't have anything.", result );
+    }
+
+    /**
+     * Verify FIFO behavior. Verifies that all items are removed.
+     */
+    public void testAddABunchAndTakeFromList()
+    {
+        // SETUP
+        SingleLinkedList<Integer> list = new SingleLinkedList<Integer>();
+
+        // DO WORK
+        int numToPut = 100;
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            list.addLast( Integer.valueOf( i ) );
+        }
+
+        // VERIFY
+        assertEquals( "Wrong number in list.", numToPut, list.size() );
+
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            Integer result = list.takeFirst();
+            assertEquals( "Wrong value returned.", Integer.valueOf( i ), result );
+        }
+
+        // DO WORK
+        Integer result = list.takeFirst();
+
+        // VERIFY
+        assertNull( "Shouldn't have anything left.", result );
+    }
+
+    /**
+     * Verify that after calling clear all items are removed adn the size is 0.
+     */
+    public void testAddABunchAndClear()
+    {
+        // SETUP
+        SingleLinkedList<Integer> list = new SingleLinkedList<Integer>();
+
+        // DO WORK
+        int numToPut = 100;
+        for ( int i = 0; i < numToPut; i++ )
+        {
+            list.addLast( Integer.valueOf( i ) );
+        }
+
+        // VERIFY
+        assertEquals( "Wrong number in list.", numToPut, list.size() );
+
+        // DO WORK
+        list.clear();
+        Integer result = list.takeFirst();
+
+        // VERIFY
+        assertEquals( "Wrong number in list.", 0, list.size() );
+        assertNull( "Shouldn't have anything left.", result );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SortedPrefArrayUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SortedPrefArrayUnitTest.java
new file mode 100644
index 0000000..3dc32c4
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/struct/SortedPrefArrayUnitTest.java
@@ -0,0 +1,454 @@
+package org.apache.commons.jcs.utils.struct;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the SortedPrefArray used by the recycle bin.
+ * @author aaronsm
+ */
+public class SortedPrefArrayUnitTest
+    extends TestCase
+{
+
+    /**
+     * Constructor for the TestSimpleLoad object
+     * @param testName Description of the Parameter
+     */
+    public SortedPrefArrayUnitTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @throws Exception
+     */
+    public void testLargePref()
+        throws Exception
+    {
+        int maxSize = 25;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = {
+            "10",
+            "11",
+            "01",
+            "02",
+            "03",
+            "04",
+            "05",
+            "08",
+            "07",
+            "06",
+            "09",
+            "12",
+            "13",
+            "15",
+            "14",
+            "20",
+            "25",
+            "29",
+            "28",
+            "16",
+            "17",
+            "96",
+            "00",
+            "72",
+            "39",
+            "55",
+            "44",
+            "26",
+            "22",
+            "59",
+            "38",
+            "16",
+            "27" };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        assertEquals( "Size was not as expected.", maxSize, array.size() );
+
+        // this is a fragile test, since it relies on a hardcoded array
+        String smallest = array.getSmallest();
+        assertEquals( "smallest should be 08", "08", smallest );
+
+        String largest = array.getLargest();
+        assertEquals( "Largest should be 96", "96", largest );
+
+        // this should take 96;
+        String taken = array.takeNearestLargerOrEqual( "95" );
+        assertEquals( "Taken should be 96", "96", taken );
+        assertEquals( "Size was not as expected.", ( maxSize - 1 ), array.size() );
+
+        // System.out.println( array.dumpArray() );
+    }
+
+    /**
+     * Verify that we don't get an error when taking from an empty array.
+     * @throws Exception
+     */
+    public void testEmptyTake()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+        for ( int i = 0; i < maxSize; i++ )
+        {
+            String taken = array.takeNearestLargerOrEqual( String.valueOf( i ) );
+            assertNull( "taken should be null, since nothing was in the array", taken );
+        }
+    }
+
+    /**
+     * Verify that we don't get a null pointer if we insert a null.
+     * @throws Exception
+     */
+    public void testNullInsertion()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+
+        String[] elem = {
+            "10",
+            "11",
+            "01",
+            "02",
+            "03",
+            "04",
+            "05",
+            "08",
+            "07",
+            "06",
+            "09",
+            "12",
+            "13",
+            "15",
+            "14",
+            "20",
+            "25",
+            "29",
+            "28",
+            "16",
+            "17",
+            "96",
+            "00",
+            "72",
+            "39",
+            "55",
+            "44",
+            "26",
+            "22",
+            "59",
+            "38",
+            "16",
+            "27" };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+        }
+        // System.out.println( array.dumpArray() );
+
+        assertEquals( "Size was not as expected.", maxSize, array.size() );
+
+        try
+        {
+            // should not get an error
+            array.add( null );
+        }
+        catch ( NullPointerException e )
+        {
+            fail( "Got a null pointer inserting a null" );
+        }
+
+    }
+
+    /**
+     * Verify that we don't get an npe when taking with a null
+     * @throws Exception
+     */
+    public void testNullTake()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+
+        try
+        {
+            String taken = array.takeNearestLargerOrEqual( null );
+            assertNull( "taken should be null, since nothing was in the array", taken );
+        }
+        catch ( NullPointerException e )
+        {
+            fail( "Got a null pointer trying to take with a null" );
+        }
+    }
+
+    /**
+     * Verify that we don't get an npe when taking from an array of only one
+     * @throws Exception
+     */
+    public void testSingleItemTake()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+
+        array.add( "10" );
+        // System.out.println( array.dumpArray() );
+
+        try
+        {
+            String taken = array.takeNearestLargerOrEqual( "09" );
+            // System.out.println( taken );
+            assertNotNull( "taken should not be null, since nothing was in the array", taken );
+        }
+        catch ( NullPointerException e )
+        {
+            fail( "Got a null pointer trying to take with a null" );
+        }
+    }
+
+    /**
+     * Verify that we don't get an npe when taking from an array of only one
+     * @throws Exception
+     */
+    public void testSingleItemTakeLarger()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+
+        array.add( "10" );
+
+        try
+        {
+            String taken = array.takeNearestLargerOrEqual( "11" );
+            assertNull( "taken should be null, since nothing smaller was in the array", taken );
+        }
+        catch ( NullPointerException e )
+        {
+            fail( "Got a null pointer trying to take with a null" );
+        }
+    }
+
+    /**
+     * Verify that we don't get an npe when taking from an array of none
+     * @throws Exception
+     */
+    public void testSingleItemTakeLargerEmpty()
+        throws Exception
+    {
+        int maxSize = 25;
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        array.setPreferLarge( true );
+
+        try
+        {
+            String taken = array.takeNearestLargerOrEqual( "11" );
+            assertNull( "taken should be null, since nothing was in the array", taken );
+        }
+        catch ( NullPointerException e )
+        {
+            fail( "Got a null pointer trying to take with a null" );
+        }
+    }
+
+    /**
+     * Test taking the largest item.
+     * @throws Exception
+     */
+    public void testTakeLargestItem()
+        throws Exception
+    {
+        int maxSize = 9;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = { "01", "02", "03", "04", "05", "08", "07", "06", "09", };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        assertEquals( "Size was not as expected.", maxSize, array.size() );
+
+        // this is a fragile test, since it relies on a hardcoded array
+        String smallest = array.getSmallest();
+        assertEquals( "smallest is not as expected", "01", smallest );
+
+        String largest = array.getLargest();
+        assertEquals( "Largest is not as expected", "09", largest );
+
+        // this should take 96;
+        String taken = array.takeNearestLargerOrEqual( "09" );
+        assertEquals( "Taken is not as expected", "09", taken );
+        assertEquals( "Size was not as expected.", ( maxSize - 1 ), array.size() );
+
+        // System.out.println( "testTakeLastItem" + array.dumpArray() );
+    }
+
+    /**
+     * Test taking every last item.
+     * <p>
+     * @throws Exception
+     */
+    public void testTakeEveryLastItem()
+        throws Exception
+    {
+        int maxSize = 9;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = { "01", "02", "03", "04", "05", "08", "07", "06", "09", };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        assertEquals( "Size was not as expected.", maxSize, array.size() );
+
+        // this is a fragile test, since it relies on a hardcoded array
+        String smallest = array.getSmallest();
+        assertEquals( "smallest is not as expected", "01", smallest );
+
+        String largest = array.getLargest();
+        assertEquals( "Largest is not as expected", "09", largest );
+
+        // this should take 96;
+        String taken = array.takeNearestLargerOrEqual( "09" );
+        assertEquals( "Taken is not as expected", "09", taken );
+        assertEquals( "Size was not as expected. " + array.dumpArray(), ( maxSize - 1 ), array.size() );
+
+        // System.out.println( "testTakeEveryLastItem" + array.dumpArray() );
+
+        // take the rest
+        // take more than the max in a reverse order
+        for ( int i = elem.length - 1; i >= 0; i-- )
+        {
+            array.takeNearestLargerOrEqual( elem[i] );
+        }
+        // System.out.println( "testTakeEveryLastItem" + array.dumpArray() );
+
+        assertEquals( "There should nothing left. " + array.dumpArray(), 0, array.size() );
+    }
+
+    /**
+     * Try taking an item larger than the greatest.
+     */
+    public void testTakeLargerThanGreatest()
+    {
+        int maxSize = 3;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = { "01", "02", "03" };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        // DO WORK
+        Comparable<String> taken = array.takeNearestLargerOrEqual( "04" );
+//        System.out.println( "testTakeLargerThanGreatest" + array.dumpArray() );
+
+        assertNull( "We should have nothing since the largest element was smaller than what we asked for. "
+            + " Instead we got " + taken, taken );
+    }
+
+    /**
+     * Try taking an item equal to the greatest.  Make the last two the same size
+     */
+    public void testEqualToGreatest_LastTwoSameSize()
+    {
+        int maxSize = 3;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = { "01", "02", "03", "03" };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        // DO WORK
+        Comparable<String> taken = array.takeNearestLargerOrEqual( "03" );
+        // System.out.println( "testEqualToGreatest_LastTwoSameSize" + array.dumpArray() );
+
+        assertNotNull( "We should have something since the largest element was equal to what we asked for.", taken );
+    }
+
+    /**
+     * Try taking an item equal to the greatest.  The second to last should be smaller. This verifies the most basic funtionality.
+     */
+    public void testEqualToGreatest()
+    {
+        int maxSize = 3;
+
+        SortedPreferentialArray<String> array = new SortedPreferentialArray<String>( maxSize );
+        // array.setPreferLarge( false );
+        array.setPreferLarge( true );
+        String[] elem = { "01", "02", "03" };
+
+        // put more than the max in a random order
+        for ( int i = 0; i < elem.length; i++ )
+        {
+            array.add( elem[i] );
+            // System.out.println( array.dumpArray() );
+        }
+
+        // DO WORK
+        Comparable<String> taken = array.takeNearestLargerOrEqual( "03" );
+        // System.out.println( "testEqualToGreatest" + array.dumpArray() );
+
+        assertNotNull( "We should have something since the largest element was equal to what we asked for.", taken );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManagerUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManagerUnitTest.java
new file mode 100644
index 0000000..dc2998e
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolManagerUnitTest.java
@@ -0,0 +1,194 @@
+package org.apache.commons.jcs.utils.threadpool;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.jcs.utils.props.PropertyLoader;
+import org.apache.commons.jcs.utils.threadpool.PoolConfiguration.WhenBlockedPolicy;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Verify that the manager can create pools as intended by the default and
+ * specified file names.
+ *
+ * @author asmuts
+ */
+public class ThreadPoolManagerUnitTest
+    extends TestCase
+{
+
+    /**
+     * Make sure it can load a default cache.ccf file
+     */
+    public void testDefaultConfig()
+    {
+        ThreadPoolManager.setPropsFileName( "thread_pool.properties" );
+        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+        assertNotNull( mgr );
+
+        ThreadPoolExecutor pool = mgr.getPool( "test1" );
+        assertNotNull( pool );
+
+        int poolSize = pool.getPoolSize();
+        int expectedPoolSize = Integer.parseInt( PropertyLoader.loadProperties( "thread_pool.properties" )
+            .getProperty( "thread_pool.test1.startUpSize" ) );
+        assertEquals( poolSize, expectedPoolSize );
+
+        // int qs = ((BoundedBuffer)pool.getQueue()).size();
+
+        int max = pool.getMaximumPoolSize();
+
+        int expected = Integer.parseInt( PropertyLoader.loadProperties( "thread_pool.properties" )
+            .getProperty( "thread_pool.test1.maximumPoolSize" ) );
+        assertEquals(expected, max );
+    }
+
+    /**
+     * Try to get an undefined pool from an existing default file.
+     */
+    public void testDefaultConfigUndefinedPool()
+    {
+        ThreadPoolManager.setPropsFileName( "thread_pool.properties" );
+        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+        assertNotNull( mgr );
+
+        ThreadPoolExecutor pool = mgr.getPool( "doesnotexist" );
+        assertNotNull( pool );
+
+        int max = pool.getMaximumPoolSize();
+
+        int expected = Integer.parseInt( PropertyLoader.loadProperties( "thread_pool.properties" )
+            .getProperty( "thread_pool.default.maximumPoolSize" ) );
+        assertEquals( expected, max );
+    }
+
+    /**
+     * Makes ure we can get a non existent pool from the non exitent config
+     * file.
+     */
+    public void testNonExistentConfigFile()
+    {
+        ThreadPoolManager.setPropsFileName( "somefilethatdoesntexist" );
+        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+        assertNotNull( mgr );
+
+        ThreadPoolExecutor pool = mgr.getPool( "doesntexist" );
+        assertNotNull( "Should have gotten back a pool configured like the default", pool );
+
+        int max = pool.getMaximumPoolSize();
+
+        // it will load from the default file
+        int expected = Integer.parseInt( PropertyLoader.loadProperties( "cache.ccf" )
+            .getProperty( "thread_pool.default.maximumPoolSize" ) );
+
+        assertEquals( expected, max );
+    }
+
+    /**
+     * Get a couple pools by name and then see if they are in the list.
+     *
+     */
+    public void testGetPoolNames()
+    {
+        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+        assertNotNull( mgr );
+
+        String poolName1 = "testGetPoolNames1";
+        mgr.getPool( poolName1 );
+
+        String poolName2 = "testGetPoolNames2";
+        mgr.getPool( poolName2 );
+
+        ArrayList<String> names = mgr.getPoolNames();
+        assertTrue( "Should have name in list.", names.contains( poolName1 ) );
+        assertTrue( "Should have name in list.", names.contains( poolName2 ) );
+    }
+
+    /**
+     * Verify that the wait policy gets set correctly.
+     *
+     * Switched off as the POLICY_WAIT is not supported by the javax.concurrent package
+     */
+    public void OFFtestWaitPolicyConfig()
+    {
+        ThreadPoolManager.setPropsFileName( "thread_pool.properties" );
+        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+        // force config from new props file
+        mgr.configure();
+        assertNotNull( mgr );
+
+        ThreadPoolExecutor pool = mgr.getPool( "waittest" );
+        assertNotNull( "Should have gotten back a pool.", pool );
+
+        int max = pool.getMaximumPoolSize();
+
+        // it will load from the default file
+        int expected = Integer.parseInt( PropertyLoader.loadProperties( "thread_pool.properties" )
+            .getProperty( "thread_pool.waittest.maximumPoolSize" ) );
+
+        assertEquals( "Max is wrong", expected, max );
+
+        PoolConfiguration config = mgr.loadConfig( "thread_pool.waittest" );
+
+        assertEquals( "Policy is wrong.", WhenBlockedPolicy.WAIT, config.getWhenBlockedPolicy() );
+    }
+
+    /**
+     * Verify that if we specify not to use a buffer boundary that we get a
+     * linked queue.
+     *
+     */
+//    public void testNoBoundary()
+//    {
+//        ThreadPoolManager.setPropsFileName( "thread_pool.properties" );
+//        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+//        // force config from new props file
+//        mgr.configure();
+//        assertNotNull( mgr );
+//
+//        ThreadPoolExecutor pool = mgr.getPool( "nobound" );
+//        assertNotNull( "Should have gotten back a pool.", pool );
+//
+//        assertTrue( "Should have a linked queue and not a bounded buffer.", pool.getQueue() instanceof LinkedQueue );
+//    }
+
+    /**
+     * Verify that if we specify useBoundary=true that we get a BoundedBuffer.
+     *
+     */
+//    public void testWithBoundary()
+//    {
+//        // SETUP
+//        ThreadPoolManager.setPropsFileName( "thread_pool.properties" );
+//        ThreadPoolManager mgr = ThreadPoolManager.getInstance();
+//        // force config from new props file
+//        mgr.configure();
+//        assertNotNull( mgr );
+//
+//        // DO WORK
+//        ThreadPoolExecutor pool = mgr.getPool( "withbound" );
+//
+//        // VERIFY
+//        assertNotNull( "Should have gotten back a pool.", pool );
+//        assertTrue( "Should have a BoundedBuffer and not a linked queue.", pool.getQueue() instanceof BoundedBuffer );
+//    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolUnitTest.java
new file mode 100644
index 0000000..677fb39
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/threadpool/ThreadPoolUnitTest.java
@@ -0,0 +1,88 @@
+package org.apache.commons.jcs.utils.threadpool;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * This test is experimental. I'm trying to find out if the max size setting will result in the
+ * removal of threads.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class ThreadPoolUnitTest
+    extends TestCase
+{
+    /**
+     * Make sure that the max size setting takes effect before the idle time is reached.
+     * <p>
+     * We just want to ensure that we can adjust the max size of an active pool.
+     * <p>
+     * http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/PooledExecutor.html#
+     * setMaximumPoolSize(int) p>
+     * @throws Exception
+     */
+    public void testMaxReduction()
+        throws Exception
+    {
+        //ThreadPoolManager.setPropsFileName( "thread_pool_test.properties" );
+        ThreadPoolExecutor pool = ThreadPoolManager.getInstance().getPool( "maxtest" );
+
+        //System.out.println( "pool = " + pool );
+        pool.setMaximumPoolSize( 5 );
+        //System.out.println( "current size before execute = " + pool.getPool().getPoolSize() );
+
+        // add 6
+        for ( int i = 1; i < 30; i++ )
+        {
+            final int cnt = i;
+            pool.execute( new Runnable()
+            {
+
+                @Override
+                public void run()
+                {
+                    try
+                    {
+                        //System.out.println( cnt );
+//                        System.out.println( "count = " + cnt + " before sleep current size = " + myPool.getPoolSize() );
+                        Thread.sleep( 200 / cnt );
+                        //System.out.println( "count = " + cnt + " after sleep current size = " + myPool.getPool().getPoolSize() );
+                    }
+                    catch ( InterruptedException e )
+                    {
+                        // TODO Auto-generated catch block
+                        e.printStackTrace();
+                    }
+                }
+            } );
+        }
+
+        //System.out.println( "current size = " + pool.getPool().getPoolSize() );
+        pool.setMaximumPoolSize( 4 );
+        //Thread.sleep( 200 );
+        //System.out.println( "current size after set size to 4= " + pool.getPool().getPoolSize() );
+        Thread.sleep( 200 );
+        //System.out.println( "current size again after sleep = " + pool.getPool().getPoolSize() );
+        assertEquals( "Pool size should have been reduced.", 4, pool.getPoolSize() );
+    }
+}
diff --git a/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/zip/CompressionUtilUnitTest.java b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/zip/CompressionUtilUnitTest.java
new file mode 100644
index 0000000..1c390f7
--- /dev/null
+++ b/commons-jcs-core/src/test/java/org/apache/commons/jcs/utils/zip/CompressionUtilUnitTest.java
@@ -0,0 +1,97 @@
+package org.apache.commons.jcs.utils.zip;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+
+/** Unit tests for the compression util */
+public class CompressionUtilUnitTest
+    extends TestCase
+{
+    /** Test method for decompressByteArray. */
+    public final void testDecompressByteArray_failure()
+    {
+        try
+        {
+            // DO WORK
+            CompressionUtil.decompressByteArray( null );
+
+            // VERIFY
+            fail( "excepted an IllegalArgumentException" );
+        }
+        catch ( IllegalArgumentException exception )
+        {
+            // expected
+            return;
+        }
+    }
+
+    /**
+     * Test method for decompressByteArray.
+     * <p>
+     * @throws IOException
+     */
+    public final void testCompressDecompressByteArray_success()
+        throws IOException
+    {
+        // SETUP
+        String text = "This is some text to compress, not a lot, just a bit ";
+
+        // DO WORK
+        byte[] compressedText = CompressionUtil.compressByteArray( text.getBytes() );
+        byte[] output = CompressionUtil.decompressByteArray( compressedText );
+
+        // VERIFY
+        String result = new String( output );
+        assertNotNull( "decompressed output stream shouldn't have been null ", output );
+        assertEquals( text, result );
+    }
+
+    /**
+     * Test method for decompressByteArray.
+     * <p>
+     * @throws IOException
+     */
+    public final void testCompressDecompressGzipByteArray_success()
+        throws IOException
+    {
+        // SETUP
+        String text = " This is some text to compress, not a lot, just a bit ";
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GZIPOutputStream os = new GZIPOutputStream( baos );
+
+        os.write( text.getBytes() );
+        os.flush();
+        os.close();
+
+        // DO WORK
+        byte[] output = CompressionUtil.decompressGzipByteArray( baos.toByteArray() );
+
+        // VERIFY
+        String result = new String( output );
+        assertNotNull( "decompressed output stream shouldn't have been null ", output );
+        assertEquals( text, result );
+    }
+}
diff --git a/commons-jcs-core/src/test/test-conf/TestARCCache.ccf b/commons-jcs-core/src/test/test-conf/TestARCCache.ccf
new file mode 100644
index 0000000..bf8c10f
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestARCCache.ccf
@@ -0,0 +1,33 @@
+# 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.
+# JCS Config for unit testing, just a simple memory only cache, with 10 max size
+# with the memory shrinker on.
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.arc.ARCMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=1
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=true
+jcs.default.elementattributes.MaxLife=600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs-core/src/test/test-conf/TestBDBJEDiskCacheCon.ccf b/commons-jcs-core/src/test/test-conf/TestBDBJEDiskCacheCon.ccf
new file mode 100644
index 0000000..914386e
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestBDBJEDiskCacheCon.ccf
@@ -0,0 +1,90 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# SYSTEM GROUP ID CACHE
+jcs.system.groupIdCache=indexedDiskCache
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=10000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=indexedDiskCache
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion2=indexedDiskCache
+jcs.region.indexedRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion2.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion3=indexedDiskCache
+jcs.region.indexedRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion3.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion4=indexedDiskCache
+jcs.region.indexedRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion4.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.indexedRegion4.cacheattributes.UseMemoryShrinker=false
+
+
+# #### AUXILIARY CACHES
+
+# Berkeley DB JE
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/bdbje-disk-cache-conc
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/TestBlockDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestBlockDiskCache.ccf
new file mode 100644
index 0000000..222a693
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestBlockDiskCache.ccf
@@ -0,0 +1,94 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=blockDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# SYSTEM GROUP ID CACHE
+jcs.system.groupIdCache=blockDiskCache
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=10000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.blockRegion1=blockDiskCache
+jcs.region.blockRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion1.cacheattributes.MaxObjects=100
+jcs.region.blockRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion2=blockDiskCache
+jcs.region.blockRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion2.cacheattributes.MaxObjects=100
+jcs.region.blockRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion3=blockDiskCache
+jcs.region.blockRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion3.cacheattributes.MaxObjects=100
+jcs.region.blockRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion4=blockDiskCache2
+jcs.region.blockRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion4.cacheattributes.MaxObjects=100
+jcs.region.blockRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Block Disk Cache
+jcs.auxiliary.blockDiskCache=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.blockDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.blockDiskCache.attributes.DiskPath=target/test-sandbox/block-disk-cache
+
+# Block Disk Cache
+jcs.auxiliary.blockDiskCache2=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.blockDiskCache2.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.blockDiskCache2.attributes.DiskPath=target/test-sandbox/block-disk-cache2
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheCon.ccf b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheCon.ccf
new file mode 100644
index 0000000..e12d2ec
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheCon.ccf
@@ -0,0 +1,94 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=blockDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.blockRegion1=blockDiskCache
+jcs.region.blockRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion1.cacheattributes.MaxObjects=100
+jcs.region.blockRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion2=blockDiskCache
+jcs.region.blockRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion2.cacheattributes.MaxObjects=100
+jcs.region.blockRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion3=blockDiskCache
+jcs.region.blockRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion3.cacheattributes.MaxObjects=100
+jcs.region.blockRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.blockRegion4=blockDiskCache
+jcs.region.blockRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion4.cacheattributes.MaxObjects=100
+jcs.region.blockRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Block Disk Cache
+jcs.auxiliary.blockDiskCache=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.blockDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.blockDiskCache.attributes.DiskPath=target/test-sandbox/block-disk-cache-conc
+jcs.auxiliary.blockDiskCache.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.blockDiskCache.attributes.MaxKeySize=10000
+jcs.auxiliary.blockDiskCache.attributes.EventQueueType=SINGLE
+jcs.auxiliary.blockDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=1
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=1
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheHuge.ccf b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheHuge.ccf
new file mode 100644
index 0000000..9106539
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheHuge.ccf
@@ -0,0 +1,61 @@
+# 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.
+# Cache configuration for the 'TestDiskCacheNoMemory' test. The memory cache has a
+# a maximum of 0 objects, so objects should get pushed into the disk cache
+
+jcs.default=blockDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.blockRegion1=blockDiskCache
+jcs.region.blockRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.blockRegion1.cacheattributes.MaxObjects=0
+jcs.region.blockRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Block Disk Cache
+jcs.auxiliary.blockDiskCache=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.blockDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.blockDiskCache.attributes.DiskPath=target/test-sandbox/block-disk-cache-huge
+jcs.auxiliary.blockDiskCache.attributes.MaxPurgatorySize=300000
+jcs.auxiliary.blockDiskCache.attributes.MaxKeySize=1000000
+jcs.auxiliary.blockDiskCache.attributes.blockSizeBytes=500
+jcs.auxiliary.blockDiskCache.attributes.EventQueueType=SINGLE
+# jcs.auxiliary.blockDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheSteadyLoad.ccf b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheSteadyLoad.ccf
new file mode 100644
index 0000000..535131d
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestBlockDiskCacheSteadyLoad.ccf
@@ -0,0 +1,32 @@
+# 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.
+# Java Caching System configuration file
+
+# DEFAULT CACHE REGION
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.DiskUsagePatternName=UPDATE
+
+
+# AVAILABLE AUXILIARY CACHES
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/block-steady-load
+jcs.auxiliary.DC.attributes.maxKeySize=20000
+jcs.auxiliary.DC.attributes.blockSizeBytes=5000
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCache.ccf
new file mode 100644
index 0000000..dfdf941
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCache.ccf
@@ -0,0 +1,94 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# SYSTEM GROUP ID CACHE
+jcs.system.groupIdCache=indexedDiskCache
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=10000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=indexedDiskCache
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion2=indexedDiskCache
+jcs.region.indexedRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion2.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion3=indexedDiskCache
+jcs.region.indexedRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion3.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion4=indexedDiskCache2
+jcs.region.indexedRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion4.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache2.attributes.DiskPath=target/test-sandbox/indexed-disk-cache2
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheCon.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheCon.ccf
new file mode 100644
index 0000000..63ba766
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheCon.ccf
@@ -0,0 +1,96 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=indexedDiskCache
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion2=indexedDiskCache
+jcs.region.indexedRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion2.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion3=indexedDiskCache
+jcs.region.indexedRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion3.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion4=indexedDiskCache
+jcs.region.indexedRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion4.cacheattributes.MaxObjects=100
+jcs.region.indexedRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache-conc
+jcs.auxiliary.indexedDiskCache.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.indexedDiskCache.attributes.MaxKeySize=10000
+jcs.auxiliary.indexedDiskCache.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.indexedDiskCache.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.indexedDiskCache.attributes.EventQueueType=SINGLE
+jcs.auxiliary.indexedDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=1
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=1
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheDefragPerformance.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheDefragPerformance.ccf
new file mode 100644
index 0000000..7ff2c7d
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheDefragPerformance.ccf
@@ -0,0 +1,34 @@
+# 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.
+# Java Caching System configuration file
+
+# DEFAULT CACHE REGION
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+cs.default.cacheattributes.DiskUsagePatterName=UPDATE
+
+# AVAILABLE AUXILIARY CACHES
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/defrag
+jcs.auxiliary.DC.attributes.maxKeySize=10000
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=5000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheHuge.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheHuge.ccf
new file mode 100644
index 0000000..f8b14f5
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheHuge.ccf
@@ -0,0 +1,62 @@
+# 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.
+# Cache configuration for the 'TestDiskCacheNoMemory' test. The memory cache has a
+# a maximum of 0 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=indexedDiskCache
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=0
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache-conc
+jcs.auxiliary.indexedDiskCache.attributes.MaxPurgatorySize=300000
+jcs.auxiliary.indexedDiskCache.attributes.MaxKeySize=500000
+jcs.auxiliary.indexedDiskCache.attributes.MaxRecycleBinSize=50000
+jcs.auxiliary.indexedDiskCache.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.indexedDiskCache.attributes.EventQueueType=SINGLE
+# jcs.auxiliary.indexedDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheNoMemory.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheNoMemory.ccf
new file mode 100644
index 0000000..1d3e1e7
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheNoMemory.ccf
@@ -0,0 +1,93 @@
+# 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.
+# Cache configuration for the 'TestDiskCacheNoMemory' test. The memory cache has a
+# a maximum of 0 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# SYSTEM GROUP ID CACHE
+jcs.system.groupIdCache=indexedDiskCache
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=0
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.indexedRegion1=indexedDiskCache
+jcs.region.indexedRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion1.cacheattributes.MaxObjects=0
+jcs.region.indexedRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion2=indexedDiskCache
+jcs.region.indexedRegion2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion2.cacheattributes.MaxObjects=0
+jcs.region.indexedRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion3=indexedDiskCache
+jcs.region.indexedRegion3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion3.cacheattributes.MaxObjects=0
+jcs.region.indexedRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.indexedRegion4=indexedDiskCache2
+jcs.region.indexedRegion4.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.indexedRegion4.cacheattributes.MaxObjects=0
+jcs.region.indexedRegion4.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache-nomemory
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache2.attributes.DiskPath=target/test-sandbox/indexed-disk-cache2-nomemory
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheSteadyLoad.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheSteadyLoad.ccf
new file mode 100644
index 0000000..e25bdef
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheSteadyLoad.ccf
@@ -0,0 +1,34 @@
+# 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.
+# Java Caching System configuration file
+
+# DEFAULT CACHE REGION
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.DiskUsagePatternName=UPDATE
+
+
+# AVAILABLE AUXILIARY CACHES
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/steady-load
+jcs.auxiliary.DC.attributes.maxKeySize=1000
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=1000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=20000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=10000
diff --git a/commons-jcs-core/src/test/test-conf/TestDiskCacheUsagePattern.ccf b/commons-jcs-core/src/test/test-conf/TestDiskCacheUsagePattern.ccf
new file mode 100644
index 0000000..61db76e
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestDiskCacheUsagePattern.ccf
@@ -0,0 +1,85 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+jcs.region.Swap=indexedDiskCache
+jcs.region.Swap.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.Swap.cacheattributes.MaxObjects=100
+jcs.region.Swap.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.Swap.cacheattributes.DiskUsagePatternName=SWAP
+
+jcs.region.Update=indexedDiskCache
+jcs.region.Update.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.Update.cacheattributes.MaxObjects=100
+jcs.region.Update.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.Update.cacheattributes.DiskUsagePatternName=UPDATE
+
+
+# #### AUXILIARY CACHES
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache-conc
+jcs.auxiliary.indexedDiskCache.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.indexedDiskCache.attributes.MaxKeySize=10000
+jcs.auxiliary.indexedDiskCache.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.indexedDiskCache.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.indexedDiskCache.attributes.EventQueueType=SINGLE
+jcs.auxiliary.indexedDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=1
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=1
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestHSQLDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestHSQLDiskCache.ccf
new file mode 100644
index 0000000..d6b3a08
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestHSQLDiskCache.ccf
@@ -0,0 +1,83 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=HSQL
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+jcs.region.noRemoveAll=HSQL_NORA
+jcs.region.noRemoveAll.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.noRemoveAll.cacheattributes.MaxObjects=0
+jcs.region.noRemoveAll.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# HSQL disk cache
+jcs.auxiliary.HSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.hsql.HSQLDiskCacheFactory
+jcs.auxiliary.HSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.HSQL.attributes.userName=sa
+jcs.auxiliary.HSQL.attributes.password=
+jcs.auxiliary.HSQL.attributes.url=jdbc:hsqldb:target/HSQLDiskCacheUnitTest1
+jcs.auxiliary.HSQL.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.HSQL.attributes.tableName=JCS_STORE3
+jcs.auxiliary.HSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.HSQL.attributes.maxActive=15
+jcs.auxiliary.HSQL.attributes.allowRemoveAll=true
+jcs.auxiliary.HSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.HSQL.attributes.EventQueueType=SINGLE
+
+# HSQL disk cache, doesn't allow remove all
+jcs.auxiliary.HSQL_NORA=org.apache.commons.jcs.auxiliary.disk.jdbc.hsql.HSQLDiskCacheFactory
+jcs.auxiliary.HSQL_NORA.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.HSQL_NORA.attributes.userName=sa
+jcs.auxiliary.HSQL_NORA.attributes.password=
+jcs.auxiliary.HSQL_NORA.attributes.url=jdbc:hsqldb:target/HSQLDiskCacheUnitTest2
+jcs.auxiliary.HSQL_NORA.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.HSQL_NORA.attributes.tableName=JCS_STORE4
+jcs.auxiliary.HSQL_NORA.attributes.testBeforeInsert=false
+jcs.auxiliary.HSQL_NORA.attributes.maxActive=15
+jcs.auxiliary.HSQL_NORA.attributes.allowRemoveAll=false
+jcs.auxiliary.HSQL_NORA.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.HSQL_NORA.attributes.EventQueueType=POOLED
+jcs.auxiliary.HSQL_NORA.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf b/commons-jcs-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf
new file mode 100644
index 0000000..c1f1f95
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf
@@ -0,0 +1,84 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=HSQL
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+jcs.region.noRemoveAll=HSQL_NORA
+jcs.region.noRemoveAll.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.noRemoveAll.cacheattributes.MaxObjects=0
+jcs.region.noRemoveAll.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# HSQL disk cache
+jcs.auxiliary.HSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.hsql.HSQLDiskCacheFactory
+jcs.auxiliary.HSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.HSQL.attributes.userName=sa
+jcs.auxiliary.HSQL.attributes.password=
+jcs.auxiliary.HSQL.attributes.url=jdbc:hsqldb:target/HSQLDiskCacheUnitTest1
+jcs.auxiliary.HSQL.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.HSQL.attributes.tableName=JCS_STORE3
+jcs.auxiliary.HSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.HSQL.attributes.maxActive=15
+jcs.auxiliary.HSQL.attributes.allowRemoveAll=true
+jcs.auxiliary.HSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.HSQL.attributes.EventQueueType=POOLED
+jcs.auxiliary.HSQL.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# HSQL disk cache, doesn't allow remove all
+jcs.auxiliary.HSQL_NORA=org.apache.commons.jcs.auxiliary.disk.jdbc.hsql.HSQLDiskCacheFactory
+jcs.auxiliary.HSQL_NORA.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.HSQL_NORA.attributes.userName=sa
+jcs.auxiliary.HSQL_NORA.attributes.password=
+jcs.auxiliary.HSQL_NORA.attributes.url=jdbc:hsqldb:target/HSQLDiskCacheUnitTest2
+jcs.auxiliary.HSQL_NORA.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.HSQL_NORA.attributes.tableName=JCS_STORE4
+jcs.auxiliary.HSQL_NORA.attributes.testBeforeInsert=false
+jcs.auxiliary.HSQL_NORA.attributes.maxActive=15
+jcs.auxiliary.HSQL_NORA.attributes.allowRemoveAll=false
+jcs.auxiliary.HSQL_NORA.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.HSQL_NORA.attributes.EventQueueType=POOLED
+jcs.auxiliary.HSQL_NORA.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestJCS-73.ccf b/commons-jcs-core/src/test/test-conf/TestJCS-73.ccf
new file mode 100644
index 0000000..ddfe439
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJCS-73.ccf
@@ -0,0 +1,42 @@
+# 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.
+# Cache configuration for the 'JCSConcurrentCacheAccessUnitTest' test.
+
+jcs.default=CACHE
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=-1
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.DiskUsagePatternName=UPDATE
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=10
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsRemote=false
+jcs.default.elementattributes.IsLateral=false
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsEternal=true
+
+jcs.auxiliary.CACHE=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.CACHE.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.CACHE.attributes.DiskPath=target/test-sandbox/concurrent_cache
+jcs.auxiliary.CACHE.attributes.MaxPurgatorySize=-1
+jcs.auxiliary.CACHE.attributes.MaxKeySize=-1
+jcs.auxiliary.CACHE.attributes.MaxRecycleBinSize=500
+jcs.auxiliary.CACHE.attributes.ShutdownSpoolTimeLimit=60
+jcs.auxiliary.CACHE.attributes.OptimizeAtRemoveCount=30000
+jcs.auxiliary.CACHE.attributes.OptimizeOnShutdown=true
+jcs.auxiliary.CACHE.attributes.EventQueueType=SINGLE
diff --git a/commons-jcs-core/src/test/test-conf/TestJCSvHashtablePerf.ccf b/commons-jcs-core/src/test/test-conf/TestJCSvHashtablePerf.ccf
new file mode 100644
index 0000000..5ef882d
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJCSvHashtablePerf.ccf
@@ -0,0 +1,60 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.testCache1=
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=100000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestJDBCDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCache.ccf
new file mode 100644
index 0000000..c193fbd
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCache.ccf
@@ -0,0 +1,62 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=JDBC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC.attributes.userName=sa
+jcs.auxiliary.JDBC.attributes.password=
+jcs.auxiliary.JDBC.attributes.url=jdbc:hsqldb:target/cache_hsql_db
+jcs.auxiliary.JDBC.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.JDBC.attributes.tableName=JCS_STORE2
+jcs.auxiliary.JDBC.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC.attributes.maxActive=15
+jcs.auxiliary.JDBC.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf
new file mode 100644
index 0000000..db5c70a
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf
@@ -0,0 +1,62 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=JDBC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC.attributes.userName=sa
+jcs.auxiliary.JDBC.attributes.password=
+jcs.auxiliary.JDBC.attributes.url=jdbc:hsqldb:target/JDBCDiskCacheRemovalUnitTest
+jcs.auxiliary.JDBC.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.JDBC.attributes.tableName=${DATABASE_NAME}
+jcs.auxiliary.JDBC.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC.attributes.maxActive=15
+jcs.auxiliary.JDBC.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf
new file mode 100644
index 0000000..387debd
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf
@@ -0,0 +1,83 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=JDBC_0
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# CONFIGURED REGIONS ########################
+
+jcs.region.testCache1=JDBC_1
+jcs.region.testCache1.cacheattributes.MaxObjects=100
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC_0=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC_0.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC_0.attributes.tableName=JCS_STORE_0
+jcs.auxiliary.JDBC_0.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC_0.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC_0.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC_0.attributes.connectionPoolName=MySharedPool
+jcs.auxiliary.JDBC_0.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC_0.attributes.EventQueuePoolName=disk_cache_event_queue
+
+jcs.auxiliary.JDBC_1=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC_1.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC_1.attributes.tableName=JCS_STORE_1
+jcs.auxiliary.JDBC_1.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC_1.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC_1.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC_1.attributes.connectionPoolName=MySharedPool
+jcs.auxiliary.JDBC_1.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC_1.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ######## OPTIONAL SHARED CONNECTION POOL CONFIGURATION ######
+# My Shared Pool
+
+jcs.jdbcconnectionpool.MySharedPool.attributes.userName=sa
+jcs.jdbcconnectionpool.MySharedPool.attributes.password=
+jcs.jdbcconnectionpool.MySharedPool.attributes.url=jdbc:hsqldb:target/cache_hsql_db_sharedpool
+jcs.jdbcconnectionpool.MySharedPool.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.jdbcconnectionpool.MySharedPool.attributes.maxActive=15
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf
new file mode 100644
index 0000000..047a2df
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf
@@ -0,0 +1,85 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=JDBC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# REGIONS ###################################
+jcs.region.expire1Second=JDBC
+jcs.region.expire1Second.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.expire1Second.cacheattributes.MaxObjects=0
+jcs.region.expire1Second.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.expire1Second.elementattributes.MaxLife=1
+
+jcs.region.expire100Second=JDBC
+jcs.region.expire100Second.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.expire100Second.cacheattributes.MaxObjects=0
+jcs.region.expire100Second.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.expire100Second.elementattributes.MaxLife=100
+
+jcs.region.eternal=JDBC
+jcs.region.eternal.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.eternal.cacheattributes.MaxObjects=0
+jcs.region.eternal.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.eternal.elementattributes.MaxLife=1
+jcs.region.eternal.elementattributes.IsEternal=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC.attributes.userName=sa
+jcs.auxiliary.JDBC.attributes.password=
+jcs.auxiliary.JDBC.attributes.url=jdbc:hsqldb:target/JDBCDiskCacheShrinkUnitTest
+jcs.auxiliary.JDBC.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.JDBC.attributes.tableName=JCS_STORE_SHRINK
+jcs.auxiliary.JDBC.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC.attributes.maxActive=14
+jcs.auxiliary.JDBC.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC.attributes.MaxPurgatorySize=9999
+jcs.auxiliary.JDBC.attributes.UseDiskShrinker=true
+jcs.auxiliary.JDBC.attributes.ShrinkerIntervalSeconds=1
+jcs.auxiliary.JDBC.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=501
+thread_pool.disk_cache_event_queue.maximumPoolSize=13
+thread_pool.disk_cache_event_queue.minimumPoolSize=11
+thread_pool.disk_cache_event_queue.keepAliveTime=35010
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=9
diff --git a/commons-jcs-core/src/test/test-conf/TestJispDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestJispDiskCache.ccf
new file mode 100644
index 0000000..9a135d9
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestJispDiskCache.ccf
@@ -0,0 +1,40 @@
+# 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.
+# Cache configuration for the 'TestHSQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=JDC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #### AUXILIARY CACHES
+# JISP Disk Cache -- save memory with disk key storage
+jcs.auxiliary.JDC=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheFactory
+jcs.auxiliary.JDC.attributes=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheAttributes
+jcs.auxiliary.JDC.attributes.DiskPath=target/test-sandbox/jjisp-disk-cache
+jcs.auxiliary.JDC.attributes.ClearOnStart=false
diff --git a/commons-jcs-core/src/test/test-conf/TestLHMLRUCache.ccf b/commons-jcs-core/src/test/test-conf/TestLHMLRUCache.ccf
new file mode 100644
index 0000000..ba34a49
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestLHMLRUCache.ccf
@@ -0,0 +1,23 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LHMLRUMemoryCache
diff --git a/commons-jcs-core/src/test/test-conf/TestMRUCache.ccf b/commons-jcs-core/src/test/test-conf/TestMRUCache.ccf
new file mode 100644
index 0000000..941f689
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestMRUCache.ccf
@@ -0,0 +1,45 @@
+# 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.
+# JCS Config for unit testing, just a simple memory only cache, with 0 max size
+# with the memory shrinker on.
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.mru.MRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=1
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# Region defined that uses the MRU
+jcs.region.mruDefined=
+jcs.region.mruDefined.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.mruDefined.cacheattributes.MaxObjects=100000
+jcs.region.mruDefined.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.mru.MRUMemoryCache
+
+# Region defined that uses the LRU
+jcs.region.lruDefined=
+jcs.region.lruDefined.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.lruDefined.cacheattributes.MaxObjects=100000
+jcs.region.lruDefined.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
diff --git a/commons-jcs-core/src/test/test-conf/TestMySQLDiskCache.ccf b/commons-jcs-core/src/test/test-conf/TestMySQLDiskCache.ccf
new file mode 100644
index 0000000..3d38e9f
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestMySQLDiskCache.ccf
@@ -0,0 +1,66 @@
+# 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.
+# Cache configuration for the 'TestMySQLDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache.
+# Since it is too much of a burden to expect a mysal instance for unit tests, we are using hsql to
+# verify that the mysql disk cache works.
+
+jcs.default=MYSQL
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# MYSQL disk cache
+jcs.auxiliary.MYSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheFactory
+jcs.auxiliary.MYSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheAttributes
+jcs.auxiliary.MYSQL.attributes.userName=sa
+jcs.auxiliary.MYSQL.attributes.password=
+jcs.auxiliary.MYSQL.attributes.url=jdbc:hsqldb:target/MySQLDiskCacheHsqlBackedUnitTest
+jcs.auxiliary.MYSQL.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.MYSQL.attributes.tableName=JCS_STORE_MYSQL
+jcs.auxiliary.MYSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.MYSQL.attributes.maxActive=15
+jcs.auxiliary.MYSQL.attributes.allowRemoveAll=true
+jcs.auxiliary.MYSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.MYSQL.attributes.optimizationSchedule=12:34:56,02:34:54
+jcs.auxiliary.MYSQL.attributes.balkDuringOptimization=true
+jcs.auxiliary.MYSQL.attributes.EventQueueType=POOLED
+jcs.auxiliary.MYSQL.attributes.EventQueuePoolName=disk_cache_event_queue
+
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteCacheClientServer.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteCacheClientServer.ccf
new file mode 100644
index 0000000..e69dbad
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteCacheClientServer.ccf
@@ -0,0 +1,39 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1102
+
+# client callback port.
+remote.cache.service.port=1301
+remote.cache.rmiSocketFactoryTimeoutMillis=12345
+
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteCacheEventLogging.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteCacheEventLogging.ccf
new file mode 100644
index 0000000..7c2e399
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteCacheEventLogging.ccf
@@ -0,0 +1,47 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+# jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+# jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.cacheeventlogger=org.apache.commons.jcs.engine.logging.MockCacheEventLogger
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteCacheServer.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteCacheServer.ccf
new file mode 100644
index 0000000..786ae9f
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteCacheServer.ccf
@@ -0,0 +1,22 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteClient.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteClient.ccf
new file mode 100644
index 0000000..cb53999
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteClient.ccf
@@ -0,0 +1,46 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=-1
+# jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+# jcs.auxiliary.RC.attributes.GetOnly=false
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteHttpCache.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteHttpCache.ccf
new file mode 100644
index 0000000..0e0226d
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteHttpCache.ccf
@@ -0,0 +1,39 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=RC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+## The Http Remote Cache Client
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.http.client.RemoteHttpCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.http.client.RemoteHttpCacheAttributes
+jcs.auxiliary.RC.attributes.url=http://localhost:8000/jcs-app/RemoteCache
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoteServer.ccf b/commons-jcs-core/src/test/test-conf/TestRemoteServer.ccf
new file mode 100644
index 0000000..7678d8e
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoteServer.ccf
@@ -0,0 +1,44 @@
+# 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.
+# #############################################################
+# ################# REMOTE SERVER CONFIG  #####################
+# Registry used to register and provide the IRmiCacheService service.
+registry.host=localhost
+registry.port=1101
+# call back port to local caches.
+remote.cache.service.port=1101
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7000
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs-core/src/test/test-conf/TestRemoval.ccf b/commons-jcs-core/src/test/test-conf/TestRemoval.ccf
new file mode 100644
index 0000000..5c23490
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestRemoval.ccf
@@ -0,0 +1,61 @@
+# 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.
+# JCS Config for unit testing, just a simple memory only cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.system.groupIdCache=
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=10000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.testCache1=
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/TestSimpleEventHandling.ccf b/commons-jcs-core/src/test/test-conf/TestSimpleEventHandling.ccf
new file mode 100644
index 0000000..f2df927
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestSimpleEventHandling.ccf
@@ -0,0 +1,65 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+jcs.region.WithDisk=indexedDiskCache
+jcs.region.WithDisk.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.WithDisk.cacheattributes.MaxObjects=0
+jcs.region.WithDisk.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.WithDisk.elementattributes.IsSpool=true
+
+jcs.region.NoDisk=
+jcs.region.NoDisk.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.NoDisk.cacheattributes.MaxObjects=0
+jcs.region.NoDisk.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.NoDisk.elementattributes.IsSpool=true
+
+jcs.region.DiskButNotAllowed=indexedDiskCache
+jcs.region.DiskButNotAllowed.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.DiskButNotAllowed.cacheattributes.MaxObjects=0
+jcs.region.DiskButNotAllowed.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.DiskButNotAllowed.elementattributes.IsSpool=false
+
+jcs.region.Maxlife=
+jcs.region.Maxlife.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.Maxlife.cacheattributes.MaxObjects=200
+jcs.region.Maxlife.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.Maxlife.elementattributes.IsEternal=false
+jcs.region.Maxlife.elementattributes.MaxLife=2
+
+jcs.region.Idletime=
+jcs.region.Idletime.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.Idletime.cacheattributes.MaxObjects=200
+jcs.region.Idletime.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.Idletime.elementattributes.IsEternal=false
+jcs.region.Idletime.elementattributes.MaxLife=300
+jcs.region.Idletime.elementattributes.IdleTime=1
+
+# #### AUXILIARY CACHES
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/indexed-disk-cache
+
diff --git a/commons-jcs-core/src/test/test-conf/TestSimpleLoad.ccf b/commons-jcs-core/src/test/test-conf/TestSimpleLoad.ccf
new file mode 100644
index 0000000..6dcef12
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestSimpleLoad.ccf
@@ -0,0 +1,28 @@
+# 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.
+# JCS Config for unit testing, just a simple memory only cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.testCache1=
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=20001
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
diff --git a/commons-jcs-core/src/test/test-conf/TestSystemProperties.ccf b/commons-jcs-core/src/test/test-conf/TestSystemProperties.ccf
new file mode 100644
index 0000000..cd5701a
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestSystemProperties.ccf
@@ -0,0 +1,39 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=indexedDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=${MY_SYSTEM_PROPERTY_MAX_SIZE}
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+jcs.region.missing=
+jcs.region.missing.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.missing.cacheattributes.MaxObjects=${NO_SUCH_PROPERTY}
+jcs.region.missing.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# Indexed Disk Cache
+jcs.auxiliary.indexedDiskCache=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.indexedDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.indexedDiskCache.attributes.DiskPath=target/test-sandbox/${MY_SYSTEM_PROPERTY_DISK_DIR}
+
+
diff --git a/commons-jcs-core/src/test/test-conf/TestSystemPropertyUsage.ccf b/commons-jcs-core/src/test/test-conf/TestSystemPropertyUsage.ccf
new file mode 100644
index 0000000..9c68eae
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestSystemPropertyUsage.ccf
@@ -0,0 +1,20 @@
+# 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.
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
diff --git a/commons-jcs-core/src/test/test-conf/TestTCPLateralCache.ccf b/commons-jcs-core/src/test/test-conf/TestTCPLateralCache.ccf
new file mode 100644
index 0000000..230c1cc
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestTCPLateralCache.ccf
@@ -0,0 +1,72 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.testTcpRegion1=LTCP
+jcs.region.testTcpRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testTcpRegion1.cacheattributes.MaxObjects=10000
+jcs.region.testTcpRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# simple Lateral TCP auxiliary
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.SocketTimeOut=1001
+jcs.auxiliary.LTCP.attributes.OpenTimeOut=2002
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/TestTCPLateralCacheConcurrent.ccf b/commons-jcs-core/src/test/test-conf/TestTCPLateralCacheConcurrent.ccf
new file mode 100644
index 0000000..71f4eaa
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestTCPLateralCacheConcurrent.ccf
@@ -0,0 +1,31 @@
+# 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.
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #### AUXILIARY CACHES
+# simple Lateral TCP auxiliary
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+# jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1102
+jcs.auxiliary.LTCP.attributes.AllowGet=true
+
+
diff --git a/commons-jcs-core/src/test/test-conf/TestTCPLateralIssueRemoveCache.ccf b/commons-jcs-core/src/test/test-conf/TestTCPLateralIssueRemoveCache.ccf
new file mode 100644
index 0000000..aee9339
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestTCPLateralIssueRemoveCache.ccf
@@ -0,0 +1,72 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.testTcpRegion1=LTCP
+jcs.region.testTcpRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testTcpRegion1.cacheattributes.MaxObjects=10000
+jcs.region.testTcpRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# simple Lateral TCP auxiliary
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1117
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1118
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=true
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/TestTCPLateralRemoveFilter.ccf b/commons-jcs-core/src/test/test-conf/TestTCPLateralRemoveFilter.ccf
new file mode 100644
index 0000000..fdfaca4
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestTCPLateralRemoveFilter.ccf
@@ -0,0 +1,43 @@
+# 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.
+# Cache configuration for the 'TestDiskCache' test. The memory cache has a
+# a maximum of 100 objects, so objects should get pushed into the disk cache
+
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### CACHE REGIONS FOR TEST
+
+jcs.region.testTcpRegion1=LTCP
+jcs.region.testTcpRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testTcpRegion1.cacheattributes.MaxObjects=10000
+jcs.region.testTcpRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #### AUXILIARY CACHES
+
+# simple Lateral TCP auxiliary
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1117
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=2001
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=true
+jcs.auxiliary.LTCP.attributes.FilterRemoveByHashCode=true
diff --git a/commons-jcs-core/src/test/test-conf/TestThrash.ccf b/commons-jcs-core/src/test/test-conf/TestThrash.ccf
new file mode 100644
index 0000000..f274d8a
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestThrash.ccf
@@ -0,0 +1,20 @@
+# 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.
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=10000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
diff --git a/commons-jcs-core/src/test/test-conf/TestUDPDiscovery.ccf b/commons-jcs-core/src/test/test-conf/TestUDPDiscovery.ccf
new file mode 100644
index 0000000..012d6b3
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestUDPDiscovery.ccf
@@ -0,0 +1,90 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=LTCP
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=LTCP
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+
+jcs.region.testCache2=LTCP2
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=1000
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=300
+jcs.region.testCache2.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLifeSeconds=60000
+jcs.region.testCache2.elementattributes.IsLateral=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Lateral TCp with UDP discovery enabled
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=6780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+# jcs.auxiliary.LTCP.attributes.Receive=true
+
+# Lateral TCP with UDP discovery disabled
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TcpServers=
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP2.attributes.AllowGet=false
+jcs.auxiliary.LTCP2.attributes.UdpDiscoveryHost=228.5.6.8
+jcs.auxiliary.LTCP2.attributes.UdpDiscoveryPort=6780
+jcs.auxiliary.LTCP2.attributes.UdpDiscoveryEnabled=false
+
diff --git a/commons-jcs-core/src/test/test-conf/TestZeroSizeCache.ccf b/commons-jcs-core/src/test/test-conf/TestZeroSizeCache.ccf
new file mode 100644
index 0000000..804d0fe
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/TestZeroSizeCache.ccf
@@ -0,0 +1,33 @@
+# 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.
+# JCS Config for unit testing, just a simple memory only cache, with 0 max size
+# with the memory shrinker on.
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=1
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLifeSeconds=600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs-core/src/test/test-conf/cache.ccf b/commons-jcs-core/src/test/test-conf/cache.ccf
new file mode 100644
index 0000000..80790e5
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/cache.ccf
@@ -0,0 +1,57 @@
+# 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.
+
+# JCS Config for unit testing, just a simple memory only cache
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.testCache1=
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
diff --git a/commons-jcs-core/src/test/test-conf/cache2.ccf b/commons-jcs-core/src/test/test-conf/cache2.ccf
new file mode 100644
index 0000000..4c6fa92
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/cache2.ccf
@@ -0,0 +1,235 @@
+# 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.
+# #############################################################
+# ################# DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=7
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# SYSTEM CACHE
+# should be defined for the storage of group attribute list
+jcs.system.groupIdCache=DC
+jcs.system.groupIdCache.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.system.groupIdCache.cacheattributes.MaxObjects=1000
+jcs.system.groupIdCache.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.system.groupIdCache.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.system.groupIdCache.elementattributes.IsEternal=true
+jcs.system.groupIdCache.elementattributes.MaxLife=3600
+jcs.system.groupIdCache.elementattributes.IdleTime=1800
+jcs.system.groupIdCache.elementattributes.IsSpool=true
+jcs.system.groupIdCache.elementattributes.IsRemote=true
+jcs.system.groupIdCache.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# CACHE REGIONS AVAILABLE ###################
+# Regions preconfirgured for caching
+jcs.region.testCache1=DC,RC
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTimeSeconds=5000
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.cacheattributes.ShrinkerIntervalSeconds=30
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLife=6000
+jcs.region.testCache1.elementattributes.IsLateral=true
+
+jcs.region.testCache2=
+jcs.region.testCache2.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache2.cacheattributes.MaxObjects=100
+jcs.region.testCache2.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache2.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache2.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache2.cacheattributes.ShrinkerIntervalSeconds=6
+jcs.region.testCache2.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache2.elementattributes.IsEternal=false
+jcs.region.testCache2.elementattributes.MaxLife=600
+jcs.region.testCache2.elementattributes.IsSpool=true
+jcs.region.testCache2.elementattributes.IsRemote=true
+jcs.region.testCache2.elementattributes.IsLateral=true
+
+jcs.region.testCache3=
+jcs.region.testCache3.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache3.cacheattributes.MaxObjects=100000
+jcs.region.testCache3.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache3.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache3.cacheattributes.MaxMemoryIdleTimeSeconds=10
+jcs.region.testCache3.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.region.testCache3.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache3.elementattributes.IsEternal=false
+jcs.region.testCache3.elementattributes.MaxLife=3600
+jcs.region.testCache3.elementattributes.IsSpool=true
+jcs.region.testCache3.elementattributes.IsRemote=true
+jcs.region.testCache3.elementattributes.IsLateral=true
+
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+
+# Remote RMI cache without failover
+jcs.auxiliary.RGroup=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RGroup.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RGroup.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RGroup.attributes.RemoteHost=localhost
+jcs.auxiliary.RGroup.attributes.RemotePort=1102
+jcs.auxiliary.RGroup.attributes.GetOnly=true
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=localhost:1102
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+
+# Primary Disk Cache-- faster than the rest because of memory key storage
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/raf
+# new disk cache parameter.
+jcs.auxiliary.DC.attributes.maxKeySize=100000
+jcs.auxiliary.DC.attributes.optimizeAtRemoveCount=300
+
+# Berkeley DB JE
+jcs.auxiliary.JE=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.JE.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.JE.attributes.DiskPath=target/test-sandbox/bdbje-disk-cache-conc
+# the minimum cache size is 1024
+jcs.auxiliary.indexedDiskCache.attributes.CacheSize=1024
+# jcs.auxiliary.indexedDiskCache.attributes.CachePercent=0
+
+# HSQL Disk Cache -- too slow as is
+jcs.auxiliary.HDC=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheFactory
+jcs.auxiliary.HDC.attributes=org.apache.commons.jcs.auxiliary.disk.hsql.HSQLCacheAttributes
+jcs.auxiliary.HDC.attributes.DiskPath=@project_home_f at hsql
+
+# JISP Disk Cache -- save memory with disk key storage
+jcs.auxiliary.JDC=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheFactory
+jcs.auxiliary.JDC.attributes=org.apache.commons.jcs.auxiliary.disk.jisp.JISPCacheAttributes
+jcs.auxiliary.JDC.attributes.DiskPath=@project_home_f at raf
+jcs.auxiliary.JDC.attributes.ClearOnStart=false
+
+# need to make put or invalidate an option
+# just a remove lock to add
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.RemoteHost=10.21.209.150
+jcs.auxiliary.RC.attributes.RemotePort=1102
+# jcs.auxiliary.RC.attributes.LocalPort=1103
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+
+# unreliable
+jcs.auxiliary.LUDP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LUDP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LUDP.attributes.TransmissionTypeName=UDP
+jcs.auxiliary.LUDP.attributes.UdpMulticastAddr=228.5.6.7
+jcs.auxiliary.LUDP.attributes.UdpMulticastPort=6789
+
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=751):PING(timeout=3000):FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+jcs.auxiliary.JG = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheFactory
+jcs.auxiliary.JG.attributes = org.apache.commons.jcs.auxiliary.javagroups.JavaGroupsCacheAttributes
+jcs.auxiliary.JG.attributes.ChannelFactoryClassName = org.javagroups.JChannelFactory
+jcs.auxiliary.JG.attributes.ChannelProperties = UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE
+
+
+# almost complete
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=false
+
+jcs.auxiliary.LTCP2=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP2.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP2.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP2.attributes.TcpServers=localhost:1112
+jcs.auxiliary.LTCP2.attributes.TcpListenerPort=1111
+jcs.auxiliary.LTCP2.attributes.PutOnlyMode=true
+
+jcs.auxiliary.XMLRPC=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.XMLRPC.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.XMLRPC.attributes.TransmissionTypeName=XMLRPC
+jcs.auxiliary.XMLRPC.attributes.HttpServers=localhost:8182
+jcs.auxiliary.XMLRPC.attributes.HttpListenerPort=8181
+jcs.auxiliary.XMLRPC.attributes.PutOnlyMode=false
+
+
+# example of how to configure the http version of the lateral cache
+# not converteed to new cache
+jcs.auxiliary.LCHTTP=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LCHTTP.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LCHTTP.attributes.TransmissionType=HTTP
+jcs.auxiliary.LCHTTP.attributes.httpServers=localhost:8080,localhost:7001,localhost:80
+jcs.auxiliary.LCHTTP.attributes.httpReceiveServlet=/cache/LateralCacheReceiverServlet
+jcs.auxiliary.LCHTTP.attributes.httpDeleteServlet=/cache/DeleteCacheServlet
+
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Default Cache Event Queue thread pool config, used by auxiliaries
+thread_pool.cache_event_queue.useBoundary=false
+# thread_pool.cache_event_queue.boundarySize=2000
+# thread_pool.cache_event_queue.maximumPoolSize=10
+thread_pool.cache_event_queue.minimumPoolSize=5
+thread_pool.cache_event_queue.keepAliveTime=3500
+# thread_pool.cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.cache_event_queue.startUpSize=5
+
+# Remote cache client thread pool config
+thread_pool.remote_cache_client.boundarySize=75
+thread_pool.remote_cache_client.maximumPoolSize=150
+thread_pool.remote_cache_client.minimumPoolSize=4
+thread_pool.remote_cache_client.keepAliveTime=350000
+thread_pool.remote_cache_client.whenBlockedPolicy=RUN
+thread_pool.remote_cache_client.startUpSize=4
+
diff --git a/commons-jcs-core/src/test/test-conf/je.properties b/commons-jcs-core/src/test/test-conf/je.properties
new file mode 100644
index 0000000..ba4ff7b
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/je.properties
@@ -0,0 +1,51 @@
+# 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.
+# Property file for unit test usage. Usually, all
+# unit tests should run w/out a je.properties file, so
+# the test can have total control over its environment.
+# It may be useful to use a property file when debugging.
+# This file should always be checked in with all properties
+# commented out.
+# $Id: je.properties 1590887 2014-04-29 07:07:32Z olamy $
+
+# je.env.forcedYield=false
+
+# java.util.logging.ConsoleHandler.on=true
+# java.util.logging.FileHandler.on=true
+# java.util.logging.level=INFO
+
+# je.env.runINCompressor=true
+# je.compressor.deadlockRetry=3
+# je.compressor.lockTimeout=5000
+
+# je.env.runEvictor=true
+# je.maxMemory defaults to 93% of jdb.maxMemory unless specified
+je.maxMemory=512
+# je.evictor.nodeScanPercentage=25
+# je.evictor.evictionBatchPercentage=25
+
+# je.env.runCheckpointer=true
+# je.checkpointer.deadlockRetry=3
+
+# je.verify.tree.dump=true
+# je.verify.inlist=true
+# je.verify.throw=false
+
+# je.env.runCleaner=true
+# je.cleaner.deadlockRetry=3
+# je.cleaner.lockTimeout=5000
+# je.cleaner.expunge=false
diff --git a/commons-jcs-core/src/test/test-conf/log4j.properties b/commons-jcs-core/src/test/test-conf/log4j.properties
new file mode 100644
index 0000000..18e947f
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/log4j.properties
@@ -0,0 +1,44 @@
+# 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.
+#log4j.rootCategory=INFO, stdout
+log4j.rootCategory=INFO, logfile
+
+log4j.category.org.apache.commons.jcs=INFO
+log4j.category.org.apache.commons.jcs.config=INFO
+log4j.category.org.apache.commons.jcs.engine=INFO
+log4j.category.org.apache.commons.jcs.engine.CacheEventQueueFactory=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.disk.jdbc=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.disk=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.disk.block=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.disk.file=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.remote=INFO
+log4j.category.org.apache.commons.jcs.auxiliary.lateral=INFO
+log4j.category.org.apache.commons.jcs.utils.struct=INFO
+log4j.category.org.apache.commons.jcs.utils.threadpool=INFO
+log4j.category.org.apache.commons.jcs.utils.discovery=INFO
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c{1}] - <%m>%n
+
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.File=target/jcs.log
+log4j.appender.logfile.Append=false
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+# Pattern to output : date priority [category] - <message>line_separator
+log4j.appender.logfile.layout.ConversionPattern=%d %p [%c{1}] - <%m>%n
+
diff --git a/commons-jcs-core/src/test/test-conf/logger.properties b/commons-jcs-core/src/test/test-conf/logger.properties
new file mode 100644
index 0000000..29336b2
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/logger.properties
@@ -0,0 +1,75 @@
+# 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.
+# The LoggerManager creates loggers for entries in this file.
+# This initializes certain logs at the set debugging levels( 0 - 4 )
+# A management tool will force reinitialization and a reread of this file at
+# runtime.  However the primary way to alter runtime loggin levels will be to
+# modify the level of the logger object through the tool.
+# An entry must have a .level entry to be initialized
+# .systemout is N by default, Y will turn it on
+# .maxfilesize -- number of bytes before archiving log
+# .numtocheck -- number of entries before checking to see if it is too big
+# The logroot value is used by default.  This can be overridden with
+# a specific entry
+
+logroot=bin/test/jcs/logs
+
+# the sleepinterval value is how often the writing thread wakes up in ms.
+# Recommend set to 1000 for development servers (so it would write with 1 sec. delay
+# and 10000 for production servers (so it would write every 10 seconds.)
+
+# Min is 5 secs ie 5000
+sleepInterval=1000
+
+# The string buffer size before messages are flushed to disk.
+# Minimum is zero, which flushes every log message to disk asap.
+buffer_capacity=0
+
+access_cacheaccess.level=2
+access_cacheaccess.systemout=y
+access_cacheaccess.maxfilesize=100000
+access_cacheaccess.numtocheck=300
+
+control_cache.level=2
+control_cache.systemout=y
+control_cache.maxfilesize=100000
+control_cache.numtocheck=300
+
+engine_groupcache.level=2
+engine_groupcache.systemout=y
+engine_groupcache.maxfilesize=100000
+engine_groupcache.numtocheck=300
+
+control_cachemanager.level=2
+control_cachemanager.systemout=y
+control_cachemanager.maxfilesize=100000
+control_cachemanager.numtocheck=300
+
+memory_lateralcacheunicaster.level=0
+memory_lateralcacheunicaster.systemout=n
+memory_lateralcacheunicaster.maxfilesize=100000
+memory_lateralcacheunicaster.numtocheck=300
+
+remote_remotecachemanager.level=2
+remote_remotecachemanager.systemout=y
+remote_remotecachemanager.maxfilesize=100000
+remote_remotecachemanager.numtocheck=300
+
+group_remotegroupcacheserver.level=2
+group_remotegroupcacheserver.systemout=y
+group_remotegroupcacheserver.maxfilesize=100000
+group_remotegroupcacheserver.numtocheck=300
diff --git a/commons-jcs-core/src/test/test-conf/thread_pool.properties b/commons-jcs-core/src/test/test-conf/thread_pool.properties
new file mode 100644
index 0000000..cbf5284
--- /dev/null
+++ b/commons-jcs-core/src/test/test-conf/thread_pool.properties
@@ -0,0 +1,72 @@
+# 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 is used for unit testing the thread pool manager.
+# Normally these settings are loaded from the cache.ccf file.
+
+# #############################################################
+# ################# THREAD POOL CONFIGURATION ###################
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=6
+thread_pool.default.keepAliveTime=350000
+# RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=6
+
+# Remote cache client thread pool config
+thread_pool.test1.boundarySize=75
+thread_pool.test1.maximumPoolSize=150
+thread_pool.test1.minimumPoolSize=4
+thread_pool.test1.keepAliveTime=350001
+thread_pool.test1.whenBlockedPolicy=RUN
+thread_pool.test1.startUpSize=4
+
+# max size test thread pool config
+thread_pool.maxtest.boundarySize=72
+thread_pool.maxtest.maximumPoolSize=142
+thread_pool.maxtest.minimumPoolSize=5
+thread_pool.maxtest.keepAliveTime=350002
+thread_pool.maxtest.whenBlockedPolicy=RUN
+thread_pool.maxtest.startUpSize=5
+
+# wait test thread pool config
+thread_pool.waittest.boundarySize=1
+thread_pool.waittest.maximumPoolSize=11
+thread_pool.waittest.minimumPoolSize=1
+thread_pool.waittest.keepAliveTime=1
+thread_pool.waittest.whenBlockedPolicy=WAIT
+thread_pool.waittest.startUpSize=1
+
+# with boundary test thread pool config
+thread_pool.withbound.useBoundary=true
+thread_pool.withbound.boundarySize=1000
+thread_pool.withbound.maximumPoolSize=11
+thread_pool.withbound.minimumPoolSize=1
+thread_pool.withbound.keepAliveTime=1
+thread_pool.withbound.whenBlockedPolicy=WAIT
+thread_pool.withbound.startUpSize=1
+
+
+# no boundary test thread pool config
+thread_pool.nobound.useBoundary=false
+thread_pool.nobound.boundarySize=1000
+thread_pool.nobound.maximumPoolSize=11
+thread_pool.nobound.minimumPoolSize=1
+thread_pool.nobound.keepAliveTime=1
+thread_pool.nobound.whenBlockedPolicy=WAIT
+thread_pool.nobound.startUpSize=1
diff --git a/commons-jcs-dist/LICENSE.txt b/commons-jcs-dist/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/commons-jcs-dist/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/commons-jcs-dist/NOTICE.txt b/commons-jcs-dist/NOTICE.txt
new file mode 100644
index 0000000..13fc80c
--- /dev/null
+++ b/commons-jcs-dist/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
+
diff --git a/commons-jcs-dist/pom.xml b/commons-jcs-dist/pom.xml
new file mode 100644
index 0000000..bf2fa52
--- /dev/null
+++ b/commons-jcs-dist/pom.xml
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>commons-jcs</artifactId>
+    <groupId>org.apache.commons</groupId>
+    <version>2.0-beta-1</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>commons-jcs-dist</artifactId>
+  <packaging>pom</packaging>
+  <name>Apache Commons JCS :: Distribution</name>
+  <description>Creates the Apache Commons JCS multimodule distribution.</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+    <dependency> <!-- no sources or javadoc since src/main/java is empty -->
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-tck</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-extras</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-extras</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-extras</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-openjpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-openjpa</artifactId>
+      <version>${project.version}</version>
+      <classifier>sources</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache-openjpa</artifactId>
+      <version>${project.version}</version>
+      <classifier>javadoc</classifier>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>release</id>
+      <properties>
+        <!-- activate the JSR-107 TCK for the release-->
+        <jcache.tck>true</jcache.tck>
+      </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-assembly-plugin</artifactId>
+            <version>2.4</version>
+            <executions>
+              <execution>
+                <id>create-distribution</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>single</goal>
+                </goals>
+                <configuration>
+                  <descriptors>
+                    <descriptor>src/assembly/bin.xml</descriptor>
+                    <descriptor>src/assembly/src.xml</descriptor>
+                  </descriptors>
+                  <tarLongFileMode>gnu</tarLongFileMode>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <artifactId>maven-install-plugin</artifactId>
+            <configuration>
+              <createChecksum>true</createChecksum>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/commons-jcs-dist/src/assembly/bin.xml b/commons-jcs-dist/src/assembly/bin.xml
new file mode 100644
index 0000000..0f67193
--- /dev/null
+++ b/commons-jcs-dist/src/assembly/bin.xml
@@ -0,0 +1,41 @@
+<!--
+ 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.
+-->
+<assembly>
+    <id>bin</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
+    <includeSiteDirectory>false</includeSiteDirectory>
+    <dependencySets>
+        <dependencySet>
+            <useProjectArtifact>false</useProjectArtifact>
+            <useTransitiveDependencies>false</useTransitiveDependencies>
+        </dependencySet>
+    </dependencySets>
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}/..</directory>
+            <includes>
+                <include>LICENSE.txt</include>
+                <include>NOTICE.txt</include>
+                <include>RELEASE-NOTES.txt</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/commons-jcs-dist/src/assembly/src.xml b/commons-jcs-dist/src/assembly/src.xml
new file mode 100644
index 0000000..2d25c01
--- /dev/null
+++ b/commons-jcs-dist/src/assembly/src.xml
@@ -0,0 +1,40 @@
+<!--
+ 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.
+-->
+<assembly>
+  <id>src</id>
+  <formats>
+    <format>tar.gz</format>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>${project.artifactId}-${project.version}-src</baseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>${project.basedir}/..</directory>
+      <excludes>
+        <exclude>**/*.log</exclude>
+        <exclude>${project.build.directory}/**</exclude>
+        <exclude>.*/**</exclude>
+        <exclude>**/${project.build.directory}/**</exclude>
+        <exclude>**/.*/**</exclude>
+        <exclude>**/KEYS</exclude>
+        <exclude>${project.basedir}/../commons-jcs-sandbox/**</exclude>
+        <exclude>${project.basedir}/../auxiliary-builds/**</exclude>
+        <exclude>${project.basedir}/../src/experimental/**</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+</assembly>
diff --git a/commons-jcs-jcache-extras/LICENSE.txt b/commons-jcs-jcache-extras/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/commons-jcs-jcache-extras/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/commons-jcs-jcache-extras/NOTICE.txt b/commons-jcs-jcache-extras/NOTICE.txt
new file mode 100644
index 0000000..13fc80c
--- /dev/null
+++ b/commons-jcs-jcache-extras/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
+
diff --git a/commons-jcs-jcache-extras/pom.xml b/commons-jcs-jcache-extras/pom.xml
new file mode 100644
index 0000000..3e1b444
--- /dev/null
+++ b/commons-jcs-jcache-extras/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>commons-jcs</artifactId>
+    <groupId>org.apache.commons</groupId>
+    <version>2.0-beta-1</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>commons-jcs-jcache-extras</artifactId>
+  <version>2.0-beta-1</version>
+  <name>Apache Commons JCS :: JCache Extras</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcache_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-atinject_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcdi_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-interceptor_1.1_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-servlet_3.0_spec</artifactId>
+      <version>1.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/AnyLiteral.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/AnyLiteral.java
new file mode 100644
index 0000000..9f9459d
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/AnyLiteral.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.util.AnnotationLiteral;
+
+public class AnyLiteral extends AnnotationLiteral<Any> implements Any
+{
+    public static final AnyLiteral INSTANCE = new AnyLiteral();
+
+    @Override
+    public String toString()
+    {
+        return "@javax.enterprise.inject.Any()";
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheManagerBean.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheManagerBean.java
new file mode 100644
index 0000000..68a9fa6
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheManagerBean.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import javax.cache.CacheManager;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.enterprise.inject.spi.PassivationCapable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.util.Collections.emptySet;
+
+public class CacheManagerBean implements Bean<CacheManager>, PassivationCapable
+{
+    private final Set<Type> types;
+    private final Set<Annotation> qualifiers;
+    private final CacheManager manager;
+    private final String id;
+
+    public CacheManagerBean(final CacheManager cacheManager)
+    {
+        manager = cacheManager;
+        id = getClass().getName() + "-" + hashCode();
+
+        types = new HashSet<Type>();
+        types.add(CacheManager.class);
+        types.add(Object.class);
+
+        qualifiers = new HashSet<Annotation>();
+        qualifiers.add(DefaultLiteral.INSTANCE);
+        qualifiers.add(AnyLiteral.INSTANCE);
+    }
+
+    @Override
+    public Set<Type> getTypes()
+    {
+        return types;
+    }
+
+    @Override
+    public Set<Annotation> getQualifiers()
+    {
+        return qualifiers;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope()
+    {
+        return ApplicationScoped.class;
+    }
+
+    @Override
+    public String getName()
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isNullable()
+    {
+        return false;
+    }
+
+    @Override
+    public Set<InjectionPoint> getInjectionPoints()
+    {
+        return emptySet();
+    }
+
+    @Override
+    public Class<?> getBeanClass()
+    {
+        return CacheManager.class;
+    }
+
+    @Override
+    public Set<Class<? extends Annotation>> getStereotypes()
+    {
+        return emptySet();
+    }
+
+    @Override
+    public boolean isAlternative()
+    {
+        return false;
+    }
+
+    @Override
+    public CacheManager create(CreationalContext<CacheManager> cacheManagerCreationalContext)
+    {
+        return manager;
+    }
+
+    @Override
+    public void destroy(CacheManager cacheManager, CreationalContext<CacheManager> cacheManagerCreationalContext)
+    {
+        manager.close();
+    }
+
+    @Override
+    public String getId()
+    {
+        return id;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheProviderBean.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheProviderBean.java
new file mode 100644
index 0000000..f520664
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/CacheProviderBean.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import javax.cache.spi.CachingProvider;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.enterprise.inject.spi.PassivationCapable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.util.Collections.emptySet;
+
+public class CacheProviderBean implements Bean<CachingProvider>, PassivationCapable
+{
+    private final Set<Type> types;
+    private final Set<Annotation> qualifiers;
+    private final CachingProvider provider;
+    private final String id;
+
+    public CacheProviderBean(final CachingProvider cacheManager)
+    {
+        provider = cacheManager;
+        id = getClass().getName() + "-" + hashCode();
+
+        types = new HashSet<Type>();
+        types.add(CachingProvider.class);
+        types.add(Object.class);
+
+        qualifiers = new HashSet<Annotation>();
+        qualifiers.add(DefaultLiteral.INSTANCE);
+        qualifiers.add(AnyLiteral.INSTANCE);
+    }
+
+    @Override
+    public Set<Type> getTypes()
+    {
+        return types;
+    }
+
+    @Override
+    public Set<Annotation> getQualifiers()
+    {
+        return qualifiers;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope()
+    {
+        return ApplicationScoped.class;
+    }
+
+    @Override
+    public String getName()
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isNullable()
+    {
+        return false;
+    }
+
+    @Override
+    public Set<InjectionPoint> getInjectionPoints()
+    {
+        return emptySet();
+    }
+
+    @Override
+    public Class<?> getBeanClass()
+    {
+        return CachingProvider.class;
+    }
+
+    @Override
+    public Set<Class<? extends Annotation>> getStereotypes()
+    {
+        return emptySet();
+    }
+
+    @Override
+    public boolean isAlternative()
+    {
+        return false;
+    }
+
+    @Override
+    public CachingProvider create(final CreationalContext<CachingProvider> cacheManagerCreationalContext)
+    {
+        return provider;
+    }
+
+    @Override
+    public void destroy(final CachingProvider cacheProvider, final CreationalContext<CachingProvider> cacheManagerCreationalContext)
+    {
+        provider.close();
+    }
+
+    @Override
+    public String getId()
+    {
+        return id;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/DefaultLiteral.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/DefaultLiteral.java
new file mode 100644
index 0000000..aebe476
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/DefaultLiteral.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import javax.enterprise.inject.Default;
+import javax.enterprise.util.AnnotationLiteral;
+
+public class DefaultLiteral extends AnnotationLiteral<Default> implements Default
+{
+    public static final DefaultLiteral INSTANCE = new DefaultLiteral();
+
+    @Override
+    public String toString()
+    {
+        return "@javax.enterprise.inject.Default()";
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtension.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtension.java
new file mode 100644
index 0000000..e0b1076
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtension.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.spi.CachingProvider;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessBean;
+import java.util.Properties;
+
+// add default CacheProvider and CacheManager
+public class ExtraJCacheExtension implements Extension
+{
+    private static final boolean ACTIVATED = "true".equals(System.getProperty("org.apache.jcs.extra.cdi", "true"));
+
+    private boolean cacheManagerFound = false;
+    private boolean cacheProviderFound = false;
+
+    public <A> void processBean(final @Observes ProcessBean<A> processBeanEvent)
+    {
+        if (!ACTIVATED)
+        {
+            return;
+        }
+
+        if (cacheManagerFound && cacheProviderFound)
+        {
+            return;
+        }
+
+        final Bean<A> bean = processBeanEvent.getBean();
+        if (CacheManagerBean.class.isInstance(bean) || CacheProviderBean.class.isInstance(bean))
+        {
+            return;
+        }
+
+        if (!cacheManagerFound)
+        {
+            cacheManagerFound = bean.getTypes().contains(CacheManager.class);
+        }
+        if (!cacheProviderFound)
+        {
+            cacheProviderFound = bean.getTypes().contains(CachingProvider.class);
+        }
+    }
+
+    public void addJCacheBeans(final @Observes AfterBeanDiscovery afterBeanDiscovery)
+    {
+        if (!ACTIVATED)
+        {
+            return;
+        }
+
+        if (cacheManagerFound && cacheProviderFound) {
+            return;
+        }
+
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        if (!cacheManagerFound)
+        {
+            final CacheManager cacheManager = cachingProvider.getCacheManager(
+                    cachingProvider.getDefaultURI(),
+                    cachingProvider.getDefaultClassLoader(),
+                    new Properties());
+            afterBeanDiscovery.addBean(new CacheManagerBean(cacheManager));
+        }
+        if (!cacheProviderFound)
+        {
+            afterBeanDiscovery.addBean(new CacheProviderBean(cachingProvider));
+        }
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/closeable/Closeables.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/closeable/Closeables.java
new file mode 100644
index 0000000..c8d4c96
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/closeable/Closeables.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.closeable;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class Closeables
+{
+    public static void close(final Object... closeables) throws IOException
+    {
+        IOException e = null;
+        for (final Object closeable : closeables)
+        {
+            if (Closeable.class.isInstance(closeable))
+            {
+                try
+                {
+                    Closeable.class.cast(closeable).close();
+                } catch (final IOException ex) {
+                    if (e == null)
+                    {
+                        e = ex;
+                    }
+                }
+            }
+        }
+        if (e != null)
+        {
+            throw e;
+        }
+    }
+
+    private Closeables()
+    {
+        // no-op
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapter.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapter.java
new file mode 100644
index 0000000..c30b0f1
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.loader;
+
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheLoaderException;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class CacheLoaderAdapter<K, V> implements CacheLoader<K, V>, Factory<CacheLoader<K, V>>
+{
+    @Override
+    public Map<K, V> loadAll(final Iterable<? extends K> keys) throws CacheLoaderException
+    {
+        final Map<K, V> result = new HashMap<K, V>();
+        for (final K k : keys)
+        {
+            final V v = load(k);
+            if (v != null)
+            {
+                result.put(k, v);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public CacheLoader<K, V> create()
+    {
+        return this;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoader.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoader.java
new file mode 100644
index 0000000..560f5d1
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoader.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.loader;
+
+import org.apache.commons.jcs.jcache.extras.closeable.Closeables;
+
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheLoaderException;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CompositeCacheLoader<K, V> implements CacheLoader<K, V>, Closeable, Factory<CacheLoader<K, V>>
+{
+    private final CacheLoader<K, V>[] delegates;
+
+    public CompositeCacheLoader(final CacheLoader<K, V>... delegates)
+    {
+        this.delegates = delegates;
+    }
+
+    @Override
+    public V load(final K key) throws CacheLoaderException
+    {
+        for (final CacheLoader<K, V> delegate : delegates)
+        {
+            final V v = delegate.load(key);
+            if (v != null)
+            {
+                return v;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Map<K, V> loadAll(final Iterable<? extends K> keys) throws CacheLoaderException
+    {
+        final Collection<K> list = new ArrayList<K>();
+        for (final K k : keys)
+        {
+            list.add(k);
+        }
+
+        final Map<K, V> result = new HashMap<K, V>();
+        for (final CacheLoader<K, V> delegate : delegates)
+        {
+            final Map<K, V> v = delegate.loadAll(list);
+            if (v != null)
+            {
+                result.putAll(v);
+                list.removeAll(v.keySet());
+                if (list.isEmpty())
+                {
+                    return v;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        Closeables.close(delegates);
+    }
+
+    @Override
+    public CacheLoader<K, V> create()
+    {
+        return this;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/InMemoryResponse.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/InMemoryResponse.java
new file mode 100644
index 0000000..7b8cb63
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/InMemoryResponse.java
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.web;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class InMemoryResponse extends HttpServletResponseWrapper implements Serializable
+{
+    private final OutputStream buffer;
+
+    private final Collection<Cookie> cookies = new CopyOnWriteArraySet<Cookie>();
+    private final Map<String, List<Serializable>> headers = new TreeMap<String, List<Serializable>>(String.CASE_INSENSITIVE_ORDER);
+    private int status = SC_OK;
+    private String contentType = null;
+    private PrintWriter writer;
+    private int contentLength;
+
+    public InMemoryResponse(final HttpServletResponse response, final OutputStream baos)
+    {
+        super(response);
+        this.buffer = baos;
+    }
+
+    private List<Serializable> ensureHeaderExists(final String s)
+    {
+        List<Serializable> values = headers.get(s);
+        if (values == null) {
+            values = new LinkedList<Serializable>();
+            headers.put(s, values);
+        }
+        return values;
+    }
+
+    @Override
+    public void addCookie(final Cookie cookie)
+    {
+        super.addCookie(cookie);
+        cookies.add(cookie);
+    }
+
+    @Override
+    public void addDateHeader(final String s, final long l)
+    {
+        super.addDateHeader(s, l);
+        ensureHeaderExists(s).add(l);
+    }
+
+    @Override
+    public void addHeader(final String s, final String s2)
+    {
+        super.addHeader(s, s2);
+        ensureHeaderExists(s).add(s2);
+    }
+
+    @Override
+    public void addIntHeader(final String s, final int i)
+    {
+        super.addIntHeader(s, i);
+        ensureHeaderExists(s).add(i);
+    }
+
+    @Override
+    public boolean containsHeader(final String s)
+    {
+        return headers.containsKey(s);
+    }
+
+    @Override
+    public String getHeader(final String s)
+    {
+        final List<Serializable> serializables = headers.get(s);
+        if (serializables.isEmpty())
+        {
+            return null;
+        }
+        return serializables.iterator().next().toString();
+    }
+
+    @Override
+    public Collection<String> getHeaderNames()
+    {
+        return headers.keySet();
+    }
+
+    @Override
+    public Collection<String> getHeaders(final String s)
+    {
+        final List<Serializable> serializables = headers.get(s);
+        final Collection<String> strings = new ArrayList<String>(serializables.size());
+        for (final Serializable ser : serializables)
+        {
+            strings.add(ser.toString());
+        }
+        return strings;
+    }
+
+    @Override
+    public int getStatus()
+    {
+        return status;
+    }
+
+    @Override
+    public void sendError(final int i) throws IOException
+    {
+        status = i;
+        super.sendError(i);
+    }
+
+    @Override
+    public void sendError(final int i, final String s) throws IOException
+    {
+        status = i;
+        super.sendError(i, s);
+    }
+
+    @Override
+    public void sendRedirect(final String s) throws IOException
+    {
+        status = SC_MOVED_TEMPORARILY;
+        super.sendRedirect(s);
+    }
+
+    @Override
+    public void setDateHeader(final String s, final long l)
+    {
+        super.setDateHeader(s, l);
+        final List<Serializable> serializables = ensureHeaderExists(s);
+        serializables.clear();
+        serializables.add(l);
+    }
+
+    @Override
+    public void setHeader(final String s, final String s2)
+    {
+        super.setHeader(s, s2);
+        final List<Serializable> serializables = ensureHeaderExists(s);
+        serializables.clear();
+        serializables.add(s2);
+    }
+
+    @Override
+    public void setIntHeader(final String s, final int i)
+    {
+        super.setIntHeader(s, i);
+        final List<Serializable> serializables = ensureHeaderExists(s);
+        serializables.clear();
+        serializables.add(i);
+    }
+
+    @Override
+    public void setStatus(int i)
+    {
+        status = i;
+        super.setStatus(i);
+    }
+
+    @Override
+    public void setStatus(final int i, final String s)
+    {
+        status = i;
+        super.setStatus(i, s);
+    }
+
+    @Override
+    public String getContentType()
+    {
+        return contentType;
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException
+    {
+        return new ServletOutputStream()
+        {
+            @Override
+            public void write(final int b) throws IOException
+            {
+                buffer.write(b);
+            }
+        };
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException
+    {
+        if (writer == null) {
+            writer = new PrintWriter(new OutputStreamWriter(buffer, getCharacterEncoding()), true);
+        }
+        return writer;
+    }
+
+    @Override
+    public void reset()
+    {
+        super.reset();
+        status = SC_OK;
+        headers.clear();
+        cookies.clear();
+        contentType = null;
+        contentLength = 0;
+    }
+
+    @Override
+    public void setContentLength(final int i)
+    {
+        super.setContentLength(i);
+        contentLength = i;
+    }
+
+    @Override
+    public void setContentType(final String s)
+    {
+        contentType = s;
+        super.setContentType(s);
+    }
+
+    @Override
+    public void flushBuffer() throws IOException
+    {
+        if (writer != null)
+        {
+            writer.flush();
+        }
+        else
+        {
+            buffer.flush();
+        }
+        super.flushBuffer();
+    }
+
+    public int getContentLength()
+    {
+        return contentLength;
+    }
+
+    public Collection<Cookie> getCookies()
+    {
+        return cookies;
+    }
+
+    public Map<String, List<Serializable>> getHeaders()
+    {
+        return headers;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilter.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilter.java
new file mode 100644
index 0000000..cdfc644
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilter.java
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.web;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheWriter;
+import javax.cache.spi.CachingProvider;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.zip.GZIPOutputStream;
+
+import static java.util.Collections.list;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+
+public class JCacheFilter implements Filter
+{
+    private Cache<PageKey, Page> cache;
+    private CachingProvider provider;
+    private CacheManager manager;
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException
+    {
+        final ClassLoader classLoader = filterConfig.getServletContext().getClassLoader();
+        provider = Caching.getCachingProvider(classLoader);
+
+        String uri = filterConfig.getInitParameter("configuration");
+        if (uri == null)
+        {
+            uri = provider.getDefaultURI().toString();
+        }
+        final Properties properties = new Properties();
+        for (final String key : list(filterConfig.getInitParameterNames()))
+        {
+            final String value = filterConfig.getInitParameter(key);
+            if (value != null)
+            {
+                properties.put(key, value);
+            }
+        }
+        manager = provider.getCacheManager(URI.create(uri), classLoader, properties);
+
+        String cacheName = filterConfig.getInitParameter("cache-name");
+        if (cacheName == null)
+        {
+            cacheName = JCacheFilter.class.getName();
+        }
+        cache = manager.getCache(cacheName);
+        if (cache == null)
+        {
+            final MutableConfiguration<PageKey, Page> configuration = new MutableConfiguration<PageKey, Page>()
+                    .setStoreByValue(false);
+            configuration.setReadThrough("true".equals(properties.getProperty("read-through", "false")));
+            configuration.setWriteThrough("true".equals(properties.getProperty("write-through", "false")));
+            if (configuration.isReadThrough())
+            {
+                configuration.setCacheLoaderFactory(new FactoryBuilder.ClassFactory<CacheLoader<PageKey, Page>>(properties.getProperty("cache-loader-factory")));
+            }
+            if (configuration.isWriteThrough())
+            {
+                configuration.setCacheWriterFactory(new FactoryBuilder.ClassFactory<CacheWriter<? super PageKey, ? super Page>>(properties.getProperty("cache-writer-factory")));
+            }
+            final String expirtyPolicy = properties.getProperty("expiry-policy-factory");
+            if (expirtyPolicy != null)
+            {
+                configuration.setExpiryPolicyFactory(new FactoryBuilder.ClassFactory<ExpiryPolicy>(expirtyPolicy));
+            }
+            configuration.setManagementEnabled("true".equals(properties.getProperty("management-enabled", "false")));
+            configuration.setStatisticsEnabled("true".equals(properties.getProperty("statistics-enabled", "false")));
+            cache = manager.createCache(cacheName, configuration);
+        }
+    }
+
+    @Override
+    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException
+    {
+        boolean gzip = false;
+        if (HttpServletRequest.class.isInstance(servletRequest))
+        {
+            final Enumeration<String> acceptEncoding = HttpServletRequest.class.cast(servletRequest).getHeaders("Accept-Encoding");
+            while (acceptEncoding != null && acceptEncoding.hasMoreElements())
+            {
+                if ("gzip".equals(acceptEncoding.nextElement()))
+                {
+                    gzip = true;
+                    break;
+                }
+            }
+        }
+
+        final HttpServletResponse httpServletResponse = HttpServletResponse.class.cast(servletResponse);
+        checkResponse(httpServletResponse);
+
+        final PageKey key = new PageKey(key(servletRequest), gzip);
+        Page page = cache.get(key);
+        if (page == null)
+        {
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            final InMemoryResponse response;
+            if (gzip)
+            {
+                response = new InMemoryResponse(httpServletResponse, new GZIPOutputStream(baos));
+            }
+            else
+            {
+                response = new InMemoryResponse(httpServletResponse, baos);
+            }
+            filterChain.doFilter(servletRequest, response);
+            response.flushBuffer();
+
+            page = new Page(
+                    response.getStatus(),
+                    response.getContentType(),
+                    response.getContentLength(),
+                    response.getCookies(),
+                    response.getHeaders(),
+                    baos.toByteArray());
+            cache.put(key, page);
+        }
+
+        if (page.status == SC_OK) {
+            checkResponse(httpServletResponse);
+
+            if (gzip)
+            {
+                httpServletResponse.setHeader("Content-Encoding", "gzip");
+            }
+
+            httpServletResponse.setStatus(page.status);
+            if (page.contentType != null)
+            {
+                httpServletResponse.setContentType(page.contentType);
+            }
+            if (page.contentLength > 0)
+            {
+                httpServletResponse.setContentLength(page.contentLength);
+            }
+            for (final Cookie c : page.cookies)
+            {
+                httpServletResponse.addCookie(c);
+            }
+            for (final Map.Entry<String, List<Serializable>> entry : page.headers.entrySet())
+            {
+                for (final Serializable value : entry.getValue())
+                {
+                    if (Integer.class.isInstance(value))
+                    {
+                        httpServletResponse.addIntHeader(entry.getKey(), Integer.class.cast(value));
+                    }
+                    else if (String.class.isInstance(value))
+                    {
+                        httpServletResponse.addHeader(entry.getKey(), String.class.cast(value));
+                    }
+                    else if (Long.class.isInstance(value))
+                    {
+                        httpServletResponse.addDateHeader(entry.getKey(), Long.class.cast(value));
+                    }
+                }
+            }
+            httpServletResponse.setContentLength(page.out.length);
+            final BufferedOutputStream bos = new BufferedOutputStream(httpServletResponse.getOutputStream());
+            if (page.out.length != 0)
+            {
+                bos.write(page.out);
+            }
+            else
+            {
+                bos.write(new byte[0]);
+            }
+            bos.flush();
+        }
+    }
+
+    protected String key(final ServletRequest servletRequest)
+    {
+        if (HttpServletRequest.class.isInstance(servletRequest))
+        {
+            final HttpServletRequest request = HttpServletRequest.class.cast(servletRequest);
+            return request.getMethod() + '_' + request.getRequestURI() + '_' + request.getQueryString();
+        }
+        return servletRequest.toString();
+    }
+
+    private void checkResponse(final ServletResponse servletResponse)
+    {
+        if (servletResponse.isCommitted()) {
+            throw new IllegalStateException("Response committed");
+        }
+    }
+
+    @Override
+    public void destroy()
+    {
+        if (!cache.isClosed())
+        {
+            cache.close();
+        }
+        if (!manager.isClosed())
+        {
+            manager.close();
+        }
+        provider.close();
+    }
+
+    protected static class PageKey implements Serializable {
+        private final String uri;
+        private boolean gzip;
+
+        public PageKey(final String uri, final boolean gzip)
+        {
+            this.uri = uri;
+            this.gzip = gzip;
+        }
+
+        public void setGzip(final boolean gzip)
+        {
+            this.gzip = gzip;
+        }
+
+        @Override
+        public boolean equals(final Object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass())
+            {
+                return false;
+            }
+
+            final PageKey pageKey = PageKey.class.cast(o);
+            return gzip == pageKey.gzip && uri.equals(pageKey.uri);
+
+        }
+
+        @Override
+        public int hashCode()
+        {
+            int result = uri.hashCode();
+            result = 31 * result + (gzip ? 1 : 0);
+            return result;
+        }
+    }
+
+    protected static class Page implements Serializable {
+        private final int status;
+        private final String contentType;
+        private final int contentLength;
+        private final Collection<Cookie> cookies;
+        private final Map<String, List<Serializable>> headers;
+        private final byte[] out;
+
+        public Page(final int status,
+                    final String contentType, final int contentLength,
+                    final Collection<Cookie> cookies, final Map<String, List<Serializable>> headers,
+                    final byte[] out)
+        {
+            this.status = status;
+            this.contentType = contentType;
+            this.contentLength = contentLength;
+            this.cookies = cookies;
+            this.headers = headers;
+            this.out = out;
+        }
+
+        @Override
+        public boolean equals(final Object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass())
+            {
+                return false;
+            }
+
+            final Page page = Page.class.cast(o);
+            return contentLength == page.contentLength
+                    && status == page.status
+                    && !(contentType != null ? !contentType.equals(page.contentType) : page.contentType != null)
+                    && cookies.equals(page.cookies)
+                    && headers.equals(page.headers)
+                    && Arrays.equals(out, page.out);
+
+        }
+
+        @Override
+        public int hashCode()
+        {
+            int result = status;
+            result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
+            result = 31 * result + contentLength;
+            result = 31 * result + cookies.hashCode();
+            result = 31 * result + headers.hashCode();
+            result = 31 * result + Arrays.hashCode(out);
+            return result;
+        }
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/AsyncCacheWriter.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/AsyncCacheWriter.java
new file mode 100644
index 0000000..53d2530
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/AsyncCacheWriter.java
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.writer;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheWriter;
+import javax.cache.integration.CacheWriterException;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class AsyncCacheWriter<K, V> implements CacheWriter<K, V>, Closeable, Factory<CacheWriter<K, V>>
+{
+    private static final Logger LOGGER = Logger.getLogger(AsyncCacheWriter.class.getName());
+
+    private final CacheWriter<K, V> writer;
+    private final ExecutorService pool;
+
+    public AsyncCacheWriter(final CacheWriter<K, V> delegate, final int poolSize)
+    {
+        writer = delegate;
+        pool = Executors.newFixedThreadPool(
+                poolSize, new DaemonThreadFactory(delegate.getClass().getName() + "-" + delegate.hashCode() + "-"));
+    }
+
+    @Override
+    public void write(final Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException
+    {
+        pool.submit(new ExceptionProtectionRunnable()
+        {
+            @Override
+            public void doRun()
+            {
+                writer.write(entry);
+            }
+        });
+    }
+
+    @Override
+    public void writeAll(final Collection<Cache.Entry<? extends K, ? extends V>> entries) throws CacheWriterException
+    {
+        pool.submit(new ExceptionProtectionRunnable()
+        {
+            @Override
+            public void doRun()
+            {
+                writer.writeAll(entries);
+            }
+        });
+    }
+
+    @Override
+    public void delete(final Object key) throws CacheWriterException
+    {
+        pool.submit(new ExceptionProtectionRunnable()
+        {
+            @Override
+            public void doRun()
+            {
+                writer.delete(key);
+            }
+        });
+    }
+
+    @Override
+    public void deleteAll(final Collection<?> keys) throws CacheWriterException
+    {
+        pool.submit(new ExceptionProtectionRunnable()
+        {
+            @Override
+            public void doRun()
+            {
+                writer.deleteAll(keys);
+            }
+        });
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        final List<Runnable> runnables = pool.shutdownNow();
+        for (final Runnable r : runnables)
+        {
+            r.run();
+        }
+    }
+
+    @Override
+    public CacheWriter<K, V> create()
+    {
+        return this;
+    }
+
+    // avoid dep on impl
+    private static class DaemonThreadFactory implements ThreadFactory
+    {
+        private final AtomicInteger index = new AtomicInteger(1);
+        private final String prefix;
+
+        public DaemonThreadFactory(final String prefix)
+        {
+            this.prefix = prefix;
+        }
+
+        @Override
+        public Thread newThread( final Runnable runner )
+        {
+            final Thread t = new Thread( runner );
+            t.setName(prefix + index.getAndIncrement());
+            t.setDaemon(true);
+            return t;
+        }
+    }
+
+    private static abstract class ExceptionProtectionRunnable implements Runnable
+    {
+        @Override
+        public void run()
+        {
+            try
+            {
+                doRun();
+            }
+            catch (final Exception e)
+            {
+                LOGGER.log(Level.SEVERE, e.getMessage(), e);
+            }
+        }
+
+        protected abstract void doRun();
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapter.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapter.java
new file mode 100644
index 0000000..79b59b9
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.writer;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheWriter;
+import javax.cache.integration.CacheWriterException;
+import java.util.Collection;
+
+public abstract class CacheWriterAdapter<K, V> implements CacheWriter<K, V>, Factory<CacheWriter<K, V>>
+{
+    @Override
+    public void writeAll(final Collection<Cache.Entry<? extends K, ? extends V>> entries) throws CacheWriterException
+    {
+        for (final Cache.Entry<? extends K, ? extends V> entry : entries)
+        {
+            write(entry);
+        }
+    }
+
+    @Override
+    public void deleteAll(final Collection<?> keys) throws CacheWriterException
+    {
+        for (final Object k : keys)
+        {
+            delete(k);
+        }
+    }
+
+    @Override
+    public CacheWriter<K, V> create()
+    {
+        return this;
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriter.java b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriter.java
new file mode 100644
index 0000000..970ab13
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriter.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.writer;
+
+import org.apache.commons.jcs.jcache.extras.closeable.Closeables;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Factory;
+import javax.cache.integration.CacheWriter;
+import javax.cache.integration.CacheWriterException;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collection;
+
+public class CompositeCacheWriter<K, V> implements CacheWriter<K, V>, Closeable, Factory<CacheWriter<K, V>>
+{
+    private final CacheWriter<K, V>[] writers;
+
+    public CompositeCacheWriter(final CacheWriter<K, V>... writers)
+    {
+        this.writers = writers;
+    }
+
+    @Override
+    public void write(final Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException
+    {
+        CacheWriterException e = null;
+        for (final CacheWriter<K, V> writer : writers)
+        {
+            try
+            {
+                writer.write(entry);
+            }
+            catch (final CacheWriterException ex)
+            {
+                if (e == null)
+                {
+                    e = ex;
+                }
+            }
+        }
+        if (e != null)
+        {
+            throw e;
+        }
+    }
+
+    @Override
+    public void writeAll(final Collection<Cache.Entry<? extends K, ? extends V>> entries) throws CacheWriterException
+    {
+        CacheWriterException e = null;
+        for (final CacheWriter<K, V> writer : writers)
+        {
+            try
+            {
+                writer.writeAll(entries);
+            }
+            catch (final CacheWriterException ex)
+            {
+                if (e == null)
+                {
+                    e = ex;
+                }
+            }
+        }
+        if (e != null)
+        {
+            throw e;
+        }
+    }
+
+    @Override
+    public void delete(final Object key) throws CacheWriterException
+    {
+        CacheWriterException e = null;
+        for (final CacheWriter<K, V> writer : writers)
+        {
+            try
+            {
+                writer.delete(key);
+            }
+            catch (final CacheWriterException ex)
+            {
+                if (e == null)
+                {
+                    e = ex;
+                }
+            }
+        }
+        if (e != null)
+        {
+            throw e;
+        }
+    }
+
+    @Override
+    public void deleteAll(final Collection<?> keys) throws CacheWriterException
+    {
+        CacheWriterException e = null;
+        for (final CacheWriter<K, V> writer : writers)
+        {
+            try
+            {
+                writer.deleteAll(keys);
+            }
+            catch (final CacheWriterException ex)
+            {
+                if (e == null)
+                {
+                    e = ex;
+                }
+            }
+        }
+        if (e != null)
+        {
+            throw e;
+        }
+    }
+
+    @Override
+    public CacheWriter<K, V> create()
+    {
+        return this;
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        Closeables.close(writers);
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/commons-jcs-jcache-extras/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..287f237
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1 @@
+org.apache.commons.jcs.jcache.extras.cdi.ExtraJCacheExtension
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/InternalCacheRule.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/InternalCacheRule.java
new file mode 100644
index 0000000..f02c2f3
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/InternalCacheRule.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.CompleteConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.spi.CachingProvider;
+import java.lang.reflect.Field;
+
+// TODO: *if needed* define @CacheDeifnition instead of relying on field types
+public class InternalCacheRule implements TestRule
+{
+    private final Object test;
+
+    public InternalCacheRule(final Object test)
+    {
+        this.test = test;
+    }
+
+    @Override
+    public Statement apply(final Statement base, final Description description)
+    {
+        return new Statement()
+        {
+            @Override
+            public void evaluate() throws Throwable
+            {
+                final CachingProvider provider = Caching.getCachingProvider();
+                final CacheManager manager = provider.getCacheManager();
+                try
+                {
+                    Field cache = null;
+                    CompleteConfiguration<?, ?> config = null;
+                    for (final Field f : test.getClass().getDeclaredFields())
+                    {
+                        if (Cache.class.isAssignableFrom(f.getType()))
+                        {
+                            f.setAccessible(true);
+                            cache = f;
+                        }
+                        else if (Configuration.class.isAssignableFrom(f.getType()))
+                        {
+                            f.setAccessible(true);
+                            config = (CompleteConfiguration<?, ?>) f.get(test);
+                        }
+                    }
+                    if (cache != null)
+                    {
+                        if (config == null)
+                        {
+                            throw new IllegalStateException("Define a Configuration field");
+                        }
+                        cache.set(test, manager.createCache(cache.getName(), config));
+                    }
+                    base.evaluate();
+                }
+                finally
+                {
+                    manager.close();
+                    provider.close();
+                }
+            }
+        };
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtensionTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtensionTest.java
new file mode 100644
index 0000000..e3ae271
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/cdi/ExtraJCacheExtensionTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.cdi;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.container.BeanManagerImpl;
+import org.apache.webbeans.inject.OWBInjector;
+import org.apache.webbeans.spi.ContainerLifecycle;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.cache.CacheManager;
+import javax.cache.spi.CachingProvider;
+import javax.inject.Inject;
+
+import static org.junit.Assert.assertNotNull;
+
+public class ExtraJCacheExtensionTest
+{
+    private static BeanManagerImpl bm;
+    private static ContainerLifecycle lifecycle;
+
+    @BeforeClass
+    public static void startContainer()
+    {
+        final WebBeansContext webBeansContext = WebBeansContext.currentInstance();
+        lifecycle = webBeansContext.getService(ContainerLifecycle.class);
+        lifecycle.startApplication(null);
+        bm = webBeansContext.getBeanManagerImpl();
+    }
+
+    @AfterClass
+    public static void stopContainer()
+    {
+        lifecycle.stopApplication(null);
+    }
+
+    @Before
+    public void inject() throws Exception
+    {
+        OWBInjector.inject(bm, this, bm.createCreationalContext(null));
+    }
+
+    @Inject
+    private BeanWithInjections bean;
+
+    @Test
+    public void defaultCacheManager()
+    {
+        assertNotNull(bean.getMgr());
+    }
+
+    @Test
+    public void defaultCacheProvider()
+    {
+        assertNotNull(bean.getProvider());
+    }
+
+    public static class BeanWithInjections {
+        @Inject
+        private CacheManager mgr;
+
+        @Inject
+        private CachingProvider provider;
+
+        public CacheManager getMgr()
+        {
+            return mgr;
+        }
+
+        public CachingProvider getProvider()
+        {
+            return provider;
+        }
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapterTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapterTest.java
new file mode 100644
index 0000000..3c6767b
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CacheLoaderAdapterTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.loader;
+
+import org.apache.commons.jcs.jcache.extras.InternalCacheRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.integration.CacheLoaderException;
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class CacheLoaderAdapterTest
+{
+    @Rule
+    public final InternalCacheRule rule = new InternalCacheRule(this);
+
+    private final AtomicInteger count = new AtomicInteger(0);
+    private final Configuration<?, ?> config = new MutableConfiguration<String, String>().setStoreByValue(false).setReadThrough(true).setCacheLoaderFactory(new CacheLoaderAdapter<String, String>()
+    {
+        @Override
+        public String load(final String key) throws CacheLoaderException
+        {
+            count.incrementAndGet();
+            return key;
+        }
+    });
+    private Cache<String, String> cache;
+
+    @Test
+    public void checkLoadAll()
+    {
+        assertFalse(cache.iterator().hasNext());
+        assertEquals("foo", cache.get("foo"));
+
+        count.decrementAndGet();
+        cache.loadAll(new HashSet<String>(asList("a", "b")), true, null);
+        int retries = 100;
+        while (retries-- > 0 && count.get() != 2)
+        {
+            try
+            {
+                Thread.sleep(20);
+            }
+            catch (final InterruptedException e)
+            {
+                Thread.interrupted();
+            }
+        }
+        assertEquals(2, count.get());
+        assertEquals("a", cache.get("a"));
+        assertEquals("b", cache.get("b"));
+        assertEquals(2, count.get());
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoaderTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoaderTest.java
new file mode 100644
index 0000000..08d1d7a
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/loader/CompositeCacheLoaderTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.loader;
+
+import org.apache.commons.jcs.jcache.extras.InternalCacheRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.integration.CacheLoaderException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class CompositeCacheLoaderTest
+{
+    @Rule
+    public final InternalCacheRule rule = new InternalCacheRule(this);
+
+    private final AtomicInteger count = new AtomicInteger(0);
+
+    private final CacheLoaderAdapter<String, String> loader1 = new CacheLoaderAdapter<String, String>()
+    {
+        @Override
+        public String load(String key) throws CacheLoaderException
+        {
+            count.incrementAndGet();
+            return null;
+        }
+    };
+    private final CacheLoaderAdapter<String, String> loader2 = new CacheLoaderAdapter<String, String>()
+    {
+        @Override
+        public String load(String key) throws CacheLoaderException
+        {
+            count.incrementAndGet();
+            return null;
+        }
+    };
+    private final Configuration<?, ?> config = new MutableConfiguration<String, String>()
+            .setStoreByValue(false)
+            .setReadThrough(true)
+            .setCacheLoaderFactory(new CompositeCacheLoader<String, String>(loader1, loader2));
+    private Cache<String, String> cache;
+
+    @Test
+    public void checkComposite()
+    {
+        cache.get("foo");
+        assertEquals(2, count.get());
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilterTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilterTest.java
new file mode 100644
index 0000000..e37e5f2
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/web/JCacheFilterTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.web;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+
+public class JCacheFilterTest
+{
+    private final ThreadLocal<ByteArrayOutputStream> outputStreamAsBytes = new ThreadLocal<ByteArrayOutputStream>() {
+        @Override
+        protected ByteArrayOutputStream initialValue()
+        {
+            return new ByteArrayOutputStream();
+        }
+    };
+    private final ThreadLocal<ServletOutputStream> outputStream = new ThreadLocal<ServletOutputStream>() {
+        @Override
+        protected ServletOutputStream initialValue()
+        {
+            return new ServletOutputStream()
+            {
+                @Override
+                public void write(final int b) throws IOException
+                {
+                    outputStreamAsBytes.get().write(b);
+                }
+            };
+        }
+
+        @Override
+        public void remove()
+        {
+            super.remove();
+            outputStreamAsBytes.remove();
+        }
+    };
+
+    @Before
+    @After
+    public void cleanup() {
+        outputStream.remove();
+    }
+
+    @Test
+    public void testFilterNoOutput() throws ServletException, IOException
+    {
+        final Filter filter = initFilter();
+        final AtomicInteger counter = new AtomicInteger(0);
+        final FilterChain chain = new FilterChain()
+        {
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
+            {
+                counter.incrementAndGet();
+                response.getWriter().write("");
+            }
+        };
+        filter.doFilter(new HttpServletRequestWrapper(newProxy(HttpServletRequest.class)), new HttpServletResponseWrapper(newProxy(HttpServletResponse.class)), chain);
+        assertEquals(1, counter.get());
+        assertEquals("", new String(outputStreamAsBytes.get().toByteArray()));
+        outputStream.remove();
+        filter.doFilter(new HttpServletRequestWrapper(newProxy(HttpServletRequest.class)), new HttpServletResponseWrapper(newProxy(HttpServletResponse.class)), chain);
+        assertEquals(1, counter.get());
+        assertEquals("", new String(outputStreamAsBytes.get().toByteArray()));
+        filter.destroy();
+    }
+
+    @Test
+    public void testFilter() throws ServletException, IOException
+    {
+        final Filter filter = initFilter();
+        final AtomicInteger counter = new AtomicInteger(0);
+        final FilterChain chain = new FilterChain()
+        {
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
+            {
+                counter.incrementAndGet();
+                response.getWriter().write("Hello!");
+            }
+        };
+        filter.doFilter(new HttpServletRequestWrapper(newProxy(HttpServletRequest.class)), new HttpServletResponseWrapper(newProxy(HttpServletResponse.class)), chain);
+        assertEquals(1, counter.get());
+        assertEquals("Hello!", new String(outputStreamAsBytes.get().toByteArray()));
+        outputStream.remove();
+        filter.doFilter(new HttpServletRequestWrapper(newProxy(HttpServletRequest.class)), new HttpServletResponseWrapper(newProxy(HttpServletResponse.class)), chain);
+        assertEquals(1, counter.get());
+        assertEquals("Hello!", new String(outputStreamAsBytes.get().toByteArray()));
+        filter.destroy();
+    }
+
+    private JCacheFilter initFilter() throws ServletException
+    {
+        final JCacheFilter filter = new JCacheFilter();
+        filter.init(new FilterConfig()
+        {
+            @Override
+            public String getFilterName()
+            {
+                return null;
+            }
+
+            @Override
+            public ServletContext getServletContext()
+            {
+                return newProxy(ServletContext.class);
+            }
+
+            @Override
+            public String getInitParameter(String name)
+            {
+                return null;
+            }
+
+            @Override
+            public Enumeration<String> getInitParameterNames()
+            {
+                return Collections.<String>enumeration(Collections.<String>emptySet()); // emptyEnumeration() is Java 1.7+
+            }
+        });
+        return filter;
+    }
+
+    private <T> T newProxy(final Class<T> clazz)
+    {
+        return clazz.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{clazz}, new InvocationHandler()
+                        {
+                            @Override
+                            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
+                            {
+                                if (method.getReturnType().getName().equals("boolean"))
+                                {
+                                    return false;
+                                }
+                                if ("getCharacterEncoding".equals(method.getName()))
+                                {
+                                    return "UTF-8";
+                                }
+                                if ("getOutputStream".equals(method.getName()))
+                                {
+                                    return outputStream.get();
+                                }
+                                return null;
+                            }
+                        }
+                )
+        );
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapterTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapterTest.java
new file mode 100644
index 0000000..190edc1
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CacheWriterAdapterTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.writer;
+
+import org.apache.commons.jcs.jcache.extras.InternalCacheRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.integration.CacheWriterException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class CacheWriterAdapterTest
+{
+    @Rule
+    public final InternalCacheRule rule = new InternalCacheRule(this);
+
+    private final Map<String, String> copy = new HashMap<String, String>();
+    private final Configuration<?, ?> config = new MutableConfiguration<String, String>()
+            .setStoreByValue(false).setReadThrough(true)
+            .setCacheWriterFactory(new CacheWriterAdapter<String, String>()
+            {
+                @Override
+                public void write(final Cache.Entry<? extends String, ? extends String> entry) throws CacheWriterException
+                {
+                    copy.put(entry.getKey(), entry.getValue());
+                }
+
+                @Override
+                public void delete(final Object key) throws CacheWriterException
+                {
+                    copy.remove(key);
+                }
+            });
+    private Cache<String, String> cache;
+
+    @Test
+    public void checkWriteAllAndDeleteAll()
+    {
+        assertTrue(copy.isEmpty());
+        assertFalse(cache.iterator().hasNext());
+        cache.put("foo", "bar");
+        assertEquals(1, copy.size());
+        cache.remove("foo");
+        assertTrue(copy.isEmpty());
+
+        cache.putAll(new HashMap<String, String>() {{
+            put("a", "b");
+            put("b", "c");
+        }});
+        assertEquals(2, copy.size());
+        cache.removeAll(new HashSet<String>(asList("a", "b")));
+        assertTrue(copy.isEmpty());
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriterTest.java b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriterTest.java
new file mode 100644
index 0000000..1ba9612
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/java/org/apache/commons/jcs/jcache/extras/writer/CompositeCacheWriterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.extras.writer;
+
+import org.apache.commons.jcs.jcache.extras.InternalCacheRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.integration.CacheWriterException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class CompositeCacheWriterTest
+{
+    @Rule
+    public final InternalCacheRule rule = new InternalCacheRule(this);
+
+
+    private final Map<String, String> copy1 = new HashMap<String, String>();
+    private final Map<String, String> copy2 = new HashMap<String, String>();
+
+    private final CacheWriterAdapter<String, String> writer1 = new CacheWriterAdapter<String, String>()
+    {
+        @Override
+        public void write(final Cache.Entry<? extends String, ? extends String> entry) throws CacheWriterException
+        {
+            copy1.put(entry.getKey(), entry.getValue());
+        }
+
+        @Override
+        public void delete(final Object key) throws CacheWriterException
+        {
+            copy1.remove(key);
+        }
+    };
+    private final CacheWriterAdapter<String, String> writer2 = new CacheWriterAdapter<String, String>()
+    {
+        @Override
+        public void write(final Cache.Entry<? extends String, ? extends String> entry) throws CacheWriterException
+        {
+            copy2.put(entry.getKey(), entry.getValue());
+        }
+
+        @Override
+        public void delete(final Object key) throws CacheWriterException
+        {
+            copy2.remove(key);
+        }
+    };
+    private final Configuration<?, ?> config = new MutableConfiguration<String, String>()
+            .setStoreByValue(false)
+            .setWriteThrough(true)
+            .setCacheWriterFactory(new CompositeCacheWriter<String, String>(writer1, writer2));
+    private Cache<String, String> cache;
+
+    @Test
+    public void checkComposite()
+    {
+        cache.put("a", "b");
+        assertEquals("b", copy1.get("a"));
+        assertEquals("b", copy2.get("a"));
+        assertEquals(1, copy1.size());
+        assertEquals(1, copy2.size());
+        cache.remove("a");
+        assertTrue(copy1.isEmpty());
+        assertTrue(copy2.isEmpty());
+    }
+}
diff --git a/commons-jcs-jcache-extras/src/test/resources/META-INF/beans.xml b/commons-jcs-jcache-extras/src/test/resources/META-INF/beans.xml
new file mode 100644
index 0000000..e1bbf76
--- /dev/null
+++ b/commons-jcs-jcache-extras/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<beans xmlns="http://java.sun.com/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+                           http://java.sun.com/xml/ns/javaee/beans_1_0.xsd" />
+
+
diff --git a/commons-jcs-jcache-openjpa/LICENSE.txt b/commons-jcs-jcache-openjpa/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/commons-jcs-jcache-openjpa/NOTICE.txt b/commons-jcs-jcache-openjpa/NOTICE.txt
new file mode 100644
index 0000000..13fc80c
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
+
diff --git a/commons-jcs-jcache-openjpa/pom.xml b/commons-jcs-jcache-openjpa/pom.xml
new file mode 100644
index 0000000..9ea89ec
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>commons-jcs</artifactId>
+    <groupId>org.apache.commons</groupId>
+    <version>2.0-beta-1</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>commons-jcs-jcache-openjpa</artifactId>
+  <name>Apache Commons JCS :: JCache OpenJPA</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jpa_2.0_spec</artifactId>
+      <version>1.1</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcache_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openjpa</groupId>
+      <artifactId>openjpa</artifactId>
+      <version>2.3.0</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+      <version>10.11.1.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCache.java b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCache.java
new file mode 100644
index 0000000..5c125c8
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCache.java
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.openjpa;
+
+import org.apache.openjpa.datacache.AbstractDataCache;
+import org.apache.openjpa.datacache.DataCacheManager;
+import org.apache.openjpa.datacache.DataCachePCData;
+import org.apache.openjpa.util.OpenJPAId;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class OpenJPAJCacheDataCache extends AbstractDataCache
+{
+    private static final String OPENJPA_PREFIX = "openjpa.datacache.";
+
+    private final Lock lock = new ReentrantLock();
+    private OpenJPAJCacheDataCacheManager manager;
+
+    @Override
+    public void initialize(final DataCacheManager manager)
+    {
+        super.initialize(manager);
+        this.manager = OpenJPAJCacheDataCacheManager.class.cast(manager);
+    }
+
+    @Override
+    protected DataCachePCData getInternal(final Object oid)
+    {
+        Object result = null;
+        if (OpenJPAId.class.isInstance(oid))
+        {
+            final Class<?> cls = OpenJPAId.class.cast(oid).getType();
+            Cache<Object, Object> cache = manager.getOrCreateCache(OPENJPA_PREFIX, cls.getName());
+            if (cache == null)
+            {
+                return null;
+            }
+            else
+            {
+                result = cache.get(oid);
+            }
+        }
+        else
+        {
+            final CacheManager cacheManager = manager.getCacheManager();
+            for (final String cacheName : cacheManager.getCacheNames())
+            {
+                if (!cacheName.startsWith(OPENJPA_PREFIX))
+                {
+                    continue;
+                }
+
+                result = cacheManager.getCache(cacheName).get(oid);
+                if (result != null)
+                {
+                    break;
+                }
+            }
+        }
+        if (result == null)
+        {
+            return null;
+        }
+        return DataCachePCData.class.cast(result);
+    }
+
+    @Override
+    protected DataCachePCData putInternal(final Object oid, final DataCachePCData pc)
+    {
+        manager.getOrCreateCache(OPENJPA_PREFIX, pc.getType().getName()).put(oid, pc);
+        return pc;
+    }
+
+    @Override
+    protected DataCachePCData removeInternal(final Object oid)
+    {
+        if (OpenJPAId.class.isInstance(oid))
+        {
+            final Object remove = manager.getOrCreateCache(OPENJPA_PREFIX, OpenJPAId.class.cast(oid).getType().getName()).getAndRemove(oid);
+            if (remove == null)
+            {
+                return null;
+            }
+            return DataCachePCData.class.cast(remove);
+        }
+        return null;
+    }
+
+    @Override
+    protected void removeAllInternal(final Class<?> cls, final boolean subclasses)
+    {
+        final String name;
+        if (subclasses)
+        {
+            name = cls.getSuperclass().getName();
+        }
+        else
+        {
+            name = cls.getName();
+        }
+        manager.getOrCreateCache(OPENJPA_PREFIX, name).removeAll();
+    }
+
+    @Override
+    protected void clearInternal()
+    {
+        final CacheManager cacheManager = manager.getCacheManager();
+        for (final String cacheName : cacheManager.getCacheNames())
+        {
+            if (!cacheName.startsWith(OPENJPA_PREFIX))
+            {
+                continue;
+            }
+            cacheManager.getCache(cacheName).clear();
+        }
+    }
+
+    @Override
+    protected boolean pinInternal(final Object oid)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected boolean unpinInternal(final Object oid)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void writeLock()
+    {
+        lock.lock();
+    }
+
+    @Override
+    public void writeUnlock()
+    {
+        lock.unlock();
+    }
+}
diff --git a/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheManager.java b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheManager.java
new file mode 100644
index 0000000..cc9fc85
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheManager.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.openjpa;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.datacache.DataCacheManagerImpl;
+import org.apache.openjpa.lib.conf.ObjectValue;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheWriter;
+import javax.cache.spi.CachingProvider;
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+
+public class OpenJPAJCacheDataCacheManager extends DataCacheManagerImpl
+{
+    private CachingProvider provider;
+    private CacheManager cacheManager;
+
+    @Override
+    public void initialize(final OpenJPAConfiguration conf, final ObjectValue dataCache, final ObjectValue queryCache)
+    {
+        super.initialize(conf, dataCache, queryCache);
+        provider = Caching.getCachingProvider();
+
+        final Properties properties = new Properties();
+        final Map<String, Object> props = conf.toProperties(false);
+        if (props != null)
+        {
+            for (final Map.Entry<?, ?> entry : props.entrySet())
+            {
+                if (entry.getKey() != null && entry.getValue() != null)
+                {
+                    properties.setProperty(entry.getKey().toString(), entry.getValue().toString());
+                }
+            }
+        }
+
+        final String uri = properties.getProperty("jcache.uri", provider.getDefaultURI().toString());
+        cacheManager = provider.getCacheManager(URI.create(uri), provider.getDefaultClassLoader(), properties);
+    }
+
+    @Override
+    public void close()
+    {
+        super.close();
+        if (!cacheManager.isClosed())
+        {
+            cacheManager.close();
+        }
+        provider.close();
+    }
+
+    Cache<Object, Object> getOrCreateCache(final String prefix, final String entity)
+    {
+        final String internalName = prefix + entity;
+        Cache<Object, Object> cache = cacheManager.getCache(internalName);
+        if (cache == null)
+        {
+            final Properties properties = cacheManager.getProperties();
+            final MutableConfiguration<Object, Object> configuration = new MutableConfiguration<Object, Object>()
+                    .setStoreByValue("true".equalsIgnoreCase(properties.getProperty("jcache.store-by-value", "false")));
+
+            configuration.setReadThrough("true".equals(properties.getProperty("jcache.read-through", "false")));
+            configuration.setWriteThrough("true".equals(properties.getProperty("jcache.write-through", "false")));
+            if (configuration.isReadThrough())
+            {
+                configuration.setCacheLoaderFactory(new FactoryBuilder.ClassFactory<CacheLoader<Object, Object>>(properties.getProperty("jcache.cache-loader-factory")));
+            }
+            if (configuration.isWriteThrough())
+            {
+                configuration.setCacheWriterFactory(new FactoryBuilder.ClassFactory<CacheWriter<Object, Object>>(properties.getProperty("jcache.cache-writer-factory")));
+            }
+            final String expirtyPolicy = properties.getProperty("jcache.expiry-policy-factory");
+            if (expirtyPolicy != null)
+            {
+                configuration.setExpiryPolicyFactory(new FactoryBuilder.ClassFactory<ExpiryPolicy>(expirtyPolicy));
+            }
+            else
+            {
+                configuration.setExpiryPolicyFactory(new FactoryBuilder.SingletonFactory<ExpiryPolicy>(new CreatedExpiryPolicy(Duration.FIVE_MINUTES)));
+            }
+            configuration.setManagementEnabled("true".equals(properties.getProperty("jcache.management-enabled", "false")));
+            configuration.setStatisticsEnabled("true".equals(properties.getProperty("jcache.statistics-enabled", "false")));
+
+            cache = cacheManager.createCache(internalName, configuration);
+        }
+        return cache;
+    }
+
+    CacheManager getCacheManager()
+    {
+        return cacheManager;
+    }
+}
diff --git a/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDerivation.java b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDerivation.java
new file mode 100644
index 0000000..4990bb0
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDerivation.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.openjpa;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
+import org.apache.openjpa.event.SingleJVMRemoteCommitProvider;
+import org.apache.openjpa.lib.conf.AbstractProductDerivation;
+import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.conf.ConfigurationProvider;
+import org.apache.openjpa.lib.conf.Configurations;
+
+import java.util.Map;
+
+public class OpenJPAJCacheDerivation extends AbstractProductDerivation
+{
+    private static final String JCACHE_NAME = "jcache";
+
+    @Override
+    public boolean beforeConfigurationLoad(final Configuration conf)
+    {
+        if (OpenJPAConfiguration.class.isInstance(conf)) {
+            final OpenJPAConfigurationImpl oconf = OpenJPAConfigurationImpl.class.cast(conf);
+            oconf.dataCacheManagerPlugin.setAlias(JCACHE_NAME, OpenJPAJCacheDataCacheManager.class.getName());
+            oconf.dataCachePlugin.setAlias(JCACHE_NAME, OpenJPAJCacheDataCache.class.getName());
+            oconf.queryCachePlugin.setAlias(JCACHE_NAME, OpenJPAJCacheQueryCache.class.getName());
+        }
+        return super.beforeConfigurationLoad(conf);
+    }
+
+    @Override
+    public boolean beforeConfigurationConstruct(final ConfigurationProvider cp)
+    {
+        final Map<?, ?> props = cp.getProperties();
+        final Object dcm = Configurations.getProperty("DataCacheManager", props);
+        if (dcm != null && JCACHE_NAME.equals(dcm))
+        {
+            if (Configurations.getProperty("DataCache", props) == null)
+            {
+                cp.addProperty("openjpa.DataCache", JCACHE_NAME);
+            }
+            if (Configurations.getProperty("QueryCache", props) == null)
+            {
+                cp.addProperty("openjpa.QueryCache", JCACHE_NAME);
+            }
+            if (Configurations.getProperty("RemoteCommitProvider", props) == null)
+            {
+                cp.addProperty("openjpa.RemoteCommitProvider", SingleJVMRemoteCommitProvider.class.getName());
+            }
+        }
+        return super.beforeConfigurationConstruct(cp);
+    }
+
+    @Override
+    public int getType()
+    {
+        return TYPE_FEATURE;
+    }
+}
diff --git a/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheQueryCache.java b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheQueryCache.java
new file mode 100644
index 0000000..7ac021a
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/main/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheQueryCache.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.openjpa;
+
+import org.apache.openjpa.datacache.AbstractQueryCache;
+import org.apache.openjpa.datacache.DataCacheManager;
+import org.apache.openjpa.datacache.QueryKey;
+import org.apache.openjpa.datacache.QueryResult;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class OpenJPAJCacheQueryCache extends AbstractQueryCache
+{
+    private static final String OPENJPA_PREFIX = "openjpa.querycache.";
+    private static final String QUERY_CACHE_NAME = "query";
+
+    private final Lock lock = new ReentrantLock();
+    private OpenJPAJCacheDataCacheManager manager;
+
+    @Override
+    public void initialize(final DataCacheManager manager)
+    {
+        super.initialize(manager);
+        this.manager = OpenJPAJCacheDataCacheManager.class.cast(manager);
+    }
+
+    @Override
+    protected void clearInternal()
+    {
+        final CacheManager cacheManager = manager.getCacheManager();
+        for (final String cacheName : cacheManager.getCacheNames())
+        {
+            if (!cacheName.startsWith(OPENJPA_PREFIX))
+            {
+                continue;
+            }
+            cacheManager.getCache(cacheName).clear();
+        }
+    }
+
+    @Override
+    protected Collection keySet()
+    {
+        final Collection<QueryKey> keys = new LinkedList<QueryKey>();
+        for (final Cache.Entry<Object, Object> entry : queryCache())
+        {
+            keys.add(QueryKey.class.cast(entry.getKey()));
+        }
+        return keys;
+    }
+
+    @Override
+    protected QueryResult getInternal(final QueryKey qk)
+    {
+        return QueryResult.class.cast(queryCache().get(qk));
+    }
+
+    private Cache<Object, Object> queryCache()
+    {
+        return manager.getOrCreateCache(OPENJPA_PREFIX, QUERY_CACHE_NAME);
+    }
+
+    @Override
+    protected QueryResult putInternal(final QueryKey qk, final QueryResult oids)
+    {
+        queryCache().put(qk, oids);
+        return oids;
+    }
+
+    @Override
+    protected QueryResult removeInternal(final QueryKey qk)
+    {
+        final Object remove = queryCache().getAndRemove(qk);
+        if (remove == null)
+        {
+            return null;
+        }
+        return QueryResult.class.cast(remove);
+    }
+
+    @Override
+    protected boolean pinInternal(final QueryKey qk)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected boolean unpinInternal(final QueryKey qk)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void writeLock()
+    {
+        lock.lock();
+    }
+
+    @Override
+    public void writeUnlock()
+    {
+        lock.unlock();
+    }
+}
diff --git a/commons-jcs-jcache-openjpa/src/main/resources/META-INF/services/org.apache.openjpa.lib.conf.ProductDerivation b/commons-jcs-jcache-openjpa/src/main/resources/META-INF/services/org.apache.openjpa.lib.conf.ProductDerivation
new file mode 100644
index 0000000..092e0cc
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/main/resources/META-INF/services/org.apache.openjpa.lib.conf.ProductDerivation
@@ -0,0 +1 @@
+org.apache.commons.jcs.jcache.openjpa.OpenJPAJCacheDerivation
diff --git a/commons-jcs-jcache-openjpa/src/test/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheTest.java b/commons-jcs-jcache-openjpa/src/test/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheTest.java
new file mode 100644
index 0000000..28eba88
--- /dev/null
+++ b/commons-jcs-jcache-openjpa/src/test/java/org/apache/commons/jcs/jcache/openjpa/OpenJPAJCacheDataCacheTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.openjpa;
+
+import org.apache.derby.jdbc.EmbeddedDriver;
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.datacache.QueryKey;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.junit.Test;
+
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Persistence;
+import javax.persistence.Query;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class OpenJPAJCacheDataCacheTest
+{
+    private static final Properties props = new Properties()
+    {{
+        setProperty("openjpa.MetaDataFactory", "jpa(Types=" + MyEntity.class.getName() + ")");
+        setProperty("openjpa.ConnectionDriverName", EmbeddedDriver.class.getName());
+        setProperty("openjpa.ConnectionURL", "jdbc:derby:memory:test;create=true");
+        setProperty("openjpa.jdbc.SynchronizeMappings", "buildSchema");
+        setProperty("openjpa.DataCacheManager", "jcache");
+        setProperty("openjpa.RuntimeUnenhancedClasses", "supported");
+
+        // implicit
+        // setProperty("openjpa.DataCache", "jcache");
+        // setProperty("openjpa.QueryCache", "jcache");
+    }};
+
+    @Test
+    public void entity()
+    {
+        final EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-jcache", props);
+        final OpenJPAConfiguration conf = OpenJPAEntityManagerFactorySPI.class.cast(emf).getConfiguration();
+
+        final EntityManager em = emf.createEntityManager();
+
+        final MyEntity entity = new MyEntity();
+        entity.setName("cacheMe1");
+        em.getTransaction().begin();
+        em.persist(entity);
+        em.getTransaction().commit();
+        assertNotNull(conf.getDataCacheManagerInstance().getDataCache("default"));
+
+        assertThat(conf.getDataCacheManagerInstance(), instanceOf(OpenJPAJCacheDataCacheManager.class));
+        assertThat(conf.getDataCacheManagerInstance().getDataCache("default"), instanceOf(OpenJPAJCacheDataCache.class));
+        assertTrue(conf.getDataCacheManagerInstance().getDataCache("default").contains(JPAFacadeHelper.toOpenJPAObjectId(conf.getMetaDataRepositoryInstance().getCachedMetaData(MyEntity.class), entity.getId())));
+
+        em.close();
+
+        emf.close();
+    }
+
+    @Test
+    public void query()
+    {
+        final EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-jcache", props);
+        final OpenJPAConfiguration conf = OpenJPAEntityManagerFactorySPI.class.cast(emf).getConfiguration();
+
+        final EntityManager em = emf.createEntityManager();
+
+        final MyEntity entity = new MyEntity();
+        entity.setName("cacheMe1");
+        em.getTransaction().begin();
+        em.persist(entity);
+        em.getTransaction().commit();
+        final Query query = em.createQuery("select e from OpenJPAJCacheDataCacheTest$MyEntity e where e.id = :id");
+        assertEquals(1, query.setParameter("id", entity.getId()).getResultList().size());
+        assertNotNull(conf.getDataCacheManagerInstance().getDataCache("default"));
+
+        assertThat(conf.getDataCacheManagerInstance(), instanceOf(OpenJPAJCacheDataCacheManager.class));
+        assertThat(conf.getDataCacheManagerInstance().getDataCache("default"), instanceOf(OpenJPAJCacheDataCache.class));
+        assertTrue(conf.getDataCacheManagerInstance().getDataCache("default").contains(JPAFacadeHelper.toOpenJPAObjectId(conf.getMetaDataRepositoryInstance().getCachedMetaData(MyEntity.class), entity.getId())));
+
+        final Map<Object, Object> args = new HashMap<Object, Object>()
+        {{
+                put("id", entity.getId());
+        }};
+        final QueryKey qk = QueryKey.newInstance(query.unwrap(org.apache.openjpa.kernel.Query.class), args);
+        assertNotNull(conf.getDataCacheManagerInstance().getSystemQueryCache().get(qk));
+
+        em.close();
+
+        emf.close();
+    }
+
+    @Entity
+    public static class MyEntity
+    {
+        @Id
+        @GeneratedValue
+        private long id;
+        private String name;
+
+        public long getId()
+        {
+            return id;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public void setName(final String name)
+        {
+            this.name = name;
+        }
+    }
+}
diff --git a/commons-jcs-jcache/LICENSE.txt b/commons-jcs-jcache/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/commons-jcs-jcache/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/commons-jcs-jcache/NOTICE.txt b/commons-jcs-jcache/NOTICE.txt
new file mode 100644
index 0000000..13fc80c
--- /dev/null
+++ b/commons-jcs-jcache/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons JCS
+Copyright 2001-2015 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/). 
+
diff --git a/commons-jcs-jcache/pom.xml b/commons-jcs-jcache/pom.xml
new file mode 100644
index 0000000..cec1ea5
--- /dev/null
+++ b/commons-jcs-jcache/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-jcs</artifactId>
+    <version>2.0-beta-1</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <artifactId>commons-jcs-jcache</artifactId>
+  <name>Apache Commons JCS :: JCache</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcache_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-atinject_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcdi_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-interceptor_1.1_spec</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jcs-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>${commons.lang.version}</version>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.5</version>
+        <executions>
+          <execution>
+            <id>default-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>cdi-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <includes>
+                <include>org/apache/commons/jcs/jcache/cdi/*</include>
+              </includes>
+              <classifier>cdi</classifier>
+            </configuration>
+          </execution>
+          <execution>
+            <id>no-cdi-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <excludes>
+                <exclude>org/apache/commons/jcs/jcache/cdi/</exclude>
+              </excludes>
+              <classifier>nocdi</classifier>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Asserts.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Asserts.java
new file mode 100644
index 0000000..57d746a
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Asserts.java
@@ -0,0 +1,36 @@
+package org.apache.commons.jcs.jcache;
+
+/*
+ * 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.
+ */
+
+public class Asserts
+{
+    public static void assertNotNull(final Object value, final String name)
+    {
+        if (value == null)
+        {
+            throw new NullPointerException(name + " is null");
+        }
+    }
+
+    private Asserts()
+    {
+        // no-op
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/EvictionListener.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/EvictionListener.java
new file mode 100644
index 0000000..a8ecbc2
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/EvictionListener.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEvent;
+import org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler;
+
+public class EvictionListener implements IElementEventHandler
+{
+    private final Statistics stats;
+
+    public EvictionListener(final Statistics statistics)
+    {
+        this.stats = statistics;
+    }
+
+    @Override
+    public void handleElementEvent(final IElementEvent event)
+    {
+        switch (event.getElementEvent())
+        {
+            case EXCEEDED_MAXLIFE_BACKGROUND:
+            case EXCEEDED_MAXLIFE_ONREQUEST:
+            case EXCEEDED_IDLETIME_ONREQUEST:
+            case EXCEEDED_IDLETIME_BACKGROUND:
+                stats.increaseEvictions(1);
+                break;
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterable.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterable.java
new file mode 100644
index 0000000..c4390e9
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterable.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+public class ImmutableIterable<T> implements Iterable<T>
+{
+    private final Collection<T> delegate;
+
+    public ImmutableIterable(final Collection<T> delegate)
+    {
+        this.delegate = new ArrayList<T>(delegate);
+    }
+
+    @Override
+    public Iterator<T> iterator()
+    {
+        return new ImmutableIterator<T>(delegate.iterator());
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterator.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterator.java
new file mode 100644
index 0000000..55c9942
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ImmutableIterator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import java.util.Iterator;
+
+public class ImmutableIterator<T> implements Iterator<T>
+{
+    private final Iterator<T> delegate;
+
+    public ImmutableIterator(final Iterator<T> delegate)
+    {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public boolean hasNext()
+    {
+        return delegate.hasNext();
+    }
+
+    @Override
+    public T next()
+    {
+        return delegate.next();
+    }
+
+    @Override
+    public void remove()
+    {
+        throw new UnsupportedOperationException("this iterator is immutable");
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java
new file mode 100644
index 0000000..ae09e86
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java
@@ -0,0 +1,1009 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import static org.apache.commons.jcs.jcache.Asserts.assertNotNull;
+import static org.apache.commons.jcs.jcache.serialization.Serializations.copy;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.CacheManager;
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.Factory;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.EventType;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.EternalExpiryPolicy;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriter;
+import javax.cache.integration.CacheWriterException;
+import javax.cache.integration.CompletionListener;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.EntryProcessorResult;
+import javax.management.ObjectName;
+
+import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.jcache.jmx.JCSCacheMXBean;
+import org.apache.commons.jcs.jcache.jmx.JCSCacheStatisticsMXBean;
+import org.apache.commons.jcs.jcache.jmx.JMXs;
+import org.apache.commons.jcs.jcache.proxy.ExceptionWrapperHandler;
+import org.apache.commons.jcs.jcache.thread.DaemonThreadFactory;
+import org.apache.commons.jcs.utils.serialization.StandardSerializer;
+
+// TODO: configure serializer
+public class JCSCache<K, V> implements Cache<K, V>
+{
+    private final CompositeCache<K, V> delegate;
+    private final JCSCachingManager manager;
+    private final JCSConfiguration<K, V> config;
+    private final CacheLoader<K, V> loader;
+    private final CacheWriter<? super K, ? super V> writer;
+    private final ExpiryPolicy expiryPolicy;
+    private final ObjectName cacheConfigObjectName;
+    private final ObjectName cacheStatsObjectName;
+    private final String name;
+    private volatile boolean closed = false;
+    private final Map<CacheEntryListenerConfiguration<K, V>, JCSListener<K, V>> listeners = new ConcurrentHashMap<CacheEntryListenerConfiguration<K, V>, JCSListener<K, V>>();
+    private final Statistics statistics = new Statistics();
+    private final ExecutorService pool;
+    private final IElementSerializer serializer; // using json/xml should work as well -> don't force Serializable
+
+
+    public JCSCache(final ClassLoader classLoader, final JCSCachingManager mgr,
+                    final String cacheName, final JCSConfiguration<K, V> configuration,
+                    final Properties properties, final CompositeCache<K, V> cache)
+    {
+        manager = mgr;
+
+        name = cacheName;
+
+        delegate = cache;
+        if (delegate.getElementAttributes() == null)
+        {
+            delegate.setElementAttributes(new ElementAttributes());
+        }
+        delegate.getElementAttributes().addElementEventHandler(new EvictionListener(statistics));
+
+        config = configuration;
+
+        final int poolSize = Integer.parseInt(property(properties, cacheName, "pool.size", "3"));
+        final DaemonThreadFactory threadFactory = new DaemonThreadFactory("JCS-JCache-" + cacheName + "-");
+        pool = poolSize > 0 ? Executors.newFixedThreadPool(poolSize, threadFactory) : Executors.newCachedThreadPool(threadFactory);
+
+        try
+        {
+            serializer = IElementSerializer.class.cast(classLoader.loadClass(property(properties, "serializer", cacheName, StandardSerializer.class.getName())).newInstance());
+        }
+        catch (final Exception e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+
+        final Factory<CacheLoader<K, V>> cacheLoaderFactory = configuration.getCacheLoaderFactory();
+        if (cacheLoaderFactory == null)
+        {
+            loader = NoLoader.INSTANCE;
+        }
+        else
+        {
+            loader = ExceptionWrapperHandler
+                    .newProxy(classLoader, cacheLoaderFactory.create(), CacheLoaderException.class, CacheLoader.class);
+        }
+
+        final Factory<CacheWriter<? super K, ? super V>> cacheWriterFactory = configuration.getCacheWriterFactory();
+        if (cacheWriterFactory == null)
+        {
+            writer = NoWriter.INSTANCE;
+        }
+        else
+        {
+            writer = ExceptionWrapperHandler
+                    .newProxy(classLoader, cacheWriterFactory.create(), CacheWriterException.class, CacheWriter.class);
+        }
+
+        final Factory<ExpiryPolicy> expiryPolicyFactory = configuration.getExpiryPolicyFactory();
+        if (expiryPolicyFactory == null)
+        {
+            expiryPolicy = new EternalExpiryPolicy();
+        }
+        else
+        {
+            expiryPolicy = expiryPolicyFactory.create();
+        }
+
+        for (final CacheEntryListenerConfiguration<K, V> listener : config.getCacheEntryListenerConfigurations())
+        {
+            listeners.put(listener, new JCSListener<K, V>(listener));
+        }
+
+        statistics.setActive(config.isStatisticsEnabled());
+
+        final String mgrStr = manager.getURI().toString().replaceAll(",|:|=|\n", ".");
+        try
+        {
+            cacheConfigObjectName = new ObjectName("javax.cache:type=CacheConfiguration,"
+                    + "CacheManager=" + mgrStr + "," + "Cache=" + name);
+            cacheStatsObjectName = new ObjectName("javax.cache:type=CacheStatistics,"
+                    + "CacheManager=" + mgrStr + "," + "Cache=" + name);
+        }
+        catch (final Exception e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+        if (config.isManagementEnabled())
+        {
+            JMXs.register(cacheConfigObjectName, new JCSCacheMXBean<K, V>(this));
+        }
+        if (config.isStatisticsEnabled())
+        {
+            JMXs.register(cacheStatsObjectName, new JCSCacheStatisticsMXBean(statistics));
+        }
+    }
+
+    private static String property(final Properties properties, final String cacheName, final String name, final String defaultValue)
+    {
+        return properties.getProperty(cacheName + "." + name, properties.getProperty(name, defaultValue));
+    }
+
+    private void assertNotClosed()
+    {
+        if (isClosed())
+        {
+            throw new IllegalStateException("cache closed");
+        }
+    }
+
+    @Override
+    public V get(final K key)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        final long getStart = Times.now(false);
+        return doGetControllingExpiry(getStart, key, true, false, false, true);
+    }
+
+    private V doLoad(final K key, final boolean update, final long now, final boolean propagateLoadException)
+    {
+        V v = null;
+        try
+        {
+            v = loader.load(key);
+        }
+        catch (final CacheLoaderException e)
+        {
+            if (propagateLoadException)
+            {
+                throw e;
+            }
+        }
+        if (v != null)
+        {
+            final Duration duration = update ? expiryPolicy.getExpiryForUpdate() : expiryPolicy.getExpiryForCreation();
+            if (isNotZero(duration))
+            {
+                final ICacheElement<K, V> element = updateElement(key, v, duration);
+                try
+                {
+                    delegate.update(element);
+                }
+                catch (final IOException e)
+                {
+                    throw new CacheException(e);
+                }
+            }
+        }
+        return v;
+    }
+
+    private ICacheElement<K, V> updateElement(final K key, final V v, final Duration duration)
+    {
+        final ICacheElement<K, V> element = new CacheElement<K, V>(name, key, v);
+        final IElementAttributes copy = delegate.getElementAttributes().copy();
+        if (duration != null)
+        {
+            copy.setTimeFactorForMilliseconds(1);
+            final boolean eternal = duration.isEternal();
+            copy.setIsEternal(eternal);
+            if (!eternal)
+            {
+                copy.setIdleTime(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
+            }
+            // MaxLife = -1 to use IdleTime excepted if jcache.ccf asked for something else
+        }
+        element.setElementAttributes(copy);
+        return element;
+    }
+
+    private void touch(final K key, final ICacheElement<K, V> element)
+    {
+        if (config.isStoreByValue())
+        {
+            final K copy = copy(serializer, manager.getClassLoader(), key);
+            try
+            {
+                delegate.update(new CacheElement<K, V>(name, copy, element.getVal(), element.getElementAttributes()));
+            }
+            catch (final IOException e)
+            {
+                throw new CacheException(e);
+            }
+        }
+    }
+
+    @Override
+    public Map<K, V> getAll(final Set<? extends K> keys)
+    {
+        assertNotClosed();
+        for (final K k : keys)
+        {
+            assertNotNull(k, "key");
+        }
+
+        final long now = Times.now(false);
+        final Map<K, V> result = new HashMap<K, V>();
+        for (final K key : keys) {
+            assertNotNull(key, "key");
+
+            final ICacheElement<K, V> elt = delegate.get(key);
+            V val = elt != null ? elt.getVal() : null;
+            if (val == null && config.isReadThrough())
+            {
+                val = doLoad(key, false, now, false);
+                if (val != null)
+                {
+                    result.put(key, val);
+                }
+            }
+            else if (elt != null)
+            {
+                final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
+                if (isNotZero(expiryForAccess))
+                {
+                    touch(key, elt);
+                    result.put(key, val);
+                }
+                else
+                {
+                    expires(key);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public boolean containsKey(final K key)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        return delegate.get(key) != null;
+    }
+
+    @Override
+    public void put(final K key, final V rawValue)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(rawValue, "value");
+
+        final ICacheElement<K, V> oldElt = delegate.get(key);
+        final V old = oldElt != null ? oldElt.getVal() : null;
+
+        final boolean storeByValue = config.isStoreByValue();
+        final V value = storeByValue ? copy(serializer, manager.getClassLoader(), rawValue) : rawValue;
+
+        final boolean created = old == null;
+        final Duration duration = created ? expiryPolicy.getExpiryForCreation() : expiryPolicy.getExpiryForUpdate();
+        if (isNotZero(duration))
+        {
+            final boolean statisticsEnabled = config.isStatisticsEnabled();
+            final long start = Times.now(false);
+
+            final K jcsKey = storeByValue ? copy(serializer, manager.getClassLoader(), key) : key;
+            final ICacheElement<K, V> element = updateElement(jcsKey, value, created ? null : duration); // reuse it to create basic structure
+            if (created && duration != null) { // set maxLife
+                final IElementAttributes copy = element.getElementAttributes();
+                copy.setTimeFactorForMilliseconds(1);
+                final boolean eternal = duration.isEternal();
+                copy.setIsEternal(eternal);
+                if (!eternal)
+                {
+                    copy.setIsEternal(false);
+                    element.getElementAttributes().setMaxLife(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
+                }
+                element.setElementAttributes(copy);
+            }
+            writer.write(new JCSEntry<K, V>(jcsKey, value));
+            try
+            {
+                delegate.update(element);
+            }
+            catch (final IOException e)
+            {
+                throw new CacheException(e);
+            }
+            for (final JCSListener<K, V> listener : listeners.values())
+            {
+                if (created)
+                {
+                    listener.onCreated(Arrays.<CacheEntryEvent<? extends K, ? extends V>> asList(new JCSCacheEntryEvent<K, V>(this,
+                            EventType.CREATED, null, key, value)));
+                }
+                else
+                {
+                    listener.onUpdated(Arrays.<CacheEntryEvent<? extends K, ? extends V>> asList(new JCSCacheEntryEvent<K, V>(this,
+                            EventType.UPDATED, old, key, value)));
+                }
+            }
+
+            if (statisticsEnabled)
+            {
+                statistics.increasePuts(1);
+                statistics.addPutTime(System.currentTimeMillis() - start);
+            }
+        }
+        else
+        {
+            if (!created)
+            {
+                expires(key);
+            }
+        }
+    }
+
+    private static boolean isNotZero(final Duration duration)
+    {
+        return duration == null || !duration.isZero();
+    }
+
+    private void expires(final K cacheKey)
+    {
+        final ICacheElement<K, V> elt = delegate.get(cacheKey);
+        delegate.remove(cacheKey);
+        for (final JCSListener<K, V> listener : listeners.values())
+        {
+            listener.onExpired(Arrays.<CacheEntryEvent<? extends K, ? extends V>> asList(new JCSCacheEntryEvent<K, V>(this,
+                    EventType.REMOVED, null, cacheKey, elt.getVal())));
+        }
+    }
+
+    @Override
+    public V getAndPut(final K key, final V value)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(value, "value");
+        final long getStart = Times.now(false);
+        final V v = doGetControllingExpiry(getStart, key, false, false, true, false);
+        put(key, value);
+        return v;
+    }
+
+    @Override
+    public void putAll(final Map<? extends K, ? extends V> map)
+    {
+        assertNotClosed();
+        final TempStateCacheView<K, V> view = new TempStateCacheView<K, V>(this);
+        for (final Map.Entry<? extends K, ? extends V> e : map.entrySet())
+        {
+            view.put(e.getKey(), e.getValue());
+        }
+        view.merge();
+    }
+
+    @Override
+    public boolean putIfAbsent(final K key, final V value)
+    {
+        if (!containsKey(key))
+        {
+            put(key, value);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean remove(final K key)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+
+        final boolean statisticsEnabled = config.isStatisticsEnabled();
+        final long start = Times.now(!statisticsEnabled);
+
+        writer.delete(key);
+        final K cacheKey = key;
+
+        final ICacheElement<K, V> v = delegate.get(cacheKey);
+        delegate.remove(cacheKey);
+
+        final V value = v != null && v.getVal() != null ? v.getVal() : null;
+        boolean remove = v != null;
+        for (final JCSListener<K, V> listener : listeners.values())
+        {
+            listener.onRemoved(Arrays.<CacheEntryEvent<? extends K, ? extends V>> asList(new JCSCacheEntryEvent<K, V>(this,
+                    EventType.REMOVED, null, key, value)));
+        }
+        if (remove && statisticsEnabled)
+        {
+            statistics.increaseRemovals(1);
+            statistics.addRemoveTime(Times.now(false) - start);
+        }
+        return remove;
+    }
+
+    @Override
+    public boolean remove(final K key, final V oldValue)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(oldValue, "oldValue");
+        final long getStart = Times.now(false);
+        final V v = doGetControllingExpiry(getStart, key, false, false, false, false);
+        final boolean found = v != null;
+        if (found)
+        {
+            if (v.equals(oldValue))
+            {
+                remove(key);
+                return true;
+            }
+            final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
+            if (expiryForAccess != null)
+            {
+                try
+                {
+                    delegate.update(updateElement(key, v, expiryForAccess));
+                }
+                catch (final IOException e)
+                {
+                    throw new CacheException(e);
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public V getAndRemove(final K key)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        final long getStart = Times.now(false);
+        final V v = doGetControllingExpiry(getStart, key, false, false, true, false);
+        remove(key);
+        return v;
+    }
+
+    private V doGetControllingExpiry(final long getStart, final K key, final boolean updateAcess, final boolean forceDoLoad, final boolean skipLoad,
+            final boolean propagateLoadException)
+    {
+        final boolean statisticsEnabled = config.isStatisticsEnabled();
+        final ICacheElement<K, V> elt = delegate.get(key);
+        V v = elt != null ? elt.getVal() : null;
+        if (v == null && (config.isReadThrough() || forceDoLoad))
+        {
+            if (!skipLoad)
+            {
+                v = doLoad(key, false, getStart, propagateLoadException);
+            }
+        }
+        else if (statisticsEnabled)
+        {
+            if (v != null)
+            {
+                statistics.increaseHits(1);
+            }
+            else
+            {
+                statistics.increaseMisses(1);
+            }
+        }
+
+        if (updateAcess && elt != null)
+        {
+            final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
+            if (!isNotZero(expiryForAccess))
+            {
+                expires(key);
+            }
+            else if (expiryForAccess != null && (!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
+            {
+                try
+                {
+                    delegate.update(updateElement(key, elt.getVal(), expiryForAccess));
+                }
+                catch (final IOException e)
+                {
+                    throw new CacheException(e);
+                }
+            }
+        }
+        if (statisticsEnabled && v != null)
+        {
+            statistics.addGetTime(Times.now(false) - getStart);
+        }
+        return v;
+    }
+
+    @Override
+    public boolean replace(final K key, final V oldValue, final V newValue)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(oldValue, "oldValue");
+        assertNotNull(newValue, "newValue");
+        final boolean statisticsEnabled = config.isStatisticsEnabled();
+        final ICacheElement<K, V> elt = delegate.get(key);
+        if (elt != null)
+        {
+            V value = elt.getVal();
+            if (value != null && statisticsEnabled)
+            {
+                statistics.increaseHits(1);
+            }
+            if (value == null && config.isReadThrough())
+            {
+                value = doLoad(key, false, Times.now(false), false);
+            }
+            if (value != null && value.equals(oldValue))
+            {
+                put(key, newValue);
+                return true;
+            }
+            else if (value != null)
+            {
+                final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
+                if (expiryForAccess != null && (!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
+                {
+                    try
+                    {
+                        delegate.update(updateElement(key, elt.getVal(), expiryForAccess));
+                    }
+                    catch (final IOException e)
+                    {
+                        throw new CacheException(e);
+                    }
+                }
+            }
+        }
+        else if (statisticsEnabled)
+        {
+            statistics.increaseMisses(1);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean replace(final K key, final V value)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(value, "value");
+        boolean statisticsEnabled = config.isStatisticsEnabled();
+        if (containsKey(key))
+        {
+            if (statisticsEnabled)
+            {
+                statistics.increaseHits(1);
+            }
+            put(key, value);
+            return true;
+        }
+        else if (statisticsEnabled)
+        {
+            statistics.increaseMisses(1);
+        }
+        return false;
+    }
+
+    @Override
+    public V getAndReplace(final K key, final V value)
+    {
+        assertNotClosed();
+        assertNotNull(key, "key");
+        assertNotNull(value, "value");
+
+        final boolean statisticsEnabled = config.isStatisticsEnabled();
+
+        final ICacheElement<K, V> elt = delegate.get(key);
+        if (elt != null)
+        {
+            V oldValue = elt.getVal();
+            if (oldValue == null && config.isReadThrough())
+            {
+                oldValue = doLoad(key, false, Times.now(false), false);
+            }
+            else if (statisticsEnabled)
+            {
+                statistics.increaseHits(1);
+            }
+            put(key, value);
+            return oldValue;
+        }
+        else if (statisticsEnabled)
+        {
+            statistics.increaseMisses(1);
+        }
+        return null;
+    }
+
+    @Override
+    public void removeAll(final Set<? extends K> keys)
+    {
+        assertNotClosed();
+        assertNotNull(keys, "keys");
+        for (final K k : keys)
+        {
+            remove(k);
+        }
+    }
+
+    @Override
+    public void removeAll()
+    {
+        assertNotClosed();
+        for (final K k : delegate.getKeySet())
+        {
+            remove(k);
+        }
+    }
+
+    @Override
+    public void clear()
+    {
+        assertNotClosed();
+        try
+        {
+            delegate.removeAll();
+        }
+        catch (final IOException e)
+        {
+            throw new CacheException(e);
+        }
+    }
+
+    @Override
+    public <C2 extends Configuration<K, V>> C2 getConfiguration(final Class<C2> clazz)
+    {
+        assertNotClosed();
+        return clazz.cast(config);
+    }
+
+    @Override
+    public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
+    {
+        assertNotClosed();
+        assertNotNull(keys, "keys");
+        for (final K k : keys)
+        {
+            assertNotNull(k, "a key");
+        }
+        pool.submit(new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                doLoadAll(keys, replaceExistingValues, completionListener);
+            }
+        });
+    }
+
+    private void doLoadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
+    {
+        try
+        {
+            final long now = Times.now(false);
+            for (final K k : keys)
+            {
+                if (replaceExistingValues)
+                {
+                    doLoad(k, containsKey(k), now, completionListener != null);
+                    continue;
+                }
+                else if (containsKey(k))
+                {
+                    continue;
+                }
+                doGetControllingExpiry(now, k, true, true, false, completionListener != null);
+            }
+        }
+        catch (final RuntimeException e)
+        {
+            if (completionListener != null)
+            {
+                completionListener.onException(e);
+                return;
+            }
+        }
+        if (completionListener != null)
+        {
+            completionListener.onCompletion();
+        }
+    }
+
+    @Override
+    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
+    {
+        final TempStateCacheView<K, V> view = new TempStateCacheView<K, V>(this);
+        final T t = doInvoke(view, key, entryProcessor, arguments);
+        view.merge();
+        return t;
+    }
+
+    private <T> T doInvoke(final TempStateCacheView<K, V> view, final K key, final EntryProcessor<K, V, T> entryProcessor,
+            final Object... arguments)
+    {
+        assertNotClosed();
+        assertNotNull(entryProcessor, "entryProcessor");
+        assertNotNull(key, "key");
+        try
+        {
+            if (config.isStatisticsEnabled())
+            {
+                if (containsKey(key))
+                {
+                    statistics.increaseHits(1);
+                }
+                else
+                {
+                    statistics.increaseMisses(1);
+                }
+            }
+            return entryProcessor.process(new JCSMutableEntry<K, V>(view, key), arguments);
+        }
+        catch (final Exception ex)
+        {
+            return throwEntryProcessorException(ex);
+        }
+    }
+
+    private static <T> T throwEntryProcessorException(final Exception ex)
+    {
+        if (EntryProcessorException.class.isInstance(ex))
+        {
+            throw EntryProcessorException.class.cast(ex);
+        }
+        throw new EntryProcessorException(ex);
+    }
+
+    @Override
+    public <T> Map<K, EntryProcessorResult<T>> invokeAll(final Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
+            final Object... arguments)
+    {
+        assertNotClosed();
+        assertNotNull(entryProcessor, "entryProcessor");
+        final Map<K, EntryProcessorResult<T>> results = new HashMap<K, EntryProcessorResult<T>>();
+        for (final K k : keys)
+        {
+            try
+            {
+                final T invoke = invoke(k, entryProcessor, arguments);
+                if (invoke != null)
+                {
+                    results.put(k, new EntryProcessorResult<T>()
+                    {
+                        @Override
+                        public T get() throws EntryProcessorException
+                        {
+                            return invoke;
+                        }
+                    });
+                }
+            }
+            catch (final Exception e)
+            {
+                results.put(k, new EntryProcessorResult<T>()
+                {
+                    @Override
+                    public T get() throws EntryProcessorException
+                    {
+                        return throwEntryProcessorException(e);
+                    }
+                });
+            }
+        }
+        return results;
+    }
+
+    @Override
+    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        assertNotClosed();
+        if (listeners.containsKey(cacheEntryListenerConfiguration))
+        {
+            throw new IllegalArgumentException(cacheEntryListenerConfiguration + " already registered");
+        }
+        listeners.put(cacheEntryListenerConfiguration, new JCSListener<K, V>(cacheEntryListenerConfiguration));
+        config.addListener(cacheEntryListenerConfiguration);
+    }
+
+    @Override
+    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        assertNotClosed();
+        listeners.remove(cacheEntryListenerConfiguration);
+        config.removeListener(cacheEntryListenerConfiguration);
+    }
+
+    @Override
+    public Iterator<Entry<K, V>> iterator()
+    {
+        assertNotClosed();
+        final Iterator<K> keys = new HashSet<K>(delegate.getKeySet()).iterator();
+        return new Iterator<Entry<K, V>>()
+        {
+            private K lastKey = null;
+
+            @Override
+            public boolean hasNext()
+            {
+                return keys.hasNext();
+            }
+
+            @Override
+            public Entry<K, V> next()
+            {
+                lastKey = keys.next();
+                return new JCSEntry<K, V>(lastKey, get(lastKey));
+            }
+
+            @Override
+            public void remove()
+            {
+                if (isClosed() || lastKey == null)
+                {
+                    throw new IllegalStateException(isClosed() ? "cache closed" : "call next() before remove()");
+                }
+                JCSCache.this.remove(lastKey);
+            }
+        };
+    }
+
+    @Override
+    public String getName()
+    {
+        assertNotClosed();
+        return name;
+    }
+
+    @Override
+    public CacheManager getCacheManager()
+    {
+        assertNotClosed();
+        return manager;
+    }
+
+    @Override
+    public synchronized void close()
+    {
+        if (isClosed())
+        {
+            return;
+        }
+
+        for (final Runnable task : pool.shutdownNow()) {
+            task.run();
+        }
+
+        manager.release(getName());
+        closed = true;
+        close(loader);
+        close(writer);
+        close(expiryPolicy);
+        for (final JCSListener<K, V> listener : listeners.values())
+        {
+            close(listener);
+        }
+        listeners.clear();
+        JMXs.unregister(cacheConfigObjectName);
+        JMXs.unregister(cacheStatsObjectName);
+        try
+        {
+            delegate.removeAll();
+        }
+        catch (final IOException e)
+        {
+            throw new CacheException(e);
+        }
+    }
+
+    private static void close(final Object potentiallyCloseable)
+    {
+        if (Closeable.class.isInstance(potentiallyCloseable))
+        {
+            Closeable.class.cast(potentiallyCloseable);
+        }
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return closed;
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        assertNotClosed();
+        if (clazz.isInstance(this))
+        {
+            return clazz.cast(this);
+        }
+        if (clazz.isAssignableFrom(Map.class) || clazz.isAssignableFrom(ConcurrentMap.class))
+        {
+            return clazz.cast(delegate);
+        }
+        throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
+    }
+
+    public Statistics getStatistics()
+    {
+        return statistics;
+    }
+
+    public void enableManagement()
+    {
+        config.managementEnabled();
+        JMXs.register(cacheConfigObjectName, new JCSCacheMXBean<K, V>(this));
+    }
+
+    public void disableManagement()
+    {
+        config.managementDisabled();
+        JMXs.unregister(cacheConfigObjectName);
+    }
+
+    public void enableStatistics()
+    {
+        config.statisticsEnabled();
+        statistics.setActive(true);
+        JMXs.register(cacheStatsObjectName, new JCSCacheStatisticsMXBean(statistics));
+    }
+
+    public void disableStatistics()
+    {
+        config.statisticsDisabled();
+        statistics.setActive(false);
+        JMXs.unregister(cacheStatsObjectName);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCacheEntryEvent.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCacheEntryEvent.java
new file mode 100644
index 0000000..54a6f87
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCacheEntryEvent.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.Cache;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.EventType;
+
+public class JCSCacheEntryEvent<K, V> extends CacheEntryEvent<K, V>
+{
+    /** Serial version */
+    private static final long serialVersionUID = 4761272981003897488L;
+
+    private final V old;
+    private final K key;
+    private final V value;
+
+    public JCSCacheEntryEvent(final Cache<K, V> source, final EventType eventType, final V old, final K key, final V value)
+    {
+        super(source, eventType);
+        this.old = old;
+        this.key = key;
+        this.value = value;
+    }
+
+    @Override
+    public V getOldValue()
+    {
+        return old;
+    }
+
+    @Override
+    public boolean isOldValueAvailable()
+    {
+        return old != null;
+    }
+
+    @Override
+    public K getKey()
+    {
+        return key;
+    }
+
+    @Override
+    public V getValue()
+    {
+        return value;
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        if (clazz.isInstance(this))
+        {
+            return clazz.cast(this);
+        }
+        throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java
new file mode 100644
index 0000000..6d098b2
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java
@@ -0,0 +1,339 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import org.apache.commons.jcs.engine.control.CompositeCacheManager;
+import org.apache.commons.jcs.jcache.lang.Subsitutor;
+import org.apache.commons.jcs.jcache.proxy.ClassLoaderAwareCache;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.configuration.Configuration;
+import javax.cache.spi.CachingProvider;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.commons.jcs.jcache.Asserts.assertNotNull;
+
+public class JCSCachingManager implements CacheManager
+{
+    private static final Subsitutor SUBSTITUTOR = Subsitutor.Helper.INSTANCE;
+
+    private static class InternalManager extends CompositeCacheManager
+    {
+        protected static InternalManager create()
+        {
+            return new InternalManager();
+        }
+
+        @Override // needed to call it from JCSCachingManager
+        protected void initialize() {
+            super.initialize();
+        }
+    }
+
+    private final CachingProvider provider;
+    private final URI uri;
+    private final ClassLoader loader;
+    private final Properties properties;
+    private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<String, Cache<?, ?>>();
+    private final Properties configProperties;
+    private volatile boolean closed = false;
+    private InternalManager delegate = InternalManager.create();
+
+    public JCSCachingManager(final CachingProvider provider, final URI uri, final ClassLoader loader, final Properties properties)
+    {
+        this.provider = provider;
+        this.uri = uri;
+        this.loader = loader;
+        this.properties = readConfig(uri, loader, properties);
+        this.configProperties = properties;
+
+        delegate.setJmxName(CompositeCacheManager.JMX_OBJECT_NAME
+                + ",provider=" + provider.hashCode()
+                + ",uri=" + uri.toString().replaceAll(",|:|=|\n", ".")
+                + ",classloader=" + loader.hashCode()
+                + ",properties=" + this.properties.hashCode());
+        delegate.initialize();
+        delegate.configure(this.properties);
+    }
+
+    private Properties readConfig(final URI uri, final ClassLoader loader, final Properties properties) {
+        final Properties props = new Properties();
+        InputStream inStream = null;
+        try
+        {
+            if (JCSCachingProvider.DEFAULT_URI.toString().equals(uri.toString()) || uri.toURL().getProtocol().equals("jcs"))
+            {
+                inStream = loader.getResourceAsStream(uri.getPath());
+            }
+            else
+            {
+                inStream = uri.toURL().openStream();
+            }
+            props.load(inStream);
+        }
+        catch (final IOException e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+        finally
+        {
+            if (inStream != null)
+            {
+                try
+                {
+                    inStream.close();
+                }
+                catch (final IOException e)
+                {
+                    // no-op
+                }
+            }
+        }
+        if (properties != null)
+        {
+            props.putAll(properties);
+        }
+        for (final Map.Entry<Object, Object> entry : props.entrySet()) {
+            if (entry.getValue() == null)
+            {
+                continue;
+            }
+            final String substitute = SUBSTITUTOR.substitute(entry.getValue().toString());
+            if (!substitute.equals(entry.getValue()))
+            {
+                entry.setValue(substitute);
+            }
+        }
+        return props;
+    }
+
+    private void assertNotClosed()
+    {
+        if (isClosed())
+        {
+            throw new IllegalStateException("cache manager closed");
+        }
+    }
+
+    @Override
+    // TODO: use configuration + handle not serializable key/values
+    public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(final String cacheName, final C configuration)
+            throws IllegalArgumentException
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        assertNotNull(configuration, "configuration");
+        final Class<?> keyType = configuration == null ? Object.class : configuration.getKeyType();
+        final Class<?> valueType = configuration == null ? Object.class : configuration.getValueType();
+        if (!caches.containsKey(cacheName))
+        {
+            final Cache<K, V> cache = ClassLoaderAwareCache.wrap(loader,
+                    new JCSCache/*<K, V>*/(
+                            loader, this, cacheName,
+                            new JCSConfiguration/*<K, V>*/(configuration, keyType, valueType),
+                            properties,
+                            delegate.getCache(cacheName)));
+            caches.putIfAbsent(cacheName, cache);
+        }
+        else
+        {
+            throw new javax.cache.CacheException("cache " + cacheName + " already exists");
+        }
+        return (Cache<K, V>) getCache(cacheName, keyType, valueType);
+    }
+
+    @Override
+    public void destroyCache(final String cacheName)
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        final Cache<?, ?> cache = caches.remove(cacheName);
+        if (cache != null && !cache.isClosed())
+        {
+            cache.clear();
+            cache.close();
+        }
+    }
+
+    @Override
+    public void enableManagement(final String cacheName, final boolean enabled)
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        final JCSCache<?, ?> cache = getJCSCache(cacheName);
+        if (cache != null)
+        {
+            if (enabled)
+            {
+                cache.enableManagement();
+            }
+            else
+            {
+                cache.disableManagement();
+            }
+        }
+    }
+
+    private JCSCache<?, ?> getJCSCache(final String cacheName)
+    {
+        final Cache<?, ?> cache = caches.get(cacheName);
+        return JCSCache.class.cast(ClassLoaderAwareCache.getDelegate(cache));
+    }
+
+    @Override
+    public void enableStatistics(final String cacheName, final boolean enabled)
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        final JCSCache<?, ?> cache = getJCSCache(cacheName);
+        if (cache != null)
+        {
+            if (enabled)
+            {
+                cache.enableStatistics();
+            }
+            else
+            {
+                cache.disableStatistics();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void close()
+    {
+        if (isClosed())
+        {
+            return;
+        }
+
+        assertNotClosed();
+        for (final Cache<?, ?> c : caches.values())
+        {
+            c.close();
+        }
+        caches.clear();
+        closed = true;
+        if (JCSCachingProvider.class.isInstance(provider))
+        {
+            JCSCachingProvider.class.cast(provider).remove(this);
+        }
+        delegate.shutDown();
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        if (clazz.isInstance(this))
+        {
+            return clazz.cast(this);
+        }
+        throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return closed;
+    }
+
+    @Override
+    public <K, V> Cache<K, V> getCache(final String cacheName)
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        return (Cache<K, V>) doGetCache(cacheName, Object.class, Object.class);
+    }
+
+    @Override
+    public Iterable<String> getCacheNames()
+    {
+        return new ImmutableIterable<String>(caches.keySet());
+    }
+
+    @Override
+    public <K, V> Cache<K, V> getCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
+    {
+        assertNotClosed();
+        assertNotNull(cacheName, "cacheName");
+        assertNotNull(keyType, "keyType");
+        assertNotNull(valueType, "valueType");
+        try
+        {
+            return doGetCache(cacheName, keyType, valueType);
+        }
+        catch (final IllegalArgumentException iae)
+        {
+            throw new ClassCastException(iae.getMessage());
+        }
+    }
+
+    private <K, V> Cache<K, V> doGetCache(final String cacheName, final Class<K> keyType, final Class<V> valueType)
+    {
+        final Cache<K, V> cache = (Cache<K, V>) caches.get(cacheName);
+        if (cache == null)
+        {
+            return null;
+        }
+
+        final Configuration<K, V> config = cache.getConfiguration(Configuration.class);
+        if ((keyType != null && !config.getKeyType().isAssignableFrom(keyType))
+                || (valueType != null && !config.getValueType().isAssignableFrom(valueType)))
+        {
+            throw new IllegalArgumentException("this cache is <" + config.getKeyType().getName() + ", " + config.getValueType().getName()
+                    + "> " + " and not <" + keyType.getName() + ", " + valueType.getName() + ">");
+        }
+        return cache;
+    }
+
+    @Override
+    public CachingProvider getCachingProvider()
+    {
+        return provider;
+    }
+
+    @Override
+    public URI getURI()
+    {
+        return uri;
+    }
+
+    @Override
+    public ClassLoader getClassLoader()
+    {
+        return loader;
+    }
+
+    @Override
+    public Properties getProperties()
+    {
+        return configProperties;
+    }
+
+    public void release(final String name) {
+        caches.remove(name);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingProvider.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingProvider.java
new file mode 100644
index 0000000..128de3a
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingProvider.java
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.CacheManager;
+import javax.cache.configuration.OptionalFeature;
+import javax.cache.spi.CachingProvider;
+import java.net.URI;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class JCSCachingProvider implements CachingProvider
+{
+    public static final URI DEFAULT_URI = URI.create("jcs://jcache.ccf");
+
+    private final ConcurrentMap<ClassLoader, ConcurrentMap<URI, CacheManager>> cacheManagersByLoader = new ConcurrentHashMap<ClassLoader, ConcurrentMap<URI, CacheManager>>();
+
+    @Override
+    public CacheManager getCacheManager(final URI inUri, final ClassLoader inClassLoader, final Properties properties)
+    {
+        final URI uri = inUri != null ? inUri : getDefaultURI();
+        final ClassLoader classLoader = inClassLoader != null ? inClassLoader : getDefaultClassLoader();
+
+        ConcurrentMap<URI, CacheManager> managers = cacheManagersByLoader.get(classLoader);
+        if (managers == null)
+        {
+            managers = new ConcurrentHashMap<URI, CacheManager>();
+            final ConcurrentMap<URI, CacheManager> existingManagers = cacheManagersByLoader.putIfAbsent(classLoader, managers);
+            if (existingManagers != null)
+            {
+                managers = existingManagers;
+            }
+        }
+
+        CacheManager mgr = managers.get(uri);
+        if (mgr == null)
+        {
+            mgr = new JCSCachingManager(this, uri, classLoader, properties);
+            final CacheManager existing = managers.putIfAbsent(uri, mgr);
+            if (existing != null)
+            {
+                mgr = existing;
+            }
+        }
+
+        return mgr;
+    }
+
+    @Override
+    public URI getDefaultURI()
+    {
+        return DEFAULT_URI;
+    }
+
+    @Override
+    public void close()
+    {
+        for (final Map<URI, CacheManager> v : cacheManagersByLoader.values())
+        {
+            for (final CacheManager m : v.values())
+            {
+                m.close();
+            }
+            v.clear();
+        }
+        cacheManagersByLoader.clear();
+    }
+
+    @Override
+    public void close(final ClassLoader classLoader)
+    {
+        final Map<URI, CacheManager> cacheManagers = cacheManagersByLoader.remove(classLoader);
+        if (cacheManagers != null)
+        {
+            for (final CacheManager mgr : cacheManagers.values())
+            {
+                mgr.close();
+            }
+            cacheManagers.clear();
+        }
+    }
+
+    @Override
+    public void close(final URI uri, final ClassLoader classLoader)
+    {
+        final Map<URI, CacheManager> cacheManagers = cacheManagersByLoader.remove(classLoader);
+        if (cacheManagers != null)
+        {
+            final CacheManager mgr = cacheManagers.remove(uri);
+            if (mgr != null)
+            {
+                mgr.close();
+            }
+        }
+    }
+
+    @Override
+    public CacheManager getCacheManager(final URI uri, final ClassLoader classLoader)
+    {
+        return getCacheManager(uri, classLoader, getDefaultProperties());
+    }
+
+    @Override
+    public CacheManager getCacheManager()
+    {
+        return getCacheManager(getDefaultURI(), getDefaultClassLoader());
+    }
+
+    @Override
+    public boolean isSupported(final OptionalFeature optionalFeature)
+    {
+        return optionalFeature == OptionalFeature.STORE_BY_REFERENCE;
+    }
+
+    @Override
+    public ClassLoader getDefaultClassLoader()
+    {
+        return JCSCachingProvider.class.getClassLoader();
+    }
+
+    @Override
+    public Properties getDefaultProperties()
+    {
+        return new Properties();
+    }
+
+    void remove(final CacheManager mgr)
+    {
+        final ClassLoader classLoader = mgr.getClassLoader();
+        final Map<URI, CacheManager> mgrs = cacheManagersByLoader.get(classLoader);
+        if (mgrs != null)
+        {
+            mgrs.remove(mgr.getURI());
+            if (mgrs.isEmpty())
+            {
+                cacheManagersByLoader.remove(classLoader);
+            }
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSConfiguration.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSConfiguration.java
new file mode 100644
index 0000000..9dffdba
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSConfiguration.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+/**
+ *  Copyright 2003-2010 Terracotta, Inc.
+ *
+ *  Licensed 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.CompleteConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.configuration.Factory;
+import javax.cache.expiry.EternalExpiryPolicy;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class JCSConfiguration<K, V> implements CompleteConfiguration<K, V>
+{
+
+    private final Class<K> keyType;
+    private final Class<V> valueType;
+    private final boolean storeByValue;
+    private final boolean readThrough;
+    private final boolean writeThrough;
+    private final Factory<CacheLoader<K, V>> cacheLoaderFactory;
+    private final Factory<CacheWriter<? super K, ? super V>> cacheWristerFactory;
+    private final Factory<ExpiryPolicy> expiryPolicyFactory;
+    private final Set<CacheEntryListenerConfiguration<K, V>> cacheEntryListenerConfigurations;
+
+    private volatile boolean statisticsEnabled;
+    private volatile boolean managementEnabled;
+
+    public JCSConfiguration(final Configuration<K, V> configuration, final Class<K> keyType, final Class<V> valueType)
+    {
+        this.keyType = keyType;
+        this.valueType = valueType;
+        if (configuration instanceof CompleteConfiguration)
+        {
+            final CompleteConfiguration<K, V> cConfiguration = (CompleteConfiguration<K, V>) configuration;
+            storeByValue = configuration.isStoreByValue();
+            readThrough = cConfiguration.isReadThrough();
+            writeThrough = cConfiguration.isWriteThrough();
+            statisticsEnabled = cConfiguration.isStatisticsEnabled();
+            managementEnabled = cConfiguration.isManagementEnabled();
+            cacheLoaderFactory = cConfiguration.getCacheLoaderFactory();
+            cacheWristerFactory = cConfiguration.getCacheWriterFactory();
+            this.expiryPolicyFactory = cConfiguration.getExpiryPolicyFactory();
+            cacheEntryListenerConfigurations = new HashSet<CacheEntryListenerConfiguration<K, V>>();
+
+            final Iterable<CacheEntryListenerConfiguration<K, V>> entryListenerConfigurations = cConfiguration
+                    .getCacheEntryListenerConfigurations();
+            if (entryListenerConfigurations != null)
+            {
+                for (final CacheEntryListenerConfiguration<K, V> kvCacheEntryListenerConfiguration : entryListenerConfigurations)
+                {
+                    cacheEntryListenerConfigurations.add(kvCacheEntryListenerConfiguration);
+                }
+            }
+        }
+        else
+        {
+            expiryPolicyFactory = EternalExpiryPolicy.factoryOf();
+            storeByValue = true;
+            readThrough = false;
+            writeThrough = false;
+            statisticsEnabled = false;
+            managementEnabled = false;
+            cacheLoaderFactory = null;
+            cacheWristerFactory = null;
+            cacheEntryListenerConfigurations = new HashSet<CacheEntryListenerConfiguration<K, V>>();
+        }
+    }
+
+    @Override
+    public Class<K> getKeyType()
+    {
+        return keyType == null ? (Class<K>) Object.class : keyType;
+    }
+
+    @Override
+    public Class<V> getValueType()
+    {
+        return valueType == null ? (Class<V>) Object.class : valueType;
+    }
+
+    @Override
+    public boolean isStoreByValue()
+    {
+        return storeByValue;
+    }
+
+    @Override
+    public boolean isReadThrough()
+    {
+        return readThrough;
+    }
+
+    @Override
+    public boolean isWriteThrough()
+    {
+        return writeThrough;
+    }
+
+    @Override
+    public boolean isStatisticsEnabled()
+    {
+        return statisticsEnabled;
+    }
+
+    @Override
+    public boolean isManagementEnabled()
+    {
+        return managementEnabled;
+    }
+
+    @Override
+    public Iterable<CacheEntryListenerConfiguration<K, V>> getCacheEntryListenerConfigurations()
+    {
+        return Collections.unmodifiableSet(cacheEntryListenerConfigurations);
+    }
+
+    @Override
+    public Factory<CacheLoader<K, V>> getCacheLoaderFactory()
+    {
+        return cacheLoaderFactory;
+    }
+
+    @Override
+    public Factory<CacheWriter<? super K, ? super V>> getCacheWriterFactory()
+    {
+        return cacheWristerFactory;
+    }
+
+    @Override
+    public Factory<ExpiryPolicy> getExpiryPolicyFactory()
+    {
+        return expiryPolicyFactory;
+    }
+
+    public synchronized void addListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        cacheEntryListenerConfigurations.add(cacheEntryListenerConfiguration);
+    }
+
+    public synchronized void removeListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        cacheEntryListenerConfigurations.remove(cacheEntryListenerConfiguration);
+    }
+
+    public void statisticsEnabled()
+    {
+        statisticsEnabled = true;
+    }
+
+    public void managementEnabled()
+    {
+        managementEnabled = true;
+    }
+
+    public void statisticsDisabled()
+    {
+        statisticsEnabled = false;
+    }
+
+    public void managementDisabled()
+    {
+        managementEnabled = false;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSEntry.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSEntry.java
new file mode 100644
index 0000000..b0382ac
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSEntry.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.Cache;
+
+public class JCSEntry<K, V> implements Cache.Entry<K, V>
+{
+    private final K key;
+    private final V value;
+
+    public JCSEntry(final K key, final V value)
+    {
+        this.key = key;
+        this.value = value;
+    }
+
+    @Override
+    public K getKey()
+    {
+        return key;
+    }
+
+    @Override
+    public V getValue()
+    {
+        return value;
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        if (clazz.isInstance(this))
+        {
+            return clazz.cast(this);
+        }
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSListener.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSListener.java
new file mode 100644
index 0000000..930a533
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSListener.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.Factory;
+import javax.cache.event.CacheEntryCreatedListener;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryEventFilter;
+import javax.cache.event.CacheEntryExpiredListener;
+import javax.cache.event.CacheEntryListener;
+import javax.cache.event.CacheEntryListenerException;
+import javax.cache.event.CacheEntryRemovedListener;
+import javax.cache.event.CacheEntryUpdatedListener;
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JCSListener<K, V> implements Closeable
+{
+    private final boolean oldValue;
+    private final boolean synchronous;
+    private final CacheEntryEventFilter<? super K, ? super V> filter;
+    private final CacheEntryListener<? super K, ? super V> delegate;
+    private final boolean remove;
+    private final boolean expire;
+    private final boolean update;
+    private final boolean create;
+
+    public JCSListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        oldValue = cacheEntryListenerConfiguration.isOldValueRequired();
+        synchronous = cacheEntryListenerConfiguration.isSynchronous();
+
+        final Factory<CacheEntryEventFilter<? super K, ? super V>> filterFactory = cacheEntryListenerConfiguration
+                .getCacheEntryEventFilterFactory();
+        if (filterFactory == null)
+        {
+            filter = NoFilter.INSTANCE;
+        }
+        else
+        {
+            filter = filterFactory.create();
+        }
+
+        delegate = cacheEntryListenerConfiguration.getCacheEntryListenerFactory().create();
+        remove = CacheEntryRemovedListener.class.isInstance(delegate);
+        expire = CacheEntryExpiredListener.class.isInstance(delegate);
+        update = CacheEntryUpdatedListener.class.isInstance(delegate);
+        create = CacheEntryCreatedListener.class.isInstance(delegate);
+    }
+
+    public void onRemoved(final List<CacheEntryEvent<? extends K, ? extends V>> events) throws CacheEntryListenerException
+    {
+        if (remove)
+        {
+            CacheEntryRemovedListener.class.cast(delegate).onRemoved(filter(events));
+        }
+    }
+
+    public void onExpired(final List<CacheEntryEvent<? extends K, ? extends V>> events) throws CacheEntryListenerException
+    {
+        if (expire)
+        {
+            CacheEntryExpiredListener.class.cast(delegate).onExpired(filter(events));
+        }
+    }
+
+    public void onUpdated(final List<CacheEntryEvent<? extends K, ? extends V>> events) throws CacheEntryListenerException
+    {
+        if (update)
+        {
+            CacheEntryUpdatedListener.class.cast(delegate).onUpdated(filter(events));
+        }
+    }
+
+    public void onCreated(final List<CacheEntryEvent<? extends K, ? extends V>> events) throws CacheEntryListenerException
+    {
+        if (create)
+        {
+            CacheEntryCreatedListener.class.cast(delegate).onCreated(filter(events));
+        }
+    }
+
+    private Iterable<CacheEntryEvent<? extends K, ? extends V>> filter(final List<CacheEntryEvent<? extends K, ? extends V>> events)
+    {
+        if (filter == NoFilter.INSTANCE)
+        {
+            return events;
+        }
+
+        final List<CacheEntryEvent<? extends K, ? extends V>> filtered = new ArrayList<CacheEntryEvent<? extends K, ? extends V>>(
+                events.size());
+        for (final CacheEntryEvent<? extends K, ? extends V> event : events)
+        {
+            if (filter.evaluate(event))
+            {
+                filtered.add(event);
+            }
+        }
+        return filtered;
+    }
+
+    @Override
+    public void close()
+    {
+        if (Closeable.class.isInstance(delegate)) {
+            Closeable.class.cast(delegate);
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSMutableEntry.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSMutableEntry.java
new file mode 100644
index 0000000..a90ae85
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSMutableEntry.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.Cache;
+import javax.cache.processor.MutableEntry;
+
+public class JCSMutableEntry<K, V> implements MutableEntry<K, V>
+{
+    private final Cache<K, V> cache;
+    private final K key;
+
+    public JCSMutableEntry(final Cache<K, V> cache, final K key)
+    {
+        this.cache = cache;
+        this.key = key;
+    }
+
+    @Override
+    public boolean exists()
+    {
+        return cache.containsKey(key);
+    }
+
+    @Override
+    public void remove()
+    {
+        cache.remove(key);
+    }
+
+    @Override
+    public void setValue(final V value)
+    {
+        cache.put(key, value);
+    }
+
+    @Override
+    public K getKey()
+    {
+        return key;
+    }
+
+    @Override
+    public V getValue()
+    {
+        return cache.get(key);
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        if (clazz.isInstance(this))
+        {
+            return clazz.cast(this);
+        }
+        throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoFilter.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoFilter.java
new file mode 100644
index 0000000..3315992
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryEventFilter;
+import javax.cache.event.CacheEntryListenerException;
+
+public class NoFilter implements CacheEntryEventFilter<Object, Object>
+{
+    public static final CacheEntryEventFilter<Object, Object> INSTANCE = new NoFilter();
+
+    private NoFilter()
+    {
+        // no-op
+    }
+
+    @Override
+    public boolean evaluate(final CacheEntryEvent<?, ?> event) throws CacheEntryListenerException
+    {
+        return true;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoLoader.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoLoader.java
new file mode 100644
index 0000000..740cac1
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoLoader.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheLoaderException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NoLoader<K, V> implements CacheLoader<K, V>
+{
+    public static final NoLoader INSTANCE = new NoLoader();
+
+    private NoLoader()
+    {
+        // no-op
+    }
+
+    @Override
+    public V load(K key) throws CacheLoaderException
+    {
+        return null;
+    }
+
+    @Override
+    public Map<K, V> loadAll(final Iterable<? extends K> keys) throws CacheLoaderException
+    {
+        final Map<K, V> entries = new HashMap<K, V>();
+        for (final K k : keys)
+        {
+            entries.put(k, null);
+        }
+        return entries;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoWriter.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoWriter.java
new file mode 100644
index 0000000..8aeba89
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/NoWriter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.Cache;
+import javax.cache.integration.CacheWriter;
+import javax.cache.integration.CacheWriterException;
+import java.util.Collection;
+
+public class NoWriter<K, V> implements CacheWriter<K, V>
+{
+    public static final NoWriter INSTANCE = new NoWriter();
+
+    @Override
+    public void write(final Cache.Entry<? extends K, ? extends V> entry) throws CacheWriterException
+    {
+        // no-op
+    }
+
+    @Override
+    public void delete(final Object key) throws CacheWriterException
+    {
+        // no-op
+    }
+
+    @Override
+    public void writeAll(final Collection<Cache.Entry<? extends K, ? extends V>> entries) throws CacheWriterException
+    {
+        for (final Cache.Entry<? extends K, ? extends V> entry : entries)
+        {
+            write(entry);
+        }
+    }
+
+    @Override
+    public void deleteAll(final Collection<?> keys) throws CacheWriterException
+    {
+        for (final Object k : keys)
+        {
+            delete(k);
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Statistics.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Statistics.java
new file mode 100644
index 0000000..61ec0e9
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Statistics.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class Statistics
+{
+    private volatile boolean active = true;
+
+    private final AtomicLong removals = new AtomicLong();
+    private final AtomicLong expiries = new AtomicLong();
+    private final AtomicLong puts = new AtomicLong();
+    private final AtomicLong hits = new AtomicLong();
+    private final AtomicLong misses = new AtomicLong();
+    private final AtomicLong evictions = new AtomicLong();
+    private final AtomicLong putTimeTaken = new AtomicLong();
+    private final AtomicLong getTimeTaken = new AtomicLong();
+    private final AtomicLong removeTimeTaken = new AtomicLong();
+
+    public long getHits()
+    {
+        return hits.get();
+    }
+
+    public long getMisses()
+    {
+        return misses.get();
+    }
+
+    public long getPuts()
+    {
+        return puts.get();
+    }
+
+    public long getRemovals()
+    {
+        return removals.get();
+    }
+
+    public long getEvictions()
+    {
+        return evictions.get();
+    }
+
+    public long getTimeTakenForGets()
+    {
+        return getTimeTaken.get();
+    }
+
+    public long getTimeTakenForPuts()
+    {
+        return putTimeTaken.get();
+    }
+
+    public long getTimeTakenForRemovals()
+    {
+        return removeTimeTaken.get();
+    }
+
+    public void increaseRemovals(final long number)
+    {
+        increment(removals, number);
+    }
+
+    public void increaseExpiries(final long number)
+    {
+        increment(expiries, number);
+    }
+
+    public void increasePuts(final long number)
+    {
+        increment(puts, number);
+    }
+
+    public void increaseHits(final long number)
+    {
+        increment(hits, number);
+    }
+
+    public void increaseMisses(final long number)
+    {
+        increment(misses, number);
+    }
+
+    public void increaseEvictions(final long number)
+    {
+        increment(evictions, number);
+    }
+
+    public void addGetTime(final long duration)
+    {
+        increment(duration, getTimeTaken);
+    }
+
+    public void addPutTime(final long duration)
+    {
+        increment(duration, putTimeTaken);
+    }
+
+    public void addRemoveTime(final long duration)
+    {
+        increment(duration, removeTimeTaken);
+    }
+
+    private void increment(final AtomicLong counter, final long number)
+    {
+        if (!active)
+        {
+            return;
+        }
+        counter.addAndGet(number);
+    }
+
+    private void increment(final long duration, final AtomicLong counter)
+    {
+        if (!active)
+        {
+            return;
+        }
+
+        if (counter.get() < Long.MAX_VALUE - duration)
+        {
+            counter.addAndGet(duration);
+        }
+        else
+        {
+            reset();
+            counter.set(duration);
+        }
+    }
+
+    public void reset()
+    {
+        puts.set(0);
+        misses.set(0);
+        removals.set(0);
+        expiries.set(0);
+        hits.set(0);
+        evictions.set(0);
+        getTimeTaken.set(0);
+        putTimeTaken.set(0);
+        removeTimeTaken.set(0);
+    }
+
+    public void setActive(final boolean active)
+    {
+        this.active = active;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/TempStateCacheView.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/TempStateCacheView.java
new file mode 100644
index 0000000..0b8db82
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/TempStateCacheView.java
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.CompleteConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.integration.CompletionListener;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.EntryProcessorResult;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.commons.jcs.jcache.Asserts.assertNotNull;
+
+// kind of transactional view for a Cache<K, V>, to use with EntryProcessor
+public class TempStateCacheView<K, V> implements Cache<K, V>
+{
+    private final JCSCache<K, V> cache;
+    private final Map<K, V> put = new HashMap<K, V>();
+    private final Collection<K> remove = new LinkedList<K>();
+    private boolean removeAll = false;
+    private boolean clear = false;
+
+    public TempStateCacheView(final JCSCache<K, V> entries)
+    {
+        this.cache = entries;
+    }
+
+    public V get(final K key)
+    {
+        if (ignoreKey(key))
+        {
+            return null;
+        }
+
+        final V v = put.get(key);
+        if (v != null)
+        {
+            return v;
+        }
+
+        // for an EntryProcessor we already incremented stats - to enhance
+        // surely
+        if (cache.getConfiguration(CompleteConfiguration.class).isStatisticsEnabled())
+        {
+            final Statistics statistics = cache.getStatistics();
+            if (cache.containsKey(key))
+            {
+                statistics.increaseHits(-1);
+            }
+            else
+            {
+                statistics.increaseMisses(-1);
+            }
+        }
+        return cache.get(key);
+    }
+
+    private boolean ignoreKey(final K key)
+    {
+        return removeAll || clear || remove.contains(key);
+    }
+
+    public Map<K, V> getAll(final Set<? extends K> keys)
+    {
+        final Map<K, V> v = new HashMap<K, V>(keys.size());
+        final Set<K> missing = new HashSet<K>();
+        for (final K k : keys)
+        {
+            final V value = put.get(k);
+            if (value != null)
+            {
+                v.put(k, value);
+            }
+            else if (!ignoreKey(k))
+            {
+                missing.add(k);
+            }
+        }
+        if (!missing.isEmpty())
+        {
+            v.putAll(cache.getAll(missing));
+        }
+        return v;
+    }
+
+    public boolean containsKey(final K key)
+    {
+        return !ignoreKey(key) && (put.containsKey(key) || cache.containsKey(key));
+    }
+
+    public void loadAll(final Set<? extends K> keys, final boolean replaceExistingValues, final CompletionListener completionListener)
+    {
+        cache.loadAll(keys, replaceExistingValues, completionListener);
+    }
+
+    public void put(final K key, final V value)
+    {
+        assertNotNull(key, "key");
+        assertNotNull(value, "value");
+        put.put(key, value);
+        remove.remove(key);
+    }
+
+    public V getAndPut(final K key, final V value)
+    {
+        final V v = get(key);
+        put(key, value);
+        return v;
+    }
+
+    public void putAll(final Map<? extends K, ? extends V> map)
+    {
+        put.putAll(map);
+        for (final K k : map.keySet())
+        {
+            remove.remove(k);
+        }
+    }
+
+    public boolean putIfAbsent(final K key, final V value)
+    {
+        if (!put.containsKey(key))
+        {
+            put.put(key, value);
+            remove.remove(key);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean remove(final K key)
+    {
+        final boolean noop = put.containsKey(key);
+        put.remove(key);
+        if (!ignoreKey(key))
+        {
+            if (!noop)
+            {
+                remove.add(key);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public boolean remove(final K key, final V oldValue)
+    {
+        put.remove(key);
+        if (!ignoreKey(key) && oldValue.equals(cache.get(key)))
+        {
+            remove.add(key);
+            return true;
+        }
+        return false;
+    }
+
+    public V getAndRemove(final K key)
+    {
+        final V v = get(key);
+        remove.add(key);
+        put.remove(key);
+        return v;
+    }
+
+    public boolean replace(final K key, final V oldValue, final V newValue)
+    {
+        if (oldValue.equals(get(key)))
+        {
+            put(key, newValue);
+            return true;
+        }
+        return false;
+    }
+
+    public boolean replace(final K key, final V value)
+    {
+        if (containsKey(key))
+        {
+            remove(key);
+            return true;
+        }
+        return false;
+    }
+
+    public V getAndReplace(final K key, final V value)
+    {
+        if (containsKey(key))
+        {
+            final V oldValue = get(key);
+            put(key, value);
+            return oldValue;
+        }
+        return null;
+    }
+
+    public void removeAll(final Set<? extends K> keys)
+    {
+        remove.addAll(keys);
+        for (final K k : keys)
+        {
+            put.remove(k);
+        }
+    }
+
+    @Override
+    public void removeAll()
+    {
+        removeAll = true;
+        put.clear();
+        remove.clear();
+    }
+
+    @Override
+    public void clear()
+    {
+        clear = true;
+        put.clear();
+        remove.clear();
+    }
+
+    public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
+    {
+        return cache.getConfiguration(clazz);
+    }
+
+    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
+    {
+        return cache.invoke(key, entryProcessor, arguments);
+    }
+
+    public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, final EntryProcessor<K, V, T> entryProcessor,
+            final Object... arguments)
+    {
+        return cache.invokeAll(keys, entryProcessor, arguments);
+    }
+
+    @Override
+    public String getName()
+    {
+        return cache.getName();
+    }
+
+    @Override
+    public CacheManager getCacheManager()
+    {
+        return cache.getCacheManager();
+    }
+
+    @Override
+    public void close()
+    {
+        cache.close();
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        return cache.isClosed();
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        return cache.unwrap(clazz);
+    }
+
+    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        cache.registerCacheEntryListener(cacheEntryListenerConfiguration);
+    }
+
+    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        cache.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
+    }
+
+    @Override
+    public Iterator<Entry<K, V>> iterator()
+    {
+        return cache.iterator();
+    }
+
+    public void merge()
+    {
+        if (removeAll)
+        {
+            cache.removeAll();
+        }
+        if (clear)
+        {
+            cache.clear();
+        }
+
+        for (final Map.Entry<K, V> entry : put.entrySet())
+        {
+            cache.put(entry.getKey(), entry.getValue());
+        }
+        put.clear();
+        for (final K entry : remove)
+        {
+            cache.remove(entry);
+        }
+        remove.clear();
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Times.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Times.java
new file mode 100644
index 0000000..66cd992
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/Times.java
@@ -0,0 +1,37 @@
+package org.apache.commons.jcs.jcache;
+
+/*
+ * 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.
+ */
+
+public class Times
+{
+    public static long now(final boolean ignore)
+    {
+        if (ignore)
+        {
+            return -1;
+        }
+        return System.nanoTime() / 1000;
+    }
+
+    private Times()
+    {
+        // no-op
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java
new file mode 100644
index 0000000..d597ff5
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CDIJCacheHelper.java
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheDefaults;
+import javax.cache.annotation.CacheKeyGenerator;
+import javax.cache.annotation.CacheResolverFactory;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Dependent;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+import javax.interceptor.InvocationContext;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Logger;
+
+ at ApplicationScoped
+public class CDIJCacheHelper
+{
+    private static final Logger LOGGER = Logger.getLogger(CDIJCacheHelper.class.getName());
+
+    private final CacheResolverFactory defaultCacheResolverFactory = new CacheResolverFactoryImpl();
+    private final CacheKeyGeneratorImpl defaultCacheKeyGenerator = new CacheKeyGeneratorImpl();
+    private final ConcurrentMap<Method, String> generatedNames = new ConcurrentHashMap<Method, String>();
+
+    @Inject
+    private BeanManager beanManager;
+
+    public String defaultName(final Method method, final CacheDefaults defaults, final String cacheName)
+    {
+        if (!cacheName.isEmpty())
+        {
+            return cacheName;
+        }
+        if (defaults != null)
+        {
+            final String name = defaults.cacheName();
+            if (!name.isEmpty())
+            {
+                return name;
+            }
+        }
+
+        String computedName = generatedNames.get(method);
+        if (computedName == null)
+        {
+            final StringBuilder name = new StringBuilder(method.getDeclaringClass().getName());
+            name.append(".");
+            name.append(method.getName());
+            name.append("(");
+            final Class<?>[] parameterTypes = method.getParameterTypes();
+            for (int pIdx = 0; pIdx < parameterTypes.length; pIdx++)
+            {
+                name.append(parameterTypes[pIdx].getName());
+                if ((pIdx + 1) < parameterTypes.length)
+                {
+                    name.append(",");
+                }
+            }
+            name.append(")");
+            computedName = name.toString();
+            generatedNames.putIfAbsent(method, computedName);
+        }
+        return computedName;
+    }
+
+    public CacheDefaults findDefaults(final InvocationContext ic)
+    {
+        Class<?> clazz = ic.getTarget().getClass();
+        CacheDefaults annotation = null;
+        while (clazz != null && clazz != Object.class)
+        {
+            annotation = clazz.getAnnotation(CacheDefaults.class);
+            if (annotation != null)
+            {
+                break;
+            }
+            clazz = clazz.getSuperclass();
+        }
+        return annotation;
+    }
+
+    public boolean isIncluded(final Class<?> aClass, final Class<?>[] in, final Class<?>[] out)
+    {
+        if (in.length == 0 && out.length == 0)
+        {
+            return false;
+        }
+        for (final Class<?> potentialIn : in)
+        {
+            if (potentialIn.isAssignableFrom(aClass))
+            {
+                for (final Class<?> potentialOut : out)
+                {
+                    if (potentialOut.isAssignableFrom(aClass))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public CacheKeyGenerator cacheKeyGeneratorFor(final CacheDefaults defaults, final Class<? extends CacheKeyGenerator> cacheKeyGenerator)
+    {
+        if (!CacheKeyGenerator.class.equals(cacheKeyGenerator))
+        {
+            return instance(cacheKeyGenerator);
+        }
+        if (defaults != null)
+        {
+            final Class<? extends CacheKeyGenerator> defaultCacheKeyGenerator = defaults.cacheKeyGenerator();
+            if (!CacheResolverFactory.class.equals(defaultCacheKeyGenerator))
+            {
+                return instance(defaultCacheKeyGenerator);
+            }
+        }
+        return defaultCacheKeyGenerator;
+    }
+
+    public CacheResolverFactory cacheResolverFactoryFor(final CacheDefaults defaults, final Class<? extends CacheResolverFactory> cacheResolverFactory)
+    {
+        if (!CacheResolverFactory.class.equals(cacheResolverFactory))
+        {
+            return instance(cacheResolverFactory);
+        }
+        if (defaults != null)
+        {
+            final Class<? extends CacheResolverFactory> defaultCacheResolverFactory = defaults.cacheResolverFactory();
+            if (!CacheResolverFactory.class.equals(defaultCacheResolverFactory))
+            {
+                return instance(defaultCacheResolverFactory);
+            }
+        }
+        return defaultCacheResolverFactory;
+    }
+
+    public <T> T instance(final Class<T> type)
+    {
+        final Set<Bean<?>> beans = beanManager.getBeans(type);
+        if (beans.isEmpty())
+        {
+            return null;
+        }
+        final Bean<?> bean = beanManager.resolve(beans);
+        final CreationalContext<?> context = beanManager.createCreationalContext(bean);
+        final Class<? extends Annotation> scope = bean.getScope();
+        final boolean dependent = Dependent.class.equals(scope);
+        if (!dependent && !beanManager.isNormalScope(scope))
+        {
+            LOGGER.warning("Not normal scope beans (" + type.getName() + ") can leak");
+        }
+        try
+        {
+            return (T) beanManager.getReference(bean, bean.getBeanClass(), context);
+        }
+        finally
+        {
+            if (dependent)
+            { // TODO: depent or pseudo scope?
+                context.release();
+            }
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java
new file mode 100644
index 0000000..71d3e9d
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationContextImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheInvocationContext;
+import javax.cache.annotation.CacheInvocationParameter;
+import javax.interceptor.InvocationContext;
+import java.lang.annotation.Annotation;
+import java.util.HashSet;
+
+import static java.util.Arrays.asList;
+
+public class CacheInvocationContextImpl<A extends Annotation> extends CacheMethodDetailsImpl<A> implements CacheInvocationContext<A>
+{
+    private CacheInvocationParameter[] parameters = null;
+
+    public CacheInvocationContextImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName)
+    {
+        super(delegate, cacheAnnotation, cacheName);
+    }
+
+    @Override
+    public Object getTarget()
+    {
+        return delegate.getTarget();
+    }
+
+    @Override
+    public CacheInvocationParameter[] getAllParameters()
+    {
+        if (parameters == null)
+        {
+            final Object[] args = delegate.getParameters();
+            final Class<?>[] parameterTypes = getMethod().getParameterTypes();
+            final Annotation[][] parameterAnnotations = getMethod().getParameterAnnotations();
+            parameters = new CacheInvocationParameter[args.length];
+            for (int i = 0; i < args.length; i++)
+            {
+                parameters[i] = new CacheInvocationParameterImpl(parameterTypes[i], args[i], new HashSet<Annotation>(asList(parameterAnnotations[i])), i);
+            }
+        }
+        return parameters;
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> cls)
+    {
+        if (cls.isAssignableFrom(getClass()))
+        {
+            return cls.cast(this);
+        }
+        throw new IllegalArgumentException(cls.getName());
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationParameterImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationParameterImpl.java
new file mode 100644
index 0000000..9944d4b
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheInvocationParameterImpl.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheInvocationParameter;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+public class CacheInvocationParameterImpl implements CacheInvocationParameter
+{
+    private final Class<?> type;
+    private final Object value;
+    private final Set<Annotation> annotations;
+    private final int position;
+
+    public CacheInvocationParameterImpl(final Class<?> type, final Object value, final Set<Annotation> annotations, final int position)
+    {
+        this.type = type;
+        this.value = value;
+        this.annotations = annotations;
+        this.position = position;
+    }
+
+    @Override
+    public Class<?> getRawType()
+    {
+        return type;
+    }
+
+    @Override
+    public Object getValue()
+    {
+        return value;
+    }
+
+    @Override
+    public Set<Annotation> getAnnotations()
+    {
+        return annotations;
+    }
+
+    @Override
+    public int getParameterPosition()
+    {
+        return position;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyGeneratorImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyGeneratorImpl.java
new file mode 100644
index 0000000..06eaa4b
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyGeneratorImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheInvocationParameter;
+import javax.cache.annotation.CacheKeyGenerator;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.GeneratedCacheKey;
+import java.lang.annotation.Annotation;
+
+public class CacheKeyGeneratorImpl implements CacheKeyGenerator
+{
+    @Override
+    public GeneratedCacheKey generateCacheKey(final CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext)
+    {
+        final CacheInvocationParameter[] keyParameters = cacheKeyInvocationContext.getKeyParameters();
+        final Object[] parameters = new Object[keyParameters.length];
+        for (int index = 0; index < keyParameters.length; index++)
+        {
+            parameters[index] = keyParameters[index].getValue();
+        }
+        return new GeneratedCacheKeyImpl(parameters);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java
new file mode 100644
index 0000000..6c47985
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheKeyInvocationContextImpl.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheInvocationParameter;
+import javax.cache.annotation.CacheKey;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.CacheValue;
+import javax.interceptor.InvocationContext;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.LinkedList;
+
+public class CacheKeyInvocationContextImpl<A extends Annotation> extends CacheInvocationContextImpl<A> implements CacheKeyInvocationContext<A>
+{
+    private CacheInvocationParameter[] keyParams = null;
+    private CacheInvocationParameter valueParam = null;
+
+    public CacheKeyInvocationContextImpl(final InvocationContext delegate, final A annotation, final String name)
+    {
+        super(delegate, annotation, name);
+    }
+
+    @Override
+    public CacheInvocationParameter[] getKeyParameters()
+    {
+        if (keyParams == null)
+        {
+            final Collection<CacheInvocationParameter> keys = new LinkedList<CacheInvocationParameter>();
+            for (final CacheInvocationParameter param : getAllParameters())
+            {
+                for (final Annotation a : param.getAnnotations())
+                {
+                    if (a.annotationType().equals(CacheKey.class))
+                    {
+                        keys.add(param);
+                    }
+                }
+            }
+            if (keys.isEmpty())
+            {
+                keyParams = getAllParameters();
+            }
+            else
+            {
+                keyParams = keys.toArray(new CacheInvocationParameter[keys.size()]);
+            }
+        }
+        return keyParams;
+    }
+
+    @Override
+    public CacheInvocationParameter getValueParameter()
+    {
+        if (valueParam == null)
+        {
+            for (final CacheInvocationParameter param : getAllParameters())
+            {
+                for (final Annotation a : param.getAnnotations())
+                {
+                    if (a.annotationType().equals(CacheValue.class))
+                    {
+                        valueParam = param;
+                        return valueParam;
+                    }
+                }
+            }
+        }
+        return valueParam;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java
new file mode 100644
index 0000000..4f5ac5b
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheMethodDetailsImpl.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CacheMethodDetails;
+import javax.interceptor.InvocationContext;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+
+public class CacheMethodDetailsImpl<A extends Annotation> implements CacheMethodDetails<A>
+{
+    protected final InvocationContext delegate;
+    private final Set<Annotation> annotations;
+    private final A cacheAnnotation;
+    private final String cacheName;
+
+    public CacheMethodDetailsImpl(final InvocationContext delegate, final A cacheAnnotation, final String cacheName)
+    {
+        this.delegate = delegate;
+        this.annotations = new HashSet<Annotation>(asList(delegate.getMethod().getAnnotations()));
+        this.cacheAnnotation = cacheAnnotation;
+        this.cacheName = cacheName;
+    }
+
+    @Override
+    public Method getMethod()
+    {
+        return delegate.getMethod();
+    }
+
+    @Override
+    public Set<Annotation> getAnnotations()
+    {
+        return annotations;
+    }
+
+    @Override
+    public A getCacheAnnotation()
+    {
+        return cacheAnnotation;
+    }
+
+    @Override
+    public String getCacheName()
+    {
+        return cacheName;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java
new file mode 100644
index 0000000..5677c3b
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CachePutInterceptor.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.annotation.CacheDefaults;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.CachePut;
+import javax.cache.annotation.GeneratedCacheKey;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+ at CachePut
+ at Interceptor
+public class CachePutInterceptor
+{
+    @Inject
+    private CDIJCacheHelper helper;
+
+    @AroundInvoke
+    public Object cache(final InvocationContext ic) throws Throwable
+    {
+        final CacheDefaults defaults = helper.findDefaults(ic);
+
+        final CachePut cachePut = ic.getMethod().getAnnotation(CachePut.class);
+        final String cacheName = helper.defaultName(ic.getMethod(), defaults, cachePut.cacheName());
+        final boolean afterInvocation = cachePut.afterInvocation();
+
+        final CacheKeyInvocationContext<CachePut> context = new CacheKeyInvocationContextImpl<CachePut>(ic, cachePut, cacheName);
+        if (!afterInvocation)
+        {
+            doCache(context, defaults, cachePut);
+        }
+
+        final Object result;
+        try
+        {
+            result = ic.proceed();
+        }
+        catch (final Throwable t)
+        {
+            if (afterInvocation)
+            {
+                if (helper.isIncluded(t.getClass(), cachePut.cacheFor(), cachePut.noCacheFor()))
+                {
+                    doCache(context, defaults, cachePut);
+                }
+            }
+
+            throw t;
+        }
+
+        if (afterInvocation)
+        {
+            doCache(context, defaults, cachePut);
+        }
+
+        return result;
+    }
+
+    private void doCache(final CacheKeyInvocationContext<CachePut> context, final CacheDefaults defaults, final CachePut cachePut)
+    {
+        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cachePut.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
+        final GeneratedCacheKey key = helper.cacheKeyGeneratorFor(defaults, cachePut.cacheKeyGenerator()).generateCacheKey(context);
+        cache.put(key, context.getValueParameter().getValue());
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java
new file mode 100644
index 0000000..83dff87
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveAllInterceptor.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.annotation.CacheDefaults;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.CacheRemoveAll;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+ at CacheRemoveAll
+ at Interceptor
+public class CacheRemoveAllInterceptor
+{
+    @Inject
+    private CDIJCacheHelper helper;
+
+    @AroundInvoke
+    public Object cache(final InvocationContext ic) throws Throwable
+    {
+        final CacheDefaults defaults = helper.findDefaults(ic);
+
+        final CacheRemoveAll cacheRemoveAll = ic.getMethod().getAnnotation(CacheRemoveAll.class);
+        final String cacheName = helper.defaultName(ic.getMethod(), defaults, cacheRemoveAll.cacheName());
+        final boolean afterInvocation = cacheRemoveAll.afterInvocation();
+
+        final CacheKeyInvocationContext<CacheRemoveAll> context = new CacheKeyInvocationContextImpl<CacheRemoveAll>(ic, cacheRemoveAll, cacheName);
+        if (!afterInvocation)
+        {
+            removeAll(context, defaults, cacheRemoveAll);
+        }
+
+        final Object result;
+        try
+        {
+            result = ic.proceed();
+        }
+        catch (final Throwable t)
+        {
+            if (afterInvocation)
+            {
+                if (helper.isIncluded(t.getClass(), cacheRemoveAll.evictFor(), cacheRemoveAll.noEvictFor()))
+                {
+                    removeAll(context, defaults, cacheRemoveAll);
+                }
+            }
+            throw t;
+        }
+
+        if (afterInvocation)
+        {
+            removeAll(context, defaults, cacheRemoveAll);
+        }
+
+        return result;
+    }
+
+    private void removeAll(final CacheKeyInvocationContext<CacheRemoveAll> context, final CacheDefaults defaults, final CacheRemoveAll cacheRemoveAll)
+    {
+        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cacheRemoveAll.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
+        cache.removeAll();
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java
new file mode 100644
index 0000000..3dc7fe3
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheRemoveInterceptor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.annotation.CacheDefaults;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.CacheRemove;
+import javax.cache.annotation.GeneratedCacheKey;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+ at CacheRemove
+ at Interceptor
+public class CacheRemoveInterceptor
+{
+    @Inject
+    private CDIJCacheHelper helper;
+
+    @AroundInvoke
+    public Object cache(final InvocationContext ic) throws Throwable
+    {
+        final CacheDefaults defaults = helper.findDefaults(ic);
+
+        final CacheRemove cacheRemove = ic.getMethod().getAnnotation(CacheRemove.class);
+        final String cacheName = helper.defaultName(ic.getMethod(), defaults, cacheRemove.cacheName());
+        final boolean afterInvocation = cacheRemove.afterInvocation();
+
+        final CacheKeyInvocationContext<CacheRemove> context = new CacheKeyInvocationContextImpl<CacheRemove>(ic, cacheRemove, cacheName);
+
+        if (!afterInvocation)
+        {
+            doRemove(context, defaults, cacheRemove);
+        }
+
+        final Object result;
+        try
+        {
+            result = ic.proceed();
+        }
+        catch (final Throwable t)
+        {
+            if (afterInvocation)
+            {
+                if (helper.isIncluded(t.getClass(), cacheRemove.evictFor(), cacheRemove.noEvictFor()))
+                {
+                    doRemove(context, defaults, cacheRemove);
+                }
+            }
+
+            throw t;
+        }
+
+        if (afterInvocation)
+        {
+            doRemove(context, defaults, cacheRemove);
+        }
+
+        return result;
+    }
+
+    private void doRemove(final CacheKeyInvocationContext<CacheRemove> context, final CacheDefaults defaults, final CacheRemove cacheRemove)
+    {
+        final Cache<Object, Object> cache = helper.cacheResolverFactoryFor(defaults, cacheRemove.cacheResolverFactory()).getCacheResolver(context).resolveCache(context);
+        final GeneratedCacheKey key = helper.cacheKeyGeneratorFor(defaults, cacheRemove.cacheKeyGenerator()).generateCacheKey(context);
+        cache.remove(key);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverFactoryImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverFactoryImpl.java
new file mode 100644
index 0000000..bc34e3a
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverFactoryImpl.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.annotation.CacheMethodDetails;
+import javax.cache.annotation.CacheResolver;
+import javax.cache.annotation.CacheResolverFactory;
+import javax.cache.annotation.CacheResult;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.spi.CachingProvider;
+import java.lang.annotation.Annotation;
+
+public class CacheResolverFactoryImpl implements CacheResolverFactory
+{
+    private final CacheManager cacheManager;
+
+    public CacheResolverFactoryImpl()
+    {
+        final CachingProvider provider = Caching.getCachingProvider();
+        cacheManager = provider.getCacheManager(provider.getDefaultURI(), provider.getDefaultClassLoader());
+    }
+
+    @Override
+    public CacheResolver getCacheResolver(CacheMethodDetails<? extends Annotation> cacheMethodDetails)
+    {
+        return findCacheResolver(cacheMethodDetails.getCacheName());
+    }
+
+    @Override
+    public CacheResolver getExceptionCacheResolver(final CacheMethodDetails<CacheResult> cacheMethodDetails)
+    {
+        final String exceptionCacheName = cacheMethodDetails.getCacheAnnotation().exceptionCacheName();
+        if (exceptionCacheName == null || exceptionCacheName.isEmpty())
+        {
+            throw new IllegalArgumentException("CacheResult.exceptionCacheName() not specified");
+        }
+        return findCacheResolver(exceptionCacheName);
+    }
+
+    private CacheResolver findCacheResolver(String exceptionCacheName)
+    {
+        Cache<?, ?> cache = cacheManager.getCache(exceptionCacheName);
+        if (cache == null)
+        {
+            cache = createCache(exceptionCacheName);
+        }
+        return new CacheResolverImpl(cache);
+    }
+
+    private Cache<?, ?> createCache(final String exceptionCacheName)
+    {
+        cacheManager.createCache(exceptionCacheName, new MutableConfiguration<Object, Object>().setStoreByValue(false));
+        return cacheManager.getCache(exceptionCacheName);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverImpl.java
new file mode 100644
index 0000000..17f983c
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResolverImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.annotation.CacheInvocationContext;
+import javax.cache.annotation.CacheResolver;
+import java.lang.annotation.Annotation;
+
+public class CacheResolverImpl implements CacheResolver
+{
+    private final Cache<?, ?> delegate;
+
+    public CacheResolverImpl(final Cache<?, ?> cache)
+    {
+        delegate = cache;
+    }
+
+    @Override
+    public <K, V> Cache<K, V> resolveCache(final CacheInvocationContext<? extends Annotation> cacheInvocationContext)
+    {
+        return (Cache<K, V>) delegate;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java
new file mode 100644
index 0000000..5a178f3
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/CacheResultInterceptor.java
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.Cache;
+import javax.cache.annotation.CacheDefaults;
+import javax.cache.annotation.CacheKeyInvocationContext;
+import javax.cache.annotation.CacheResolver;
+import javax.cache.annotation.CacheResolverFactory;
+import javax.cache.annotation.CacheResult;
+import javax.cache.annotation.GeneratedCacheKey;
+import javax.inject.Inject;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+ at CacheResult
+ at Interceptor
+public class CacheResultInterceptor
+{
+    @Inject
+    private CDIJCacheHelper helper;
+
+    @AroundInvoke
+    public Object cache(final InvocationContext ic) throws Throwable
+    {
+        final CacheDefaults defaults = helper.findDefaults(ic);
+
+        final CacheResult cacheResult = ic.getMethod().getAnnotation(CacheResult.class);
+        final String cacheName = helper.defaultName(ic.getMethod(), defaults, cacheResult.cacheName());
+
+        final CacheKeyInvocationContext<CacheResult> context = new CacheKeyInvocationContextImpl<CacheResult>(ic, cacheResult, cacheName);
+
+        final CacheResolverFactory cacheResolverFactory = helper.cacheResolverFactoryFor(defaults, cacheResult.cacheResolverFactory());
+        final CacheResolver cacheResolver = cacheResolverFactory.getCacheResolver(context);
+        final Cache<Object, Object> cache = cacheResolver.resolveCache(context);
+
+        final GeneratedCacheKey cacheKey = helper.cacheKeyGeneratorFor(defaults, cacheResult.cacheKeyGenerator()).generateCacheKey(context);
+
+        Cache<Object, Object> exceptionCache = null; // lazily created
+
+        Object result;
+        if (!cacheResult.skipGet())
+        {
+            result = cache.get(cacheKey);
+            if (result != null)
+            {
+                return result;
+            }
+
+
+            if (!cacheResult.exceptionCacheName().isEmpty())
+            {
+                exceptionCache = cacheResolverFactory.getExceptionCacheResolver(context).resolveCache(context);
+                final Object exception = exceptionCache.get(cacheKey);
+                if (exception != null)
+                {
+                    throw Throwable.class.cast(exception);
+                }
+            }
+        }
+
+        try
+        {
+            result = ic.proceed();
+            if (result != null)
+            {
+                cache.put(cacheKey, result);
+            }
+
+            return result;
+        }
+        catch (final Throwable t)
+        {
+            if (helper.isIncluded(t.getClass(), cacheResult.cachedExceptions(), cacheResult.nonCachedExceptions()))
+            {
+                if (exceptionCache == null)
+                {
+                    exceptionCache = cacheResolverFactory.getExceptionCacheResolver(context).resolveCache(context);
+                }
+                exceptionCache.put(cacheKey, t);
+            }
+            throw t;
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/GeneratedCacheKeyImpl.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/GeneratedCacheKeyImpl.java
new file mode 100644
index 0000000..95c53c1
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/GeneratedCacheKeyImpl.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.GeneratedCacheKey;
+import java.util.Arrays;
+
+public class GeneratedCacheKeyImpl implements GeneratedCacheKey
+{
+    private final Object[] params;
+    private final int hash;
+
+    public GeneratedCacheKeyImpl(final Object[] parameters)
+    {
+        this.params = parameters;
+        this.hash = Arrays.deepHashCode(parameters);
+    }
+
+    @Override
+    public boolean equals(final Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass())
+        {
+            return false;
+        }
+        final GeneratedCacheKeyImpl that = GeneratedCacheKeyImpl.class.cast(o);
+        return Arrays.deepEquals(params, that.params);
+
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return hash;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/MakeJCacheCDIInterceptorFriendly.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/MakeJCacheCDIInterceptorFriendly.java
new file mode 100644
index 0000000..325a15f
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/cdi/MakeJCacheCDIInterceptorFriendly.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.cdi;
+
+import javax.cache.annotation.CachePut;
+import javax.cache.annotation.CacheRemove;
+import javax.cache.annotation.CacheRemoveAll;
+import javax.cache.annotation.CacheResult;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+
+// TODO: observe annotated type (or maybe sthg else) to cache data and inecjt this extension (used as metadata cache)
+// to get class model and this way allow to add cache annotation on the fly - == avoid java pure reflection to get metadata
+public class MakeJCacheCDIInterceptorFriendly implements Extension
+{
+    protected void discoverInterceptorBindings(final @Observes BeforeBeanDiscovery beforeBeanDiscoveryEvent)
+    {
+        beforeBeanDiscoveryEvent.addInterceptorBinding(CachePut.class);
+        beforeBeanDiscoveryEvent.addInterceptorBinding(CacheResult.class);
+        beforeBeanDiscoveryEvent.addInterceptorBinding(CacheRemove.class);
+        beforeBeanDiscoveryEvent.addInterceptorBinding(CacheRemoveAll.class);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/ConfigurableMBeanServerIdBuilder.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/ConfigurableMBeanServerIdBuilder.java
new file mode 100644
index 0000000..cd5746f
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/ConfigurableMBeanServerIdBuilder.java
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.jmx;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerBuilder;
+import javax.management.MBeanServerDelegate;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ConfigurableMBeanServerIdBuilder extends MBeanServerBuilder
+{
+    private static ConcurrentMap<Key, MBeanServer> JVM_SINGLETONS = new ConcurrentHashMap<Key, MBeanServer>();
+
+    private static class Key
+    {
+        private final String domain;
+        private final MBeanServer outer;
+
+        private Key(final String domain, final MBeanServer outer)
+        {
+            this.domain = domain;
+            this.outer = outer;
+        }
+
+        @Override
+        public boolean equals(final Object o)
+        {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            final Key key = Key.class.cast(o);
+            return !(domain != null ? !domain.equals(key.domain) : key.domain != null)
+                    && !(outer != null ? !outer.equals(key.outer) : key.outer != null);
+
+        }
+
+        @Override
+        public int hashCode()
+        {
+            int result = domain != null ? domain.hashCode() : 0;
+            result = 31 * result + (outer != null ? outer.hashCode() : 0);
+            return result;
+        }
+    }
+
+    @Override
+    public MBeanServer newMBeanServer(final String defaultDomain, final MBeanServer outer, final MBeanServerDelegate delegate)
+    {
+        final Key key = new Key(defaultDomain, outer);
+        MBeanServer server = JVM_SINGLETONS.get(key);
+        if (server == null)
+        {
+            server = super.newMBeanServer(defaultDomain, outer, new ForceIdMBeanServerDelegate(delegate));
+            final MBeanServer existing = JVM_SINGLETONS.putIfAbsent(key, server);
+            if (existing != null)
+            {
+                server = existing;
+            }
+        }
+        return server;
+    }
+
+    private class ForceIdMBeanServerDelegate extends MBeanServerDelegate
+    {
+        private final MBeanServerDelegate delegate;
+
+        public ForceIdMBeanServerDelegate(final MBeanServerDelegate delegate)
+        {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public String getMBeanServerId()
+        {
+            return System.getProperty("org.jsr107.tck.management.agentId", delegate.getMBeanServerId());
+        }
+
+        @Override
+        public String getSpecificationName()
+        {
+            return delegate.getSpecificationName();
+        }
+
+        @Override
+        public String getSpecificationVersion()
+        {
+            return delegate.getSpecificationVersion();
+        }
+
+        @Override
+        public String getSpecificationVendor()
+        {
+            return delegate.getSpecificationVendor();
+        }
+
+        @Override
+        public String getImplementationName()
+        {
+            return delegate.getImplementationName();
+        }
+
+        @Override
+        public String getImplementationVersion()
+        {
+            return delegate.getImplementationVersion();
+        }
+
+        @Override
+        public String getImplementationVendor()
+        {
+            return delegate.getImplementationVendor();
+        }
+
+        @Override
+        public MBeanNotificationInfo[] getNotificationInfo()
+        {
+            return delegate.getNotificationInfo();
+        }
+
+        @Override
+        public void addNotificationListener(final NotificationListener listener, final NotificationFilter filter, final Object handback)
+                throws IllegalArgumentException
+        {
+            delegate.addNotificationListener(listener, filter, handback);
+        }
+
+        @Override
+        public void removeNotificationListener(final NotificationListener listener, final NotificationFilter filter, final Object handback)
+                throws ListenerNotFoundException
+        {
+            delegate.removeNotificationListener(listener, filter, handback);
+        }
+
+        @Override
+        public void removeNotificationListener(final NotificationListener listener) throws ListenerNotFoundException
+        {
+            delegate.removeNotificationListener(listener);
+        }
+
+        @Override
+        public void sendNotification(final Notification notification)
+        {
+            delegate.sendNotification(notification);
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheMXBean.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheMXBean.java
new file mode 100644
index 0000000..3e352df
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheMXBean.java
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.jmx;
+
+import javax.cache.Cache;
+import javax.cache.configuration.CompleteConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.management.CacheMXBean;
+
+public class JCSCacheMXBean<K, V> implements CacheMXBean
+{
+    private final Cache<K, V> delegate;
+
+    public JCSCacheMXBean(final Cache<K, V> delegate)
+    {
+        this.delegate = delegate;
+    }
+
+    private Configuration<K, V> config()
+    {
+        return delegate.getConfiguration(Configuration.class);
+    }
+
+    private CompleteConfiguration<K, V> completeConfig()
+    {
+        return delegate.getConfiguration(CompleteConfiguration.class);
+    }
+
+    @Override
+    public String getKeyType()
+    {
+        return config().getKeyType().getName();
+    }
+
+    @Override
+    public String getValueType()
+    {
+        return config().getValueType().getName();
+    }
+
+    @Override
+    public boolean isReadThrough()
+    {
+        try
+        {
+            return completeConfig().isReadThrough();
+        }
+        catch (final Exception e)
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isWriteThrough()
+    {
+        try
+        {
+            return completeConfig().isWriteThrough();
+        }
+        catch (final Exception e)
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isStoreByValue()
+    {
+        return config().isStoreByValue();
+    }
+
+    @Override
+    public boolean isStatisticsEnabled()
+    {
+        try
+        {
+            return completeConfig().isStatisticsEnabled();
+        }
+        catch (final Exception e)
+        {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isManagementEnabled()
+    {
+        try
+        {
+            return completeConfig().isManagementEnabled();
+        }
+        catch (final Exception e)
+        {
+            return false;
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheStatisticsMXBean.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheStatisticsMXBean.java
new file mode 100644
index 0000000..df1273a
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JCSCacheStatisticsMXBean.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.jmx;
+
+import org.apache.commons.jcs.jcache.Statistics;
+
+import javax.cache.management.CacheStatisticsMXBean;
+
+public class JCSCacheStatisticsMXBean implements CacheStatisticsMXBean
+{
+    private final Statistics statistics;
+
+    public JCSCacheStatisticsMXBean(final Statistics stats)
+    {
+        this.statistics = stats;
+    }
+
+    @Override
+    public void clear()
+    {
+        statistics.reset();
+    }
+
+    @Override
+    public long getCacheHits()
+    {
+        return statistics.getHits();
+    }
+
+    @Override
+    public float getCacheHitPercentage()
+    {
+        final long hits = getCacheHits();
+        if (hits == 0)
+        {
+            return 0;
+        }
+        return (float) hits / getCacheGets() * 100.0f;
+    }
+
+    @Override
+    public long getCacheMisses()
+    {
+        return statistics.getMisses();
+    }
+
+    @Override
+    public float getCacheMissPercentage()
+    {
+        final long misses = getCacheMisses();
+        if (misses == 0)
+        {
+            return 0;
+        }
+        return (float) misses / getCacheGets() * 100.0f;
+    }
+
+    @Override
+    public long getCacheGets()
+    {
+        return getCacheHits() + getCacheMisses();
+    }
+
+    @Override
+    public long getCachePuts()
+    {
+        return statistics.getPuts();
+    }
+
+    @Override
+    public long getCacheRemovals()
+    {
+        return statistics.getRemovals();
+    }
+
+    @Override
+    public long getCacheEvictions()
+    {
+        return statistics.getEvictions();
+    }
+
+    @Override
+    public float getAverageGetTime()
+    {
+        return averageTime(statistics.getTimeTakenForGets());
+    }
+
+    @Override
+    public float getAveragePutTime()
+    {
+        return averageTime(statistics.getTimeTakenForPuts());
+    }
+
+    @Override
+    public float getAverageRemoveTime()
+    {
+        return averageTime(statistics.getTimeTakenForRemovals());
+    }
+
+    private float averageTime(final long timeTaken)
+    {
+        final long gets = getCacheGets();
+        if (timeTaken == 0 || gets == 0)
+        {
+            return 0;
+        }
+        return timeTaken / gets;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JMXs.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JMXs.java
new file mode 100644
index 0000000..c8597df
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/jmx/JMXs.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.jmx;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+
+public class JMXs
+{
+    private static final MBeanServer SERVER = findMBeanServer();
+
+    public static MBeanServer server()
+    {
+        return SERVER;
+    }
+
+    public static void register(final ObjectName on, final Object bean)
+    {
+        if (!SERVER.isRegistered(on))
+        {
+            try
+            {
+                SERVER.registerMBean(bean, on);
+            }
+            catch (final Exception e)
+            {
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+    }
+
+    public static void unregister(final ObjectName on)
+    {
+        if (SERVER.isRegistered(on))
+        {
+            try
+            {
+                SERVER.unregisterMBean(on);
+            }
+            catch (final Exception e)
+            {
+                // no-op
+            }
+        }
+    }
+
+    private static MBeanServer findMBeanServer()
+    {
+        if (System.getProperty("javax.management.builder.initial") != null)
+        {
+            return MBeanServerFactory.createMBeanServer();
+        }
+        return ManagementFactory.getPlatformMBeanServer();
+    }
+
+    private JMXs()
+    {
+        // no-op
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/DefaultSubsitutor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/DefaultSubsitutor.java
new file mode 100644
index 0000000..f3a0ecb
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/DefaultSubsitutor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.jcs.jcache.lang;
+
+public class DefaultSubsitutor implements Subsitutor
+{
+    @Override
+    public String substitute(final String value)
+    {
+        if (value.startsWith("${") && value.endsWith("}")) {
+            return System.getProperty(value.substring("${".length(), value.length() - 1), value);
+        }
+        return value;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Lang3Substitutor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Lang3Substitutor.java
new file mode 100644
index 0000000..bd00eff
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Lang3Substitutor.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.jcs.jcache.lang;
+
+import org.apache.commons.lang3.text.StrSubstitutor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Lang3Substitutor implements Subsitutor
+{
+    private static final StrSubstitutor SUBSTITUTOR = new StrSubstitutor(new HashMap<String, Object>() {{
+        putAll(Map.class.cast(System.getProperties()));
+        putAll(System.getenv());
+    }});
+
+    @Override
+    public String substitute(final String value)
+    {
+        return SUBSTITUTOR.replace(value);
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Subsitutor.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Subsitutor.java
new file mode 100644
index 0000000..9c049a2
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/lang/Subsitutor.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.jcs.jcache.lang;
+
+public interface Subsitutor
+{
+    String substitute(String value);
+
+    public static class Helper {
+        public static final Subsitutor INSTANCE;
+        static {
+            Subsitutor value = null;
+            for (final String name : new String[]
+            { // ordered by features
+                    "org.apache.commons.jcs.jcache.lang.Lang3Substitutor",
+                    "org.apache.commons.jcs.jcache.lang.DefaultSubsitutor"
+            })
+            {
+                try
+                {
+                    value = Subsitutor.class.cast(
+                            Subsitutor.class.getClassLoader().loadClass(name).newInstance());
+                    value.substitute("${java.version}"); // ensure it works
+                }
+                catch (final Throwable e) // not Exception otherwise NoClassDefFoundError
+                {
+                    // no-op: next
+                }
+            }
+            if (value == null) {
+                throw new IllegalStateException("Can't find a " + Subsitutor.class.getName());
+            }
+            INSTANCE = value;
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ClassLoaderAwareCache.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ClassLoaderAwareCache.java
new file mode 100644
index 0000000..9cad390
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ClassLoaderAwareCache.java
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.proxy;
+
+import org.apache.commons.jcs.jcache.JCSCache;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.Configuration;
+import javax.cache.integration.CompletionListener;
+import javax.cache.processor.EntryProcessor;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.EntryProcessorResult;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+// don't use a proxy, reflection is too slow here :(
+public class ClassLoaderAwareCache<K extends Serializable, V extends Serializable> implements Cache<K, V>
+{
+    private final ClassLoader loader;
+    private final JCSCache<K, V> delegate;
+
+    public ClassLoaderAwareCache(final ClassLoader loader, final JCSCache<K, V> delegate)
+    {
+        this.loader = loader;
+        this.delegate = delegate;
+    }
+
+    private ClassLoader before(final Thread thread)
+    {
+        final ClassLoader tccl = thread.getContextClassLoader();
+        thread.setContextClassLoader(loader);
+        return tccl;
+    }
+
+    public V get(final K key)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.get(key);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public Map<K, V> getAll(final Set<? extends K> keys)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getAll(keys);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean containsKey(final K key)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.containsKey(key);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void loadAll(final Set<? extends K> keys, boolean replaceExistingValues, final CompletionListener completionListener)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.loadAll(keys, replaceExistingValues, completionListener);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void put(final K key, final V value)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.put(key, value);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public V getAndPut(final K key, final V value)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getAndPut(key, value);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void putAll(final Map<? extends K, ? extends V> map)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.putAll(map);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean putIfAbsent(final K key, final V value)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.putIfAbsent(key, value);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean remove(final K key)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.remove(key);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean remove(final K key, final V oldValue)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.remove(key, oldValue);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public V getAndRemove(final K key)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getAndRemove(key);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean replace(final K key, final V oldValue, final V newValue)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.replace(key, oldValue, newValue);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public boolean replace(final K key, final V value)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.replace(key, value);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public V getAndReplace(final K key, final V value)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getAndReplace(key, value);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void removeAll(final Set<? extends K> keys)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.removeAll(keys);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public void removeAll()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.removeAll();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public void clear()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.clear();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public <C extends Configuration<K, V>> C getConfiguration(final Class<C> clazz)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getConfiguration(clazz);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public <T> T invoke(final K key, final EntryProcessor<K, V, T> entryProcessor, final Object... arguments) throws EntryProcessorException
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.invoke(key, entryProcessor, arguments);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.invokeAll(keys, entryProcessor, arguments);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public String getName()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getName();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public CacheManager getCacheManager()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.getCacheManager();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public void close()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.close();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public boolean isClosed()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.isClosed();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> clazz)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.unwrap(clazz);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void registerCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.registerCacheEntryListener(cacheEntryListenerConfiguration);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    public void deregisterCacheEntryListener(final CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration)
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            delegate.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public Iterator<Entry<K, V>> iterator()
+    {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader loader = before(thread);
+        try
+        {
+            return delegate.iterator();
+        }
+        finally
+        {
+            thread.setContextClassLoader(loader);
+        }
+    }
+
+    @Override
+    public boolean equals(final Object obj)
+    {
+        if (ClassLoaderAwareCache.class.isInstance(obj))
+        {
+            return delegate.equals(ClassLoaderAwareCache.class.cast(obj).delegate);
+        }
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return delegate.hashCode();
+    }
+
+    public static <K extends Serializable, V extends Serializable> Cache<K, V> wrap(final ClassLoader loader, final JCSCache<K, V> delegate)
+    {
+        ClassLoader dontWrapLoader = ClassLoaderAwareCache.class.getClassLoader();
+        while (dontWrapLoader != null)
+        {
+            if (loader == dontWrapLoader)
+            {
+                return delegate;
+            }
+            dontWrapLoader = dontWrapLoader.getParent();
+        }
+        return new ClassLoaderAwareCache<K, V>(loader, delegate);
+    }
+
+    public static <K extends Serializable, V extends Serializable> JCSCache<K, V> getDelegate(final Cache<?, ?> cache)
+    {
+        if (JCSCache.class.isInstance(cache))
+        {
+            return (JCSCache<K, V>) cache;
+        }
+        return ((ClassLoaderAwareCache<K, V>) cache).delegate;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ExceptionWrapperHandler.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ExceptionWrapperHandler.java
new file mode 100644
index 0000000..9c81427
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/proxy/ExceptionWrapperHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.proxy;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class ExceptionWrapperHandler<T> implements InvocationHandler
+{
+    private final T delegate;
+    private final Constructor<? extends RuntimeException> wrapper;
+
+    public ExceptionWrapperHandler(final T delegate, final Class<? extends RuntimeException> exceptionType)
+    {
+        this.delegate = delegate;
+        try
+        {
+            this.wrapper = exceptionType.getConstructor(Throwable.class);
+        }
+        catch (final NoSuchMethodException e)
+        {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Override
+    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
+    {
+        try
+        {
+            return method.invoke(delegate, args);
+        }
+        catch (final InvocationTargetException ite)
+        {
+            final Throwable e = ite.getCause();
+            if (RuntimeException.class.isInstance(e))
+            {
+                final RuntimeException re;
+                try
+                {
+                    re = wrapper.newInstance(e);
+                }
+                catch (final Exception e1)
+                {
+                    throw new IllegalArgumentException(e1);
+                }
+                throw re;
+            }
+            throw e;
+        }
+    }
+
+    public static <T> T newProxy(final ClassLoader loader, final T delegate, final Class<? extends RuntimeException> exceptionType,
+            final Class<T> apis)
+    {
+        return (T) Proxy.newProxyInstance(loader, new Class<?>[] { apis }, new ExceptionWrapperHandler<T>(delegate, exceptionType));
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/serialization/Serializations.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/serialization/Serializations.java
new file mode 100644
index 0000000..b024719
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/serialization/Serializations.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.serialization;
+
+import org.apache.commons.jcs.engine.behavior.IElementSerializer;
+
+public class Serializations
+{
+    public static <K> K copy(final IElementSerializer serializer, final ClassLoader loader, final K key)
+    {
+        try
+        {
+            return serializer.deSerialize(serializer.serialize(key), loader);
+        }
+        catch ( final Exception e)
+        {
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/thread/DaemonThreadFactory.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/thread/DaemonThreadFactory.java
new file mode 100644
index 0000000..ce97d9d
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/thread/DaemonThreadFactory.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache.thread;
+
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DaemonThreadFactory implements ThreadFactory
+{
+    private final AtomicInteger index = new AtomicInteger(1);
+    private final String prefix;
+
+    public DaemonThreadFactory(final String prefix)
+    {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public Thread newThread( final Runnable runner )
+    {
+        final Thread t = new Thread( runner );
+        t.setName(prefix + index.getAndIncrement());
+        t.setDaemon(true);
+        return t;
+    }
+}
\ No newline at end of file
diff --git a/commons-jcs-jcache/src/main/resources/META-INF/beans.xml b/commons-jcs-jcache/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..7894dfe
--- /dev/null
+++ b/commons-jcs-jcache/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+
+   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.
+
+-->
+<beans xmlns="http://java.sun.com/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+          http://java.sun.com/xml/ns/javaee
+          http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
+  <interceptors>
+    <class>org.apache.commons.jcs.jcache.cdi.CachePutInterceptor</class>
+    <class>org.apache.commons.jcs.jcache.cdi.CacheRemoveAllInterceptor</class>
+    <class>org.apache.commons.jcs.jcache.cdi.CacheRemoveInterceptor</class>
+    <class>org.apache.commons.jcs.jcache.cdi.CacheResultInterceptor</class>
+  </interceptors>
+</beans>
diff --git a/commons-jcs-jcache/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider b/commons-jcs-jcache/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider
new file mode 100644
index 0000000..c3eaf99
--- /dev/null
+++ b/commons-jcs-jcache/src/main/resources/META-INF/services/javax.cache.spi.CachingProvider
@@ -0,0 +1,18 @@
+# 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.
+
+org.apache.commons.jcs.jcache.JCSCachingProvider
diff --git a/commons-jcs-jcache/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/commons-jcs-jcache/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
new file mode 100644
index 0000000..a47b35b
--- /dev/null
+++ b/commons-jcs-jcache/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension
@@ -0,0 +1,17 @@
+# 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.
+org.apache.commons.jcs.jcache.cdi.MakeJCacheCDIInterceptorFriendly
diff --git a/commons-jcs-jcache/src/main/resources/jcache.ccf b/commons-jcs-jcache/src/main/resources/jcache.ccf
new file mode 100644
index 0000000..f0f1d0c
--- /dev/null
+++ b/commons-jcs-jcache/src/main/resources/jcache.ccf
@@ -0,0 +1,32 @@
+# 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.
+
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
+jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
diff --git a/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CacheTest.java b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CacheTest.java
new file mode 100644
index 0000000..13f1394
--- /dev/null
+++ b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CacheTest.java
@@ -0,0 +1,312 @@
+package org.apache.commons.jcs.jcache;
+
+/*
+ * 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.
+ */
+
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.configuration.CompleteConfiguration;
+import javax.cache.configuration.Factory;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.event.CacheEntryCreatedListener;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryEventFilter;
+import javax.cache.event.CacheEntryListener;
+import javax.cache.event.CacheEntryListenerException;
+import javax.cache.event.CacheEntryRemovedListener;
+import javax.cache.event.CacheEntryUpdatedListener;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.integration.CacheLoader;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriter;
+import javax.cache.spi.CachingProvider;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class CacheTest
+{
+    @Test
+    public void getPut()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        final Cache<String, String> cache = cacheManager.createCache("default", new MutableConfiguration<String, String>());
+        assertFalse(cache.containsKey("foo"));
+        cache.put("foo", "bar");
+        assertTrue(cache.containsKey("foo"));
+        assertEquals("bar", cache.get("foo"));
+        cache.remove("foo");
+        assertFalse(cache.containsKey("foo"));
+        cachingProvider.close();
+    }
+
+    @Test
+    public void listeners()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        cacheManager.createCache("default", new MutableConfiguration<Object, Object>());
+        final Cache<String, String> cache = cacheManager.getCache("default");
+        final Set<String> event = new HashSet<String>();
+        cache.registerCacheEntryListener(new CacheEntryListenerConfiguration<String, String>()
+        {
+            @Override
+            public Factory<CacheEntryListener<? super String, ? super String>> getCacheEntryListenerFactory()
+            {
+                return new Factory<CacheEntryListener<? super String, ? super String>>()
+                {
+                    @Override
+                    public CacheEntryListener<? super String, ? super String> create()
+                    {
+                        return new CacheEntryCreatedListener<String, String>()
+                        {
+                            @Override
+                            public void onCreated(Iterable<CacheEntryEvent<? extends String, ? extends String>> cacheEntryEvents)
+                                    throws CacheEntryListenerException
+                            {
+                                event.add(cacheEntryEvents.iterator().next().getKey());
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            public boolean isOldValueRequired()
+            {
+                return false;
+            }
+
+            @Override
+            public Factory<CacheEntryEventFilter<? super String, ? super String>> getCacheEntryEventFilterFactory()
+            {
+                return null;
+            }
+
+            @Override
+            public boolean isSynchronous()
+            {
+                return false;
+            }
+        });
+        cache.registerCacheEntryListener(new CacheEntryListenerConfiguration<String, String>()
+        {
+            @Override
+            public Factory<CacheEntryListener<? super String, ? super String>> getCacheEntryListenerFactory()
+            {
+                return new Factory<CacheEntryListener<? super String, ? super String>>()
+                {
+                    @Override
+                    public CacheEntryListener<? super String, ? super String> create()
+                    {
+                        return new CacheEntryUpdatedListener<String, String>()
+                        {
+                            @Override
+                            public void onUpdated(Iterable<CacheEntryEvent<? extends String, ? extends String>> cacheEntryEvents)
+                                    throws CacheEntryListenerException
+                            {
+                                event.add(cacheEntryEvents.iterator().next().getKey());
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            public boolean isOldValueRequired()
+            {
+                return false;
+            }
+
+            @Override
+            public Factory<CacheEntryEventFilter<? super String, ? super String>> getCacheEntryEventFilterFactory()
+            {
+                return null;
+            }
+
+            @Override
+            public boolean isSynchronous()
+            {
+                return false;
+            }
+        });
+        cache.registerCacheEntryListener(new CacheEntryListenerConfiguration<String, String>()
+        {
+            @Override
+            public Factory<CacheEntryListener<? super String, ? super String>> getCacheEntryListenerFactory()
+            {
+                return new Factory<CacheEntryListener<? super String, ? super String>>()
+                {
+                    @Override
+                    public CacheEntryListener<? super String, ? super String> create()
+                    {
+                        return new CacheEntryRemovedListener<String, String>()
+                        {
+                            @Override
+                            public void onRemoved(Iterable<CacheEntryEvent<? extends String, ? extends String>> cacheEntryEvents)
+                                    throws CacheEntryListenerException
+                            {
+                                event.add(cacheEntryEvents.iterator().next().getKey());
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            public boolean isOldValueRequired()
+            {
+                return false;
+            }
+
+            @Override
+            public Factory<CacheEntryEventFilter<? super String, ? super String>> getCacheEntryEventFilterFactory()
+            {
+                return null;
+            }
+
+            @Override
+            public boolean isSynchronous()
+            {
+                return false;
+            }
+        });
+
+        cache.put("foo", "bar");
+        assertEquals(1, event.size());
+        assertEquals("foo", event.iterator().next());
+        event.clear();
+        cache.put("foo", "new");
+        assertEquals(1, event.size());
+        assertEquals("foo", event.iterator().next());
+        event.clear();
+        cache.remove("foo");
+        assertEquals(1, event.size());
+        assertEquals("foo", event.iterator().next());
+
+        cachingProvider.close();
+    }
+
+    @Test
+    public void loader()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        cacheManager.createCache("default", new CompleteConfiguration<Object, Object>()
+        {
+            @Override
+            public boolean isReadThrough()
+            {
+                return true;
+            }
+
+            @Override
+            public boolean isWriteThrough()
+            {
+                return false;
+            }
+
+            @Override
+            public boolean isStatisticsEnabled()
+            {
+                return false;
+            }
+
+            @Override
+            public boolean isManagementEnabled()
+            {
+                return false;
+            }
+
+            @Override
+            public Iterable<CacheEntryListenerConfiguration<Object, Object>> getCacheEntryListenerConfigurations()
+            {
+                return null;
+            }
+
+            @Override
+            public Factory<CacheLoader<Object, Object>> getCacheLoaderFactory()
+            {
+                return new Factory<CacheLoader<Object, Object>>()
+                {
+                    @Override
+                    public CacheLoader<Object, Object> create()
+                    {
+                        return new CacheLoader<Object, Object>()
+                        {
+                            @Override
+                            public Object load(Object key) throws CacheLoaderException
+                            {
+                                return "super";
+                            }
+
+                            @Override
+                            public Map<Object, Object> loadAll(Iterable<?> keys) throws CacheLoaderException
+                            {
+                                return null;
+                            }
+                        };
+                    }
+                };
+            }
+
+            @Override
+            public Factory<CacheWriter<? super Object, ? super Object>> getCacheWriterFactory()
+            {
+                return null;
+            }
+
+            @Override
+            public Factory<ExpiryPolicy> getExpiryPolicyFactory()
+            {
+                return null;
+            }
+
+            @Override
+            public Class<Object> getKeyType()
+            {
+                return Object.class;
+            }
+
+            @Override
+            public Class<Object> getValueType()
+            {
+                return Object.class;
+            }
+
+            @Override
+            public boolean isStoreByValue()
+            {
+                return false;
+            }
+        });
+        final Cache<String, String> cache = cacheManager.getCache("default");
+        assertEquals("super", cache.get("lazilyLoaded"));
+        cachingProvider.close();
+    }
+}
diff --git a/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CachingProviderTest.java b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CachingProviderTest.java
new file mode 100644
index 0000000..24234e8
--- /dev/null
+++ b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/CachingProviderTest.java
@@ -0,0 +1,44 @@
+package org.apache.commons.jcs.jcache;
+
+/*
+ * 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.
+ */
+
+import org.junit.Test;
+
+import javax.cache.Caching;
+import javax.cache.spi.CachingProvider;
+
+import static org.junit.Assert.assertNotNull;
+
+public class CachingProviderTest
+{
+    @Test
+    public void findProvider()
+    {
+        assertNotNull(Caching.getCachingProvider());
+    }
+
+    @Test
+    public void createCacheMgr()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        assertNotNull(cachingProvider.getCacheManager());
+        cachingProvider.close();
+    }
+}
diff --git a/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ImmediateExpiryTest.java b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ImmediateExpiryTest.java
new file mode 100644
index 0000000..bd4d445
--- /dev/null
+++ b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ImmediateExpiryTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package org.apache.commons.jcs.jcache;
+
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.spi.CachingProvider;
+
+import static org.junit.Assert.assertFalse;
+
+public class ImmediateExpiryTest
+{
+    @Test
+    public void immediate()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        cacheManager.createCache("default",
+                new MutableConfiguration<Object, Object>()
+                        .setExpiryPolicyFactory(
+                                new FactoryBuilder.SingletonFactory<ExpiryPolicy>(new CreatedExpiryPolicy(Duration.ZERO))));
+        final Cache<String, String> cache = cacheManager.getCache("default");
+        assertFalse(cache.containsKey("foo"));
+        cache.put("foo", "bar");
+        assertFalse(cache.containsKey("foo"));
+        cachingProvider.close();
+    }
+}
diff --git a/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/NotSerializableTest.java b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/NotSerializableTest.java
new file mode 100644
index 0000000..dc9e729
--- /dev/null
+++ b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/NotSerializableTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import org.junit.Test;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.spi.CachingProvider;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class NotSerializableTest
+{
+    @Test
+    public void run()
+    {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        cacheManager.createCache("default", new MutableConfiguration<String, NotSerializableAndImHappyWithIt>().setStoreByValue(false));
+        final Cache<String, NotSerializableAndImHappyWithIt> cache = cacheManager.getCache("default");
+        assertFalse(cache.containsKey("foo"));
+        cache.put("foo", new NotSerializableAndImHappyWithIt("bar"));
+        assertTrue(cache.containsKey("foo"));
+        assertEquals("bar", cache.get("foo").name);
+        cache.remove("foo");
+        assertFalse(cache.containsKey("foo"));
+        cache.close();
+        cacheManager.close();
+        cachingProvider.close();
+    }
+
+    public static class NotSerializableAndImHappyWithIt {
+        private final String name;
+
+        public NotSerializableAndImHappyWithIt(final String name)
+        {
+            this.name = name;
+        }
+    }
+}
diff --git a/commons-jcs-tck-tests/pom.xml b/commons-jcs-tck-tests/pom.xml
new file mode 100644
index 0000000..8685fbd
--- /dev/null
+++ b/commons-jcs-tck-tests/pom.xml
@@ -0,0 +1,240 @@
+<?xml version="1.0"?>
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-jcs</artifactId>
+    <version>2.0-beta-1</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+
+  <!--
+
+  to run from an IDE a JMX test add:
+    -Dorg.jsr107.tck.management.agentId=MBeanServerJCS -Djavax.management.builder.initial=org.apache.commons.jcs.jcache.jmx.ConfigurableMBeanServerIdBuilder
+  -->
+
+  <artifactId>commons-jcs-jcache-tck</artifactId>
+  <version>2.0-beta-1</version>
+
+  <name>Apache Commons JCS :: JCache TCK</name>
+
+  <properties>
+
+    <implementation-groupId>${project.groupId}</implementation-groupId>
+    <implementation-artifactId>commons-jcs</implementation-artifactId>
+    <implementation-version>${project.version}</implementation-version>
+
+    <CacheManagerImpl>org.apache.commons.jcs.jcache.JCSCachingManager</CacheManagerImpl>
+    <CacheImpl>org.apache.commons.jcs.jcache.JCSCache</CacheImpl>
+    <CacheEntryImpl>org.apache.commons.jcs.jcache.JCSEntry</CacheEntryImpl>
+
+    <javax.management.builder.initial>org.apache.commons.jcs.jcache.jmx.ConfigurableMBeanServerIdBuilder
+    </javax.management.builder.initial>
+    <org.jsr107.tck.management.agentId>MBeanServerJCS</org.jsr107.tck.management.agentId>
+
+    <domain-lib-dir>${project.build.directory}/domainlib</domain-lib-dir>
+    <domain-jar>domain.jar</domain-jar>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>commons-jcs-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>commons-jcs-jcache</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.cache</groupId>
+      <artifactId>test-domain</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.cache</groupId>
+      <artifactId>app-domain</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcache_1.0_spec</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.cache</groupId>
+      <artifactId>cache-tests</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.cache</groupId>
+      <artifactId>cache-tests</artifactId>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.transaction</groupId>
+      <artifactId>jta</artifactId>
+      <version>1.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jcdi_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-atinject_1.0_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-interceptor_1.1_spec</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.openwebbeans</groupId>
+      <artifactId>openwebbeans-impl</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <testResources>
+      <testResource>
+        <directory>src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+    </testResources>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <version>2.2</version>
+        <configuration>
+          <skipSource>true</skipSource>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>jcacheTck</id>
+      <activation>
+        <property>
+          <name>jcache.tck</name>
+          <value>true</value>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-dependency-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>copy-cache-tests</id>
+                <phase>generate-test-resources</phase>
+                <goals>
+                  <goal>unpack-dependencies</goal>
+                </goals>
+                <configuration>
+                  <outputDirectory>${project.build.testOutputDirectory}</outputDirectory>
+                  <includeArtifactIds>cache-tests</includeArtifactIds>
+                  <includeScope>test</includeScope>
+                  <excludes>**/unwrap.properties</excludes>
+                </configuration>
+              </execution>
+              <execution>
+                <id>copy-domain</id>
+                <phase>generate-test-resources</phase>
+                <goals>
+                  <goal>copy</goal>
+                </goals>
+                <configuration>
+                  <artifactItems>
+                    <artifactItem>
+                      <groupId>javax.cache</groupId>
+                      <artifactId>app-domain</artifactId>
+                      <version>${jsr107.api.version}</version>
+                      <outputDirectory>${domain-lib-dir}</outputDirectory>
+                      <destFileName>${domain-jar}</destFileName>
+                    </artifactItem>
+                  </artifactItems>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-surefire-plugin</artifactId>
+            <version>2.17</version>
+            <configuration>
+              <systemPropertyVariables>
+                <domainJar>${domain-lib-dir}/${domain-jar}</domainJar>
+                <javax.management.builder.initial>${javax.management.builder.initial}</javax.management.builder.initial>
+                <org.jsr107.tck.management.agentId>${org.jsr107.tck.management.agentId}</org.jsr107.tck.management.agentId>
+                <javax.cache.CacheManager>${CacheManagerImpl}</javax.cache.CacheManager>
+                <javax.cache.Cache>${CacheImpl}</javax.cache.Cache>
+                <javax.cache.Cache.Entry>${CacheEntryImpl}</javax.cache.Cache.Entry>
+                <javax.cache.annotation.CacheInvocationContext>${CacheInvocationContextImpl}</javax.cache.annotation.CacheInvocationContext>
+              </systemPropertyVariables>
+            </configuration>
+          </plugin>
+
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
+
diff --git a/commons-jcs-tck-tests/run-tck.sh b/commons-jcs-tck-tests/run-tck.sh
new file mode 100644
index 0000000..e612723
--- /dev/null
+++ b/commons-jcs-tck-tests/run-tck.sh
@@ -0,0 +1,19 @@
+#! /bin/bash
+
+# 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.
+
+mvn clean install -Djcache.tck
+
diff --git a/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/EnsureCDIIsTestedWhenTCKsRunTest.java b/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/EnsureCDIIsTestedWhenTCKsRunTest.java
new file mode 100644
index 0000000..fdcab32
--- /dev/null
+++ b/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/EnsureCDIIsTestedWhenTCKsRunTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import org.junit.Test;
+
+import javax.cache.annotation.BeanProvider;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+// useless test but without it we are not sure
+// CDI TCKs passed
+public class EnsureCDIIsTestedWhenTCKsRunTest
+{
+    @Test
+    public void checkOWBProvider()
+    {
+        try {
+            final Iterator<BeanProvider> iterator = ServiceLoader.load(BeanProvider.class).iterator();
+            assertTrue(iterator.hasNext());
+            assertThat(iterator.next(), instanceOf(OWBBeanProvider.class));
+        } catch (java.lang.UnsupportedClassVersionError e) {
+            System.err.println("Ignoring checkOWBProvider test failure on " + System.getProperty("java.version"));
+        }
+    }
+}
diff --git a/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/OWBBeanProvider.java b/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/OWBBeanProvider.java
new file mode 100644
index 0000000..3323aa8
--- /dev/null
+++ b/commons-jcs-tck-tests/src/test/java/org/apache/commons/jcs/jcache/OWBBeanProvider.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jcs.jcache;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.container.BeanManagerImpl;
+import org.apache.webbeans.spi.ContainerLifecycle;
+
+import javax.cache.annotation.BeanProvider;
+import javax.enterprise.inject.spi.Bean;
+import java.util.Set;
+
+public class OWBBeanProvider implements BeanProvider
+{
+    private final BeanManagerImpl bm;
+
+    public OWBBeanProvider()
+    {
+        final WebBeansContext webBeansContext = WebBeansContext.currentInstance();
+        final ContainerLifecycle lifecycle = webBeansContext.getService(ContainerLifecycle.class);
+        lifecycle.startApplication(null);
+        Runtime.getRuntime().addShutdownHook(new Thread()
+        {
+            @Override
+            public void run()
+            {
+                lifecycle.stopApplication(null);
+            }
+        });
+        bm = webBeansContext.getBeanManagerImpl();
+    }
+
+    @Override
+    public <T> T getBeanByType(final Class<T> tClass)
+    {
+        if (tClass == null)
+        {
+            throw new IllegalArgumentException("no bean class specified");
+        }
+
+        final Set<Bean<?>> beans = bm.getBeans(tClass);
+        if (beans.isEmpty())
+        {
+            throw new IllegalStateException("no bean of type " + tClass.getName());
+        }
+        final Bean<?> bean = bm.resolve(beans);
+        return (T) bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
+    }
+}
diff --git a/commons-jcs-tck-tests/src/test/resources/ExcludeList b/commons-jcs-tck-tests/src/test/resources/ExcludeList
new file mode 100644
index 0000000..170a7fa
--- /dev/null
+++ b/commons-jcs-tck-tests/src/test/resources/ExcludeList
@@ -0,0 +1,19 @@
+# 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.
+
+# for tck this test needs to be excluded
+org.jsr107.tck.CachingTest#dummyTest
diff --git a/commons-jcs-tck-tests/src/test/resources/META-INF/services/javax.cache.annotation.BeanProvider b/commons-jcs-tck-tests/src/test/resources/META-INF/services/javax.cache.annotation.BeanProvider
new file mode 100644
index 0000000..ee527d8
--- /dev/null
+++ b/commons-jcs-tck-tests/src/test/resources/META-INF/services/javax.cache.annotation.BeanProvider
@@ -0,0 +1,18 @@
+# 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.
+
+org.apache.commons.jcs.jcache.OWBBeanProvider
diff --git a/commons-jcs-tck-tests/src/test/resources/log4j.properties b/commons-jcs-tck-tests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..18c5889
--- /dev/null
+++ b/commons-jcs-tck-tests/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+# 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.
+log4j.rootCategory=INFO, stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
diff --git a/doap_jcs.rdf b/doap_jcs.rdf
new file mode 100644
index 0000000..22894f1
--- /dev/null
+++ b/doap_jcs.rdf
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:asfext="http://projects.apache.org/ns/asfext#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:doap="http://usefulinc.com/ns/doap#" xml:lang="en">
+  <Project rdf:about="http://commons.apache.org/jcs/">
+    <name>Apache Commons JCS</name>
+    <homepage rdf:resource="http://commons.apache.org/jcs/"/>
+    <programming-language>Java</programming-language>
+    <category rdf:resource="http://projects.apache.org/category/library"/>
+    <license rdf:resource="http://usefulinc.com/doap/licenses/asl20"/>
+    <bug-database rdf:resource="http://issues.apache.org/jira/browse/JCS"/>
+    <download-page rdf:resource="http://commons.apache.org/jcs/DownloadPage.html"/>
+    <asfext:pmc rdf:resource="http://commons.apache.org/"/>
+    <shortdesc xml:lang="en">Java Caching System</shortdesc>
+    <description xml:lang="en">Comprehensive Caching System</description>
+    <repository>
+      <SVNRepository>
+        <browse rdf:resource="http://svn.apache.org/viewcvs.cgi/commons/proper/jcs/trunk/"/>
+        <location rdf:resource="http://svn.apache.org/repos/asf/commons/proper/jcs"/>
+      </SVNRepository>
+    </repository>
+    <mailing-list rdf:resource="http://commons.apache.org/jcs/mail-lists.html"/>
+  </Project>
+</rdf:RDF>
diff --git a/init-git-svn.sh b/init-git-svn.sh
new file mode 100755
index 0000000..af24a64
--- /dev/null
+++ b/init-git-svn.sh
@@ -0,0 +1,21 @@
+# 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.
+# !/bin/zsh -f
+cd .git;wget http://git.apache.org/authors.txt; cd ..
+git config svn.authorsfile ".git/authors.txt"
+git svn init --prefix=origin/ --tags=tags --trunk=trunk --branches=branches https://svn.apache.org/repos/asf/commons/proper/jcs
+git svn rebase
diff --git a/jcache-fast.sh b/jcache-fast.sh
new file mode 100755
index 0000000..a9ca7af
--- /dev/null
+++ b/jcache-fast.sh
@@ -0,0 +1,23 @@
+#! /bin/bash
+
+# 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.
+
+cd commons-jcs-core && mvn clean install -Dmaven.test.skip=true && cd -
+cd commons-jcs-jcache && mvn clean install && cd -
+cd commons-jcs-jcache-openjpa && mvn clean install && cd -
+cd commons-jcs-jcache-extras && mvn clean install && cd -
+cd commons-jcs-tck-tests && mvn clean install -Djcache.tck && cd -
+
diff --git a/maven-eclipse-codestyle.xml b/maven-eclipse-codestyle.xml
new file mode 100644
index 0000000..cb17060
--- /dev/null
+++ b/maven-eclipse-codestyle.xml
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<profiles version="8">
+<profile name="mergere-formats" version="8">
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="48"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="18"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="37"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="37"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="37"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="37"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="next_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+</profile>
+</profiles>
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d38855d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,695 @@
+<!--
+
+   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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-parent</artifactId>
+    <version>36</version>
+  </parent>
+
+  <artifactId>commons-jcs</artifactId>
+  <packaging>pom</packaging>
+  <version>2.0-beta-1</version>
+
+  <!--
+   * Build with -PjcacheTck to run the JSR-107 TCK
+  -->
+
+  <name>Apache Commons JCS</name>
+  <url>http://commons.apache.org/proper/commons-jcs/</url>
+  <description>Apache Commons JCS is a distributed, versatile caching system.</description>
+  <inceptionYear>2002</inceptionYear>
+
+  <issueManagement>
+    <system>jira</system>
+    <url>http://issues.apache.org/jira/browse/JCS</url>
+  </issueManagement>
+
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/jcs/tags/commons-jcs-2.0-beta-1</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/jcs/tags/commons-jcs-2.0-beta-1</developerConnection>
+    <url>http://svn.apache.org/viewvc/commons/proper/jcs/tags/commons-jcs-2.0-beta-1</url>
+  </scm>
+
+  <mailingLists>
+    <mailingList>
+      <name>Jakarta JCS User List Archive</name>
+      <subscribe />
+      <unsubscribe />
+      <archive>http://mail-archives.apache.org/mod_mbox/jakarta-jcs-users/</archive>
+    </mailingList>
+    <mailingList>
+      <name>Jakarta JCS Developer List Archive</name>
+      <subscribe />
+      <unsubscribe />
+      <archive>http://mail-archives.apache.org/mod_mbox/jakarta-jcs-dev/</archive>
+    </mailingList>
+    <mailingList>
+      <name>OLD User List Archive</name>
+      <subscribe />
+      <unsubscribe />
+      <archive>http://mail-archives.apache.org/mod_mbox/jakarta-turbine-jcs-user/</archive>
+    </mailingList>
+    <mailingList>
+      <name>OLD Developer List Archive</name>
+      <subscribe />
+      <unsubscribe />
+      <archive>http://mail-archives.apache.org/mod_mbox/jakarta-turbine-jcs-dev/</archive>
+    </mailingList>
+    <mailingList>
+      <name>Commons User List</name>
+      <subscribe>user-subscribe at commons.apache.org</subscribe>
+      <unsubscribe>user-unsubscribe at commons.apache.org</unsubscribe>
+      <post>user at commons.apache.org</post>
+      <archive>http://mail-archives.apache.org/mod_mbox/commons-user/</archive>
+      <otherArchives>
+        <otherArchive>http://markmail.org/list/org.apache.commons.users/</otherArchive>
+        <otherArchive>http://old.nabble.com/Commons---User-f319.html</otherArchive>
+        <otherArchive>http://www.mail-archive.com/user@commons.apache.org/</otherArchive>
+        <otherArchive>http://news.gmane.org/gmane.comp.jakarta.commons.user</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Commons Dev List</name>
+      <subscribe>dev-subscribe at commons.apache.org</subscribe>
+      <unsubscribe>dev-unsubscribe at commons.apache.org</unsubscribe>
+      <post>dev at commons.apache.org</post>
+      <archive>http://mail-archives.apache.org/mod_mbox/commons-dev/</archive>
+      <otherArchives>
+        <otherArchive>http://markmail.org/list/org.apache.commons.dev/</otherArchive>
+        <otherArchive>http://old.nabble.com/Commons---Dev-f317.html</otherArchive>
+        <otherArchive>http://www.mail-archive.com/dev@commons.apache.org/</otherArchive>
+        <otherArchive>http://news.gmane.org/gmane.comp.jakarta.commons.devel</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Commons Issues List</name>
+      <subscribe>issues-subscribe at commons.apache.org</subscribe>
+      <unsubscribe>issues-unsubscribe at commons.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/commons-issues/</archive>
+      <otherArchives>
+        <otherArchive>http://markmail.org/list/org.apache.commons.issues/</otherArchive>
+        <otherArchive>http://old.nabble.com/Commons---Issues-f25499.html</otherArchive>
+        <otherArchive>http://www.mail-archive.com/issues@commons.apache.org/</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Commons Commits List</name>
+      <subscribe>commits-subscribe at commons.apache.org</subscribe>
+      <unsubscribe>commits-unsubscribe at commons.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/commons-commits/</archive>
+      <otherArchives>
+        <otherArchive>http://markmail.org/list/org.apache.commons.commits/</otherArchive>
+        <otherArchive>http://www.mail-archive.com/commits@commons.apache.org/</otherArchive>
+      </otherArchives>
+    </mailingList>
+    <mailingList>
+      <name>Apache Announce List</name>
+      <subscribe>announce-subscribe at apache.org</subscribe>
+      <unsubscribe>announce-unsubscribe at apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/www-announce/</archive>
+      <otherArchives>
+        <otherArchive>http://markmail.org/list/org.apache.announce/</otherArchive>
+        <otherArchive>http://old.nabble.com/Apache-News-and-Announce-f109.html</otherArchive>
+        <otherArchive>http://www.mail-archive.com/announce@apache.org/</otherArchive>
+        <otherArchive>http://news.gmane.org/gmane.comp.apache.announce</otherArchive>
+      </otherArchives>
+    </mailingList>
+  </mailingLists>
+
+  <modules>
+    <module>commons-jcs-core</module>
+    <module>commons-jcs-jcache</module>
+    <module>commons-jcs-tck-tests</module>
+    <module>commons-jcs-jcache-extras</module>
+    <module>commons-jcs-jcache-openjpa</module>
+  </modules>
+
+  <developers>
+    <developer>
+      <id>asmuts</id>
+      <name>Aaron Smuts</name>
+      <email>asmuts at apache.org</email>
+      <organization />
+    </developer>
+    <developer>
+      <id>jtaylor</id>
+      <name>James Taylor</name>
+      <email>james at jamestaylor.org</email>
+      <organization />
+    </developer>
+    <developer>
+      <id>hchar</id>
+      <name>Hanson Char</name>
+      <email>hchar at apache.org</email>
+      <organization />
+    </developer>
+    <developer>
+      <id>tsavo</id>
+      <name>Travis Savo</name>
+      <email>tsavo at ifilm.com</email>
+      <organization>IFilm</organization>
+    </developer>
+    <developer>
+      <id>tv</id>
+      <name>Thomas Vandahl</name>
+      <email>tv at apache.org</email>
+      <organization />
+    </developer>
+    <developer>
+      <id>rmannibucau</id>
+      <name>Romain Manni-Bucau</name>
+      <email>rmannibucau at apache.org</email>
+      <organization />
+    </developer>
+  </developers>
+
+  <contributors>
+    <contributor>
+      <name>Scott Eade</name>
+      <email>seade at backstagetech.com.au</email>
+    </contributor>
+    <contributor>
+      <name>Michael Stevens</name>
+      <email>mstevens at etla.org</email>
+    </contributor>
+  </contributors>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}</targetPath>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes combine.children="append">
+            <exclude>.java-version</exclude>
+            <exclude>**/zipcodes.txt</exclude>
+            <exclude>**/download_jcs.cgi</exclude>
+            <exclude>**/.gitignore</exclude>
+            <exclude>**/derby.log</exclude>
+            <exclude>**/META-INF/services/javax.*</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.7</version>
+        <executions>
+          <execution>
+            <id>copy-notices</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target xmlns:if="ant:if" xmlns:unless="ant:unless">
+                <presetdef name="truth">
+                  <condition value="true" else="false" />
+                </presetdef>
+
+                <macrodef name="seek">
+                  <attribute name="name" />
+                  <attribute name="addto" />
+                  <sequential>
+                    <union id="@{name}-ws" />
+                    <step dir="${user.dir}" name="@{name}" addto="@{addto}" ws="@{name}-ws" />
+                  </sequential>
+                </macrodef>
+
+                <macrodef name="step">
+                  <attribute name="name" />
+                  <attribute name="dir" />
+                  <attribute name="addto" />
+                  <attribute name="ws" />
+                  <sequential>
+                    <local name="seenDir" />
+                    <truth property="seenDir">
+                      <resourcecount when="gt" count="0">
+                        <intersect id="intersection">
+                          <resources refid="@{ws}" />
+                          <file name="@{dir}" />
+                        </intersect>
+                      </resourcecount>
+                    </truth>
+                    <sequential unless:true="${seenDir}">
+                      <augment id="@{ws}">
+                        <file file="@{dir}" />
+                      </augment>
+                      <local name="exists" />
+                      <truth property="exists">
+                        <available file="@{dir}/@{name}" />
+                      </truth>
+
+                      <sequential if:true="${exists}">
+                        <augment id="@{addto}">
+                          <file file="@{dir}/@{name}" />
+                        </augment>
+                      </sequential>
+                      <sequential unless:true="${exists}">
+                        <local name="parent.dir" />
+                        <dirname property="parent.dir" file="@{dir}" />
+                        <step dir="${parent.dir}" name="@{name}" addto="@{addto}" ws="@{ws}" />
+                      </sequential>
+                    </sequential>
+                  </sequential>
+                </macrodef>
+
+                <truth property="pom-only">
+                  <equals arg1="${project.packaging}" arg2="pom" />
+                </truth>
+                <sequential unless:true="${pom-only}">
+                  <union id="notices" />
+                  <seek name="LICENSE.txt" addto="notices" />
+                  <seek name="NOTICE.txt" addto="notices" />
+
+                  <mkdir dir="${project.build.directory}/generated-resources/notices/META-INF" />
+                  <copy verbose="true" todir="${project.build.directory}/generated-resources/notices/META-INF">
+                    <resources refid="notices" />
+                  </copy>
+
+                  <mkdir dir="${project.build.directory}/generated-test-resources/notices/META-INF" />
+                  <copy verbose="true" todir="${project.build.directory}/generated-test-resources/notices/META-INF">
+                    <resources refid="notices" />
+                  </copy>
+
+                  <mkdir dir="${project.build.directory}/apidocs/META-INF" />
+                  <copy verbose="true" todir="${project.build.directory}/apidocs/META-INF">
+                    <resources refid="notices" />
+                  </copy>
+                </sequential>
+              </target>
+            </configuration>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.9.4</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-test-jar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <skipIfEmpty>true</skipIfEmpty>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>create-javadoc-jar</id>
+            <goals>
+              <goal>javadoc</goal>
+              <goal>jar</goal>
+            </goals>
+            <phase>package</phase>
+          </execution>
+        </executions>
+        <configuration>
+          <source>${maven.compiler.source}</source>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar-no-fork</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>attach-test-sources</id>
+            <goals>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.9.1</version>
+        <executions>
+          <execution>
+            <id>add-source-notices</id>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>add-resource</goal>
+            </goals>
+            <configuration>
+              <resources>
+                <resource>
+                  <directory>${project.build.directory}/generated-resources/notices</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+          <execution>
+            <id>add-test-notices</id>
+            <phase>generate-test-resources</phase>
+            <goals>
+              <goal>add-test-resource</goal>
+            </goals>
+            <configuration>
+              <resources>
+                <resource>
+                  <directory>${project.build.directory}/generated-test-resources/notices</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencyManagement>
+    <dependencies>
+
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>commons-jcs-core</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>commons-jcs-core</artifactId>
+        <version>${project.version}</version>
+        <type>test-jar</type>
+        <scope>test</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>commons-jcs-jcache</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <!--  REQUIRED FOR JCS CORE -->
+      <dependency>
+        <groupId>commons-logging</groupId>
+        <artifactId>commons-logging</artifactId>
+        <version>1.2</version>
+      </dependency>
+
+      <!--  JDBC DISK CACHE -->
+      <dependency>
+        <groupId>commons-dbcp</groupId>
+        <artifactId>commons-dbcp</artifactId>
+        <version>1.4</version>
+        <optional>true</optional>
+      </dependency>
+
+      <!--  JDBC DISK CACHE -->
+      <dependency>
+        <groupId>commons-pool</groupId>
+        <artifactId>commons-pool</artifactId>
+        <version>1.6</version>
+        <optional>true</optional>
+      </dependency>
+
+      <dependency>
+        <groupId>hsqldb</groupId>
+        <artifactId>hsqldb</artifactId>
+        <version>1.8.0.10</version>
+        <optional>true</optional>
+      </dependency>
+
+      <!--  For comparative performance tests only -->
+      <dependency>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-collections4</artifactId>
+        <version>${commons.collections.version}</version>
+        <scope>test</scope>
+      </dependency>
+
+      <!--  Test dependencies -->
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.11</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.hamcrest</groupId>
+        <artifactId>hamcrest-library</artifactId>
+        <version>1.3</version>
+        <scope>test</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>log4j</groupId>
+        <artifactId>log4j</artifactId>
+        <version>1.2.17</version>
+        <scope>test</scope>
+      </dependency>
+
+      <!-- Exclude for now -->
+      <!-- dependency>
+        <groupId>org.jgroups</groupId>
+        <artifactId>jgroups</artifactId>
+        <version>3.4.1.Final</version>
+        <optional>true</optional>
+      </dependency -->
+
+      <dependency>
+        <groupId>org.apache.velocity</groupId>
+        <artifactId>velocity-tools</artifactId>
+        <version>2.0</version>
+        <optional>true</optional>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-httpclient</groupId>
+        <artifactId>commons-httpclient</artifactId>
+        <version>3.1</version>
+        <optional>true</optional>
+      </dependency>
+
+      <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>servlet-api</artifactId>
+        <version>2.5</version>
+        <optional>true</optional>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-jcdi_1.0_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-interceptor_1.1_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-atinject_1.0_spec</artifactId>
+        <version>1.0</version>
+        <scope>provided</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.geronimo.specs</groupId>
+        <artifactId>geronimo-jcache_1.0_spec</artifactId>
+        <version>1.0-alpha-1</version>
+        <scope>provided</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>org.apache.openwebbeans</groupId>
+        <artifactId>openwebbeans-impl</artifactId>
+        <version>1.2.6</version>
+        <scope>test</scope>
+      </dependency>
+
+      <dependency>
+        <!-- Unfortunately the 1.0 version of this jar was built for Java 1.7+, see JCS-132 -->
+        <groupId>javax.cache</groupId>
+        <artifactId>test-domain</artifactId>
+        <version>${jsr107.api.version}</version>
+        <!-- should this be scope test? -->
+      </dependency>
+
+      <dependency>
+        <groupId>javax.cache</groupId>
+        <artifactId>app-domain</artifactId>
+        <version>${jsr107.api.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>javax.cache</groupId>
+        <artifactId>cache-tests</artifactId>
+        <version>${jsr107.api.version}</version>
+        <scope>test</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>javax.cache</groupId>
+        <artifactId>cache-tests</artifactId>
+        <classifier>tests</classifier>
+        <scope>test</scope>
+        <version>${jsr107.api.version}</version>
+      </dependency>
+
+    </dependencies>
+  </dependencyManagement>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>3.2</version>
+        <configuration>
+          <targetJdk>1.6</targetJdk>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>2.5.5</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <profiles>
+    <profile>
+      <id>sandbox</id>
+      <modules>
+        <module>commons-jcs-sandbox</module>
+      </modules>
+    </profile>
+    <profile>
+      <id>release</id>
+      <modules>
+        <module>commons-jcs-dist</module>
+      </modules>
+      <build>
+        <plugins>
+          <plugin>
+            <artifactId>maven-release-plugin</artifactId>
+            <configuration>
+              <autoVersionSubmodules>true</autoVersionSubmodules>
+            </configuration>
+          </plugin>
+          <plugin>
+            <artifactId>maven-assembly-plugin</artifactId>
+            <inherited>false</inherited>
+            <executions>
+              <execution>
+                <goals>
+                  <goal>single</goal>
+                </goals>
+                <!-- disable assembly:single from commons-parent
+                     release profile; multimodule assembly handled
+                     by dist module -->
+                <phase />
+              </execution>
+            </executions>
+          </plugin>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>create-source-jar</id>
+                <!-- suppress execution specified by parent profile -->
+                <phase />
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>site</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <configuration>
+              <aggregate>true</aggregate>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+  <properties>
+
+    <!-- Default configuration for compiler source and target JVM -->
+    <maven.compiler.sourceEncoding>UTF-8</maven.compiler.sourceEncoding>
+    <maven.compiler.source>1.6</maven.compiler.source>
+    <maven.compiler.target>1.6</maven.compiler.target>
+
+    <commons.componentid>jcs</commons.componentid>
+    <commons.release.version>2.0-beta-1</commons.release.version>
+    <commons.release.desc>(Java 6.0+)</commons.release.desc>
+    <!-- The RC version used in the staging repository URL. -->
+    <commons.rc.version>RC1</commons.rc.version>
+    <commons.jira.id>JCS</commons.jira.id>
+    <commons.site.path>commons-jcs</commons.site.path>
+    <commons.scmPubUrl>
+      https://svn.apache.org/repos/infra/websites/production/commons/content/proper/${commons.site.path}
+    </commons.scmPubUrl>
+    <!-- Ensure copies work OK (can be removed later when this is in parent POM) -->
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <commons.encoding>UTF-8</commons.encoding>
+    <commons.javadoc.java.link>http://docs.oracle.com/javase/6/docs/api/</commons.javadoc.java.link>
+
+    <jsr107.api.version>1.0.0</jsr107.api.version>
+    <commons.collections.version>4.0</commons.collections.version>
+    <commons.lang.version>3.3.2</commons.lang.version>
+
+    <test.type>Unit</test.type>
+  </properties>
+
+</project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
new file mode 100644
index 0000000..4f23987
--- /dev/null
+++ b/src/changes/changes.xml
@@ -0,0 +1,477 @@
+<?xml version="1.0" encoding="UTF-8"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>JCS Change Log</title>
+		<author email="dev at commons.apache.org">Apache Commons Developers</author>
+	</properties>
+	<body>
+		<release version="2.0" date="unreleased" description="JDK 1.6 based major release">
+            <action issue="JCS-139" dev="tv" type="fix" due-to="Youngho Cho">
+                PropertySetter doesn't set Enum properly. Added a test.
+            </action>
+            <action issue="JCS-136" dev="tv" type="fix" due-to="Youngho Cho">
+                OutOfMemoryError when using compressing serializer
+            </action>
+            <action issue="JCS-133" dev="sebb" type="fix">
+                RemoteUtils.getNamingURL does not handle IPv6 numeric addresses properly
+            </action>
+            <action issue="JCS-131" dev="tv" type="remove">
+                Remove KeyGeneratorUtil and AddressUtil as they are not used
+            </action>
+            <action issue="JCS-113" dev="tv" type="fix">
+                Improve error handling by bubbling up exceptions
+            </action>
+            <action dev="rmannibucau" type="add">
+                jcache-extra module to provide basic classes based on JCache API.
+            </action>
+            <action dev="rmannibucau" type="add">
+                Web CacheFilter to cache http responses.
+            </action>
+            <action dev="rmannibucau" type="add">
+                OpenJPA L2 Cache based on JCache API.
+            </action>
+            <action dev="rmannibucau" type="update">
+                Allow configuration to set its TimeFactorForMilliseconds and
+                use second by default.
+                This has as impact to remove "Seconds" suffix from corresponding configurations.
+            </action>
+            <action issue="JCS-115" dev="tv" type="fix">
+                JDBCDiskCachePoolAccessManager is thread hostile
+            </action>
+            <action issue="JCS-118" dev="olamy" type="add" due-to="Romain Manni-Bucau">
+                Add jcache implementation
+            </action>
+            <action issue="JCS-113" dev="sebb" type="fix">
+                Potential NPE in JDBCDiskCache
+                Fixed NPEs in getSize() and getStatistics()
+            </action>
+            <action issue="JCS-112" dev="sebb" type="fix">
+                RemoteCacheServer.logUpdateInfo bug updating put count
+            </action>
+            <action dev="tv" type="fix">
+                Fix updating of last access time, add tests for event handling
+            </action>
+            <action dev="tv" type="update">
+                Update documentation to reflect generics and API changes
+            </action>
+            <action dev="tv" type="remove">
+                Remove dependency on commons-lang3
+            </action>
+            <action dev="tv" type="add">
+                Add simple JMX monitoring feature by exposing the JCSAdminBean to JMX
+            </action>
+            <action dev="tv" type="update" issue="JCS-109" due-to="Xiong LIU">
+                Improve performance of BlockDisk.write(Serializable)
+            </action>
+            <action dev="tv" type="fix" issue="JCS-102" due-to="Robert Clark">
+                Reworked the interfaces to make the group cache typesafe
+            </action>
+            <action dev="tv" type="fix" issue="JCS-108" due-to="Xiong LIU">
+                Fix return value of BlockDisk.write(long, byte[])
+            </action>
+            <action dev="tv" type="update">
+                Change package to org.apache.commons.jcs, groupId to org.apache.commons,
+                artifactId to commons-jcs and version to 2.0-SNAPSHOT
+            </action>
+            <action dev="tv" type="fix" issue="JCS-95" due-to="Nicolas Gomez Navarrete">
+                Map region names to valid file names.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-106" due-to="Johannes Weberhofer">
+                Bad comparison of int with Long.MAX_VALUE.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-103" due-to="Pavel Novak">
+                MaxMemoryIdleTimeSeconds default value is wrongly documented.
+            </action>
+            <action dev="tv" type="update">
+                Use central thread pool instance to clean up the code.
+            </action>
+            <action dev="tv" type="update">
+                Use type-safe enums instead of integer constants to clean up the code.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-91" due-to="Diego Rivera">
+                JCS fails to properly dispose of the lateral TCP listener thread.
+                If a webapp that uses JCS lateral TCP cache is undeployed, the listener is left
+                hanging with no hope for exit.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-49" due-to="David Easley">
+                Contradictory documentation on RemoveUponRemotePut default value.
+            </action>
+            <action dev="tv" type="add" issue="JCS-94" due-to="Andrew Leamon">
+                Add getGroupNames() to all caches.
+            </action>
+            <action dev="tv" type="add" issue="JCS-94" due-to="Andrew Leamon">
+                LateralTCPService should implement getGroupKeys.
+            </action>
+            <action dev="tv" type="update" issue="JCS-75" due-to="Chiat Lam">
+                CompositeCache: Add method to get auxiliary caches, changed
+                visibility of isExpired() to protected.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-73" due-to="Alexander Kleymenov">
+                Concurrent cache access causes values loss.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-77" due-to="Matt Morrisson">
+                NullPointerException thrown by IndexedDiskCache if IndexedDisk calls fail to
+                initialize.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-90" due-to="Diego Rivera">
+                When issuing a shutDown() command, JCS fails to clean up the Queue Processor
+                thread.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-89" due-to="Diego Rivera">
+                UDP Discovery fails to report correct IP address to peers for back-connect
+                when InetAddress.getLocalHost() fails to return an externally-visible
+                address (i.e. returns a local address)
+            </action>
+            <action dev="tv" type="fix" issue="JCS-88" due-to="Diego Rivera">
+                Add a test to verify correct block size calculation
+            </action>
+            <action dev="tv" type="update">
+                Change the interfaces to use generics
+            </action>
+            <action dev="tv" type="fix" issue="JCS-86" due-to="Roman Solo">
+                Fix region properties documentation.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-22" due-to="Michael Jordan">
+                Throw an explicit CacheException when a cache manager instance cannot
+                read its configuration.
+            </action>
+            <action dev="tv" type="fix" issue="JCS-79" due-to="Graham Leggett">
+                Fix NPE in CompositeCacheConfigurator.parseRegion
+            </action>
+            <action dev="tv" type="fix" issue="JCS-68" due-to="Niall Gallagher">
+                Made RemoteCacheServer public, added remote cache remove feature to
+                JCSAdminBean
+            </action>
+            <action dev="tv" type="fix" issue="JCS-82" due-to="Nikunj Trivedi">
+                Use oos.writeUnshared() for LateralTCPSender
+            </action>
+            <action dev="tv" type="fix" issue="JCS-69" due-to="Michael Stevens">
+                Ensure shutdown of LateralCacheMonitor
+            </action>
+            <action dev="tv" type="fix" issue="JCS-11" due-to="Peter Schwarz">
+                Add method for programmatic configuration to JCS
+            </action>
+            <action dev="tv" type="fix" issue="JCS-84" due-to="Aleksandar Ivanisevic">
+                Increase precision of CREATE_TIME, fix name of UPDATE_TIME_SECONDS
+                column.
+            </action>
+            <action dev="tv" type="update">
+                Update IndexDisk and BlockDisk to use NIO in an attempt to fix
+                a timing-dependent test failure.
+            </action>
+            <action dev="tv" type="update">
+                Update build files to make JCS a true Apache Commons component.
+            </action>
+            <action dev="tv" type="update">
+                Set UTF-8 encoding on all source files
+            </action>
+            <action dev="tv" type="update">
+                Move site generation to Maven 2. Integrate site into Apache Commons.
+            </action>
+            <action dev="tv" type="fix">
+                Fix VelocityServlet deprecation
+            </action>
+			<action dev="bayard" type="add">
+			    Upgraded Commons Lang dependency to 3.0
+			</action>
+            <action dev="tv" type="update">
+                Move to JDK 5 Generics
+            </action>
+            <action dev="tv" type="update">
+                Move to JDK 5 Concurrent
+            </action>
+            <action dev="tv" type="update">
+                Move build system to Maven 2
+            </action>
+		</release>
+		<release version="1.3.3.5" date="2009-08-12" description="tempbuild">
+			<action dev="asmuts" type="fix" issue="JCS-67">Fixed bug in
+				indexed disk cache. Partial key removal was adding duplicates in the
+				recycle bin. This lead to the multiple keys pointing to the same spot
+				on disk.</action>
+		</release>
+		<release version="1.3.3.4" date="2009-08-11" description="tempbuild">
+			<action dev="asmuts" type="fix" issue="JCS-66">Fixed bug in block
+				disk cache. It couldn't handle items with more than 127 blocks. Now
+				it can. (Note: for performance reasons, you should try to fit your
+				items in as few blocks as possible.)</action>
+		</release>
+		<release version="1.3.3.3" date="2009-07-23" description="tempbuild">
+			<action dev="asmuts" type="update">TCP Lateral now uses a queuing
+				zombie service during recovery.</action>
+			<action dev="asmuts" type="update">Refactored UDP Discovery.
+			</action>
+			<action dev="asmuts" type="fix" issue="JCS-60">Slots for same key
+				updates now get added to the recycle bin.</action>
+			<action dev="asmuts" type="update" issue="JCS-58">Added File Disk
+				Cache.</action>
+			<action dev="asmuts" type="fix" issue="JCS-53">Default
+				properties should now work.</action>
+			<action dev="asmuts" type="update">Caught Throwable in JDBC disk
+				cache shrinker, so it won't die.</action>
+		</release>
+		<release version="1.3.3.2" date="2009-06-11" description="tempbuild">
+			<action dev="asmuts" type="update">Added a compressing serializer.
+			</action>
+			<action dev="asmuts" type="update">Added an LHMLRUMemoryCache.
+			</action>
+		</release>
+		<release version="1.3.3.1" date="2009-05-22" description="tempbuild">
+			<action dev="asmuts" type="update">Added a clearDiskOnStartup
+				configuration option for the IndexedDisk Cache. False by default.
+			</action>
+			<action dev="asmuts" type="fix" issue="JCS-56">Fixed data chunking
+				bug in BlockDisk.</action>
+		</release>
+		<release version="1.3.3.0" date="2009-05-22" description="tempbuild">
+			<action dev="asmuts" type="update">Made "openTimeOut" and
+				"socketTimeOut" configurable on the TCP Lateral.</action>
+		</release>
+		<release version="1.3.2.9" date="2009-02-02" description="tempbuild">
+			<action dev="asmuts" type="fix">Fixed bug in Remote Http Client
+				URL creation for query strings.</action>
+			<action dev="asmuts" type="fix" issue="JCS-55" due-to="Alexander Sofronov"> Fixed
+				disk cache custom serializer injection.</action>
+		</release>
+		<release version="1.3.2.8" date="2008-12-16" description="tempbuild">
+			<action dev="asmuts" type="fix">Fixed bug remote cache listener
+				interface.</action>
+		</release>
+		<release version="1.3.2.7" date="2008-12-15" description="tempbuild">
+			<action dev="asmuts" type="update">Added a simple http remote
+				cache client and server.</action>
+			<action dev="asmuts" type="fix">Fixed bug in the MySQLDiskCache
+				optimizer. It can now use a shared pool.</action>
+		</release>
+		<release version="1.3.2.6" date="2008-12-01" description="tempbuild">
+			<action dev="asmuts" type="fix">Fixed balking bug in
+				getMatching( String pattern ) API.</action>
+			<action dev="asmuts" type="fix">Fixed event naming bug in
+				getMatching( String pattern ) API.</action>
+		</release>
+		<release version="1.3.2.5" date="2008-11-20" description="tempbuild">
+			<action dev="asmuts" type="update">Added a getMatching( String
+				pattern ) API.</action>
+		</release>
+		<release version="1.3.2.4" date="2008-?" description="tempbuild">
+			<action dev="asmuts" type="update">Added the ability to inject a
+				custom event queue. You simply specify the classname as the type.
+			</action>
+			<action dev="asmuts" type="update">Added the ability to share a
+				connection pool amongst different JBC disk cache instances.</action>
+		</release>
+		<release version="1.3.2.3" date="2008-09-26" description="tempbuild">
+			<action dev="asmuts" type="update">Added the ability to inject a
+				custom RMI socket factory to be used by the remote cache server.
+			</action>
+		</release>
+		<release version="1.3.2.2" date="2008-09-17" description="tempbuild">
+			<action dev="asmuts" type="update">Added a registry keep alive and
+				restore option for the remote cache server.</action>
+		</release>
+		<release version="1.3.2.1" date="2008-09-08" description="tempbuild">
+			<action dev="asmuts" type="update">Made all disk cache managers
+				handle custom event loggers.</action>
+		</release>
+		<release version="1.3.2.0" date="2008-08-29" description="tempbuild">
+			<action dev="asmuts" type="update"> Added eventLogging to major
+				auxiliaries and to the auxiliary factory interface. This allows you
+				to time and monitor the internal working of the cache.</action>
+			<action dev="asmuts" type="update">Moving to JDK 1.4 compliance.
+				1.3 not supported.</action>
+			<action dev="asmuts" type="update"> Added the ability to inject custom
+				serializers into auxiliary caches.</action>
+			<action dev="asmuts" type="fix" issue="JCS-40" due-to="Niall Gallagher"> Added a
+				work-around to the host address resolution used by the TCP lateral
+				and the remote cache.</action>
+			<action dev="asmuts" type="fix" issue="JCS-38" due-to="Timothy Cronin"> Added
+				getSource to IElementEvent interface.</action>
+			<action dev="asmuts" type="update" issue="JCS-44" due-to="Maxim Gordienko"> Made
+				spool chunk size configurable.</action>
+			<action dev="asmuts" type="fix" issue="JCS-41" due-to="Niall Gallagher"> Added
+				RMI socket connect timeout. Made this configurable on the server and
+				client.</action>
+			<action dev="asmuts" type="fix" issue="JCS-36" due-to="Matthias Kerkhoff">Fixed
+				client decrement count in indexed disk cache.</action>
+			<action dev="asmuts" type="fix" issue="JCS-32" due-to="Andy">Moved
+				shutdown hook from disk cache to composite cache manager.</action>
+		</release>
+		<release version="1.3.1.0" date="2008-04-11" description="tempbuild">
+			<action dev="asmuts" type="update" due-to="Chris Fairbanks"> Added getMultiple()
+				method to ICache</action>
+		</release>
+		<release version="1.3" date="2007-06-05" description="First formal release">
+			<action dev="tv" type="update"> Updated/added license headers in all
+				files.</action>
+		</release>
+		<release version="1.2.7.9.3" date="in SVN">
+			<action dev="asmuts" type="fix" issue="JCS-15" due-to="Kevin Preece"> Fixed
+				partial key and group id removal bug in indexed disk cache.</action>
+			<action dev="asmuts" type="fix" issue="JCS-20" due-to="Alistair Forbes"> Fixed
+				partial key removal SQL syntax problem with the JDBC disk cache.
+			</action>
+			<action dev="asmuts" type="fix" issue="JCS-21" due-to="Michael Stevens"> Fixed a
+				few minor missing locks.</action>
+		</release>
+		<release version="1.2.7.9.2" date="in SVN">
+			<action dev="asmuts" type="update"> Added a summary page to the
+				JCSAdmin.jsp that just dumps the stats for a region. It doesn't
+				display all the keys.</action>
+			<action dev="asmuts" type="update"> Added get, put, and remove counts
+				to the lateral and remote stats. Added hit count to disk cache
+				stats.</action>
+			<action dev="asmuts" type="fix" issue="JCS-12" due-to="John Klame"> Fixed
+				non thread safe OOS writes in the TCP Lateral Sender. This was
+				causing endless loops deep down in the OOS code.</action>
+		</release>
+		<release version="1.2.7.9" date="in SVN">
+			<action dev="asmuts" type="fix" issue="JCS-1"> Fixed last element too
+				small recycle bin bug.</action>
+			<action dev="asmuts" type="update" issue="JCS-2"> Added the ability to
+				send all items to disk or to use the disk merely as a swap. This is
+				done by setting the DiskUsagePattern on the cache attributes for a
+				region.</action>
+			<action dev="asmuts" type="fix" issue="JCS-3" due-to="Peter Schwarz"> Disk
+				optimization now occurs in place, without using additional files. It
+				is far more efficient.</action>
+			<action dev="asmuts" type="fix" issue="JCS-4"> The disk cache will not
+				optimize on shutdown if the freedata size is 0.</action>
+			<action dev="asmuts" type="fix" issue="JCS-8"> Remote cache client
+				will now shutdown properly.</action>
+			<action dev="asmuts" type="fix" issue="JCS-9"> Only send memory items
+				to disk auxiliaries on shutdown.</action>
+		</release>
+		<release version="1.2.7.8" date="in SVN">
+			<action dev="asmuts" type="update"> Added the ability to schedule
+				optimizations for the MySQL disk cache. It can also recover from
+				optimization failure and repair the table. It's been tested and is
+				running in a production environment.</action>
+		</release>
+		<release version="1.2.7.7" date="in SVN">
+			<action dev="asmuts" type="fix" due-to="Brian Crow  @noteworthyms.com"> Fixed the array index
+				out of bounds exception in the Sorted Preferential Array.</action>
+		</release>
+		<release version="1.2.7.6" date="in SVN">
+			<action dev="asmuts" type="fix" due-to="Rick Szeto @vizible.com"> Fixed UDP discovery
+				configuration problem. The discovery address was not being used. It
+				was broken in the last release.</action>
+			<action dev="asmuts" type="fix" due-to="Alistair Forbes"> Fixed disk cache class
+				cast problem when running inside the remote cache. The disk cache
+				was casting to an instance and not the ICacheElement interface.
+			</action>
+		</release>
+		<release version="1.2.7.3" date="in SVN">
+			<action dev="asmuts" type="update" due-to=""> You can now
+				configure the remote cache client to not receive from the remote
+				server.</action>
+			<action dev="asmuts" type="update" due-to=""> Added a new xdoc
+				for the Remote Cache properties.</action>
+			<action dev="asmuts" type="update" due-to=""> Improved the jcs
+				admin jsp: added stats details, better navigation, item lookup, and
+				remove confirmation.</action>
+		</release>
+		<release version="1.2.7.2" date="in SVN">
+			<action dev="asmuts" type="update" due-to=""> You can configure
+				the remote cache to use a custom RMI Socket Factory with socket
+				timeouts.</action>
+			<action dev="hchar" type="fix" due-to=""> Removed redundant
+				system out logging from remote cache server.</action>
+			<action dev="asmuts" type="update" due-to=""> Added additional
+				stats for remote cache.</action>
+		</release>
+		<release version="1.2.7.1" date="in SVN">
+			<action dev="asmuts" type="update" due-to=""> Added a JDBC disk
+				cache. It has been tested with MYSql and HSQL.</action>
+			<action dev="asmuts" type="update" due-to=""> Added a special
+				HSQL implementation of the JDBC disk cache. This replaces the
+				previous experimental HSQL implementation.</action>
+			<action dev="asmuts" type="update" due-to=""> Added a new xdoc
+				for the JDBC Disk Cache properties.</action>
+		</release>
+		<release version="1.2.7.0" date="in SVN">
+			<action dev="asmuts" type="update" due-to=""> Removed
+				dependencies on commons-lang and on commons-collections from the
+				core of JCS.</action>
+			<action dev="asmuts" type="update" due-to=""> Increased test
+				coverage.</action>
+			<action dev="asmuts" type="fix" due-to=""> Fixed entry set
+				creation problem in LRUMap.</action>
+		</release>
+		<release version="1.2.6.9" date="in SVN">
+			<action dev="asmuts" type="update" due-to=""> The remote cache
+				now sends a byte array to the server instead of the class. This
+				allows you to run the remote server without copies of your objects
+				in its classpath. This makes the remote server far easier to use.
+			</action>
+			<action dev="asmuts" type="update" due-to=""> Created an
+				interface for cache element serializers and made a default
+				implementation. This will allow use to use other serialization
+				mechanisms in the future. I converted the remote cache to use the
+				standard serializer.</action>
+			<action dev="asmuts" type="update" due-to=""> Added several new
+				unit tests.</action>
+			<action dev="asmuts" type="update" due-to=""> Improved Javadocs
+				throughout.</action>
+		</release>
+		<release version="1.2.6.8" date="in SVN">
+			<action dev="asmuts" type="fix" due-to=""> Moved synchronization
+				from the client to the sorted preferential array. This solved the
+				index out of bounds exception that appeared periodically on disk
+				cleanup.</action>
+			<action dev="asmuts" type="update" due-to=""> Improved Javadocs
+				throughout.</action>
+		</release>
+		<release version="1.2.6.7" date="in SVN">
+			<action dev="asmuts" type="fix" due-to="Adam Siefker @amazon.com"> Fixed shutdown for
+				lateral. Added shutdown observer framework. Changed listener thread
+				to a daemon. Made executor on listener use a daemon setting thread
+				factory.</action>
+			<action dev="asmuts" type="fix" due-to=""> Fixed abstract inner
+				class references for Jikes.</action>
+			<action dev="asmuts" type="update" due-to=""> Improved Javadocs
+				throughout.</action>
+		</release>
+		<release version="1.2.6.6" date="in SVN">
+			<action dev="asmuts" type="fix"> Properties are loaded using the
+				current thread's classloader by default.</action>
+			<action dev="asmuts" type="fix"> When a config file is not found a
+				descriptive error message is printed to the logs.</action>
+			<action dev="asmuts" type="update"> Made an interface for the cache
+				manager to improve testability.</action>
+			<action dev="asmuts" type="update"> Auxiliaries are assigned a cache
+				manager and the lateral TCP has a settable source id, so it can be
+				tested.</action>
+			<action dev="asmuts" type="update"> Increased JGroups version to 2.2.8.
+				It works again. The old JGroups didn't work for unknown reasons.
+			</action>
+			<action dev="asmuts" type="update"> Cleaned up some javadocs.</action>
+			<action dev="asmuts" type="update"> Improved info level logging for TCP
+				lateral.</action>
+			<action dev="asmuts" type="update"> Added configuration properties
+				xdocs for TCP lateral, indexed disk, and memory cache.</action>
+			<action dev="asmuts" type="update"> Created concurrent tests for TCP
+				lateral to verify correct region behavior.</action>
+			<action dev="asmuts" type="update"> Made BDBJE work with new auxiliary
+				changes.</action>
+			<action dev="asmuts" type="fix"> UDP discovery no longer activates
+				if the lateral type isn't TCP.</action>
+			<action dev="asmuts" type="fix"> Reduced lateral get timeout to 1
+				second, rather than 10. We still need to disable gets when a timeout
+				occurs.</action>
+		</release>
+	</body>
+</document>
diff --git a/src/changes/release-notes.vm b/src/changes/release-notes.vm
new file mode 100644
index 0000000..5d14423
--- /dev/null
+++ b/src/changes/release-notes.vm
@@ -0,0 +1,140 @@
+## 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.
+##
+
+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.
+
+
+                        ${project.name}
+                            Version ${version}
+                           Release Notes
+
+
+INTRODUCTION:
+
+This document contains the release notes for the ${version} version of Apache Commons JCS.
+Commons JCS is a distributed caching system written in Java. It is intend to speed up 
+applications by providing a means to manage cached data of various dynamic natures.
+
+JCS 2.0 and onwards now targets Java 6.0, making use of features that arrived with Java 5.0 
+such as generics and concurrency.
+
+For the advice on upgrading from 1.x to 2.x, see the following page: 
+
+    ${project.url}UpgradingFrom13.html
+
+$introduction.replaceAll("(?<!\015)\012", "
+").replaceAll("(?m)^ +","")
+
+## N.B. the available variables are described here:
+## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html
+##
+## Hack to improve layout: replace all pairs of spaces with a single new-line
+$release.description.replaceAll("  ", "
+")
+
+## set up indent sizes. Only change indent1
+#set($props=${project.properties})
+#set($jiralen=$props.get("commons.jira.id").length())
+## indent1 =   POOL-nnnn:
+#set($blanklen=$jiralen+6)## +6 for "-nnnn:"
+## must be at least as long as the longest JIRA id
+#set($blanks="                                  ")
+#set($indent1=$blanks.substring(0,$blanklen))
+## indent2 allows for issue wrapper
+#set($indent2="$indent1   ")
+##
+#macro ( processaction )
+## Use replaceAll to fix up LF-only line ends on Windows.
+#set($action=$actionItem.getAction().replaceAll("\n","
+"))
+## Fix up indentation for multi-line action descriptions
+#set($action=$action.replaceAll("(?m)^  +",$indent2))
+#if ($actionItem.getIssue())
+#set($issue="$actionItem.getIssue():")
+## Pad shorter issue numbers
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#else
+#set($issue=$indent1)
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=" Thanks to $actionItem.getDueTo().")
+#else
+#set($dueto="")
+#end
+o $issue ${action}$dueto
+#set($action="")
+#set($issue="")
+#set($dueto="")
+#end
+##
+#if ($release.getActions().size() == 0)
+No changes defined in this version.
+#else
+Changes in this version include:
+
+#if ($release.getActions('add').size() !=0)
+New features:
+#foreach($actionItem in $release.getActions('add'))
+#processaction()
+#end 
+#end
+
+#if ($release.getActions('fix').size() !=0)
+Fixed Bugs:
+#foreach($actionItem in $release.getActions('fix'))
+#processaction()
+#end
+#end
+
+#if ($release.getActions('update').size() !=0)
+Changes:
+#foreach($actionItem in $release.getActions('update'))
+#processaction()
+#end
+#end
+
+#if ($release.getActions('remove').size() !=0)
+Removed:
+#foreach($actionItem in $release.getActions('remove'))
+#processaction()
+#end
+#end
+## End of main loop
+#end
+
+Historical list of changes: ${project.url}changes-report.html
+
+For complete information on ${project.name}, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache ${project.name} website:
+
+${project.url}
\ No newline at end of file
diff --git a/src/scripts/cpappend.bat b/src/scripts/cpappend.bat
new file mode 100644
index 0000000..9e7b411
--- /dev/null
+++ b/src/scripts/cpappend.bat
@@ -0,0 +1,18 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+echo %_LIBJARS%
+set _LIBJARS=%_LIBJARS%;%1
diff --git a/src/scripts/directory.bat b/src/scripts/directory.bat
new file mode 100644
index 0000000..c407836
--- /dev/null
+++ b/src/scripts/directory.bat
@@ -0,0 +1,17 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+set CURDIR=%2
diff --git a/src/scripts/jgtest.bat b/src/scripts/jgtest.bat
new file mode 100644
index 0000000..34205ab
--- /dev/null
+++ b/src/scripts/jgtest.bat
@@ -0,0 +1,26 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+rem-Dlog4j.configuration=I:/dev/jakarta-turbine-jcs/src/scripts/log4j.properties
+
+:run
+java org.jgroups.tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555
+
+
diff --git a/src/scripts/jgtest_send.bat b/src/scripts/jgtest_send.bat
new file mode 100644
index 0000000..efa6697
--- /dev/null
+++ b/src/scripts/jgtest_send.bat
@@ -0,0 +1,26 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+rem-Dlog4j.configuration=I:/dev/jakarta-turbine-jcs/src/scripts/log4j.properties
+
+:run
+java org.jgroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555 -ttl 32
+
+
diff --git a/src/scripts/jgtest_send.sh b/src/scripts/jgtest_send.sh
new file mode 100644
index 0000000..5ca9b84
--- /dev/null
+++ b/src/scripts/jgtest_send.sh
@@ -0,0 +1,35 @@
+# 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.
+# !/bin/zsh -f
+THIS_DIR=$(dirname $0)
+
+export CLASSPATH=${THIS_DIR}/../../src/conf
+export CLASSPATH=${CLASSPATH}:${THIS_DIR}/../../target/test-classes
+export CLASSPATH=${CLASSPATH}:${THIS_DIR}/../../target/classes
+export CLASSPATH=${CLASSPATH}:.
+
+for i in `find ${THIS_DIR}/../../jars -name "*.jar" `
+do
+        export CLASSPATH=${CLASSPATH}:$i
+done
+
+
+
+echo ${CLASSPATH}
+# -Xrunhprof:cpu=samples,depth=6,thread=y
+
+${JAVA_HOME}/bin/java  -ms90m -mx400m -verbosegc -classpath "${CLASSPATH}" org.jgroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555 -ttl 32
diff --git a/src/scripts/prep.bat b/src/scripts/prep.bat
new file mode 100644
index 0000000..63327dc
--- /dev/null
+++ b/src/scripts/prep.bat
@@ -0,0 +1,48 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at rem echo off
+
+:setcurdir
+call setCURDIR
+echo %CURDIR%
+
+goto javahome
+
+:javahome
+if "%JAVA_HOME%" == "" goto noJavaHome
+goto setcpbase
+
+:noJavaHome
+echo Warning: JAVA_HOME environment variable is not set.
+set JAVA_HOME=C:\jdk1.2.2
+
+:setcpbase
+set CLASSPATH=.
+set CLASSPATH=%CLASSPATH%;%CURDIR%\src\conf\
+set CLASSPATH=%CLASSPATH%;%CURDIR%\target\classes\
+set CLASSPATH=%CLASSPATH%;%CURDIR%\target\test-classes\
+set CLASSPATH=%CLASSPATH%;%CURDIR%\auxiliary-builds\jdk14\target\classes\
+goto jars
+
+:jars
+set _LIBJARS=
+for %%i in (%CURDIR%\jars\*.jar) do call %CURDIR%\src\scripts\cpappend.bat %%i
+if not "%_LIBJARS%" == "" goto addLibJars
+
+:addLibJars
+set CLASSPATH=%CLASSPATH%;%_LIBJARS%
+
diff --git a/src/scripts/remoteCacheStats.bat b/src/scripts/remoteCacheStats.bat
new file mode 100644
index 0000000..e5ed54a
--- /dev/null
+++ b/src/scripts/remoteCacheStats.bat
@@ -0,0 +1,28 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo on
+
+call prep.bat
+
+:run
+rem set DBUGPARM=-classic -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=5000,suspend=n
+%JAVA_HOME%\bin\java %DBUGPARM% -ms10m -mx20m -classpath %CLASSPATH% "-Djava.security.policy=%CURDIR%\src\conf\cache.policy" org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory -stats /remote.cache%1.ccf
+
+
+
+
+
diff --git a/src/scripts/remoteCacheStats.sh b/src/scripts/remoteCacheStats.sh
new file mode 100644
index 0000000..ef40c9a
--- /dev/null
+++ b/src/scripts/remoteCacheStats.sh
@@ -0,0 +1,44 @@
+# 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.
+# ! /bin/sh
+
+export CLASSPATH=.
+export CLASSPATH=${CLASSPATH}:`dirname $0`/../conf:/usr/java/jcs/conf:/usr/java/jcs/conf/
+
+THISDIR=`dirname $0`
+
+for i in `find ${THISDIR}/../lib -name "*.jar" `
+do
+        export CLASSPATH=${CLASSPATH}:$i
+done
+echo "Classpath = ${CLASSPATH}"
+
+
+POLICY="-Djava.security.policy=`dirname $0`/../conf/cache.policy"
+
+HEAP="-Xms10m -Xmx20m"
+
+DEBUG="-verbose:gc -XX:+PrintTenuringDistribution"
+
+ARGS="$HEAP $DEBUG $POLICY"
+
+echo $ARGS
+
+java  $ARGS org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory -stats "$1"
+
+
+
diff --git a/src/scripts/setCURDIR.bat b/src/scripts/setCURDIR.bat
new file mode 100644
index 0000000..128754a
--- /dev/null
+++ b/src/scripts/setCURDIR.bat
@@ -0,0 +1,27 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+rem cd ..
+rem dir | find "Directory" > }{.bat
+rem echo set CURDIR=%%2> directory.bat
+rem for %%a in (call del) do %%a }{.bat
+rem cd bin
+
+cd ..\..
+set CURDIR=%CD%
+echo %CURDIR%
+cd src
+cd scripts
diff --git a/src/scripts/startJetty.bat b/src/scripts/startJetty.bat
new file mode 100644
index 0000000..58112cc
--- /dev/null
+++ b/src/scripts/startJetty.bat
@@ -0,0 +1,25 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+:run
+rem set DBUGPARM=-classic -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=5000,suspend=n
+%JAVA_HOME%\bin\java %DBUGPARM% -ms10m -mx200m -classpath %CLASSPATH% "-Djava.security.policy=%CURDIR%/conf/cache.policy" com.mortbay.Jetty.Server %CURDIR%/conf/myjetty%1.xml
+
+
diff --git a/src/scripts/startRemoteCache.bat b/src/scripts/startRemoteCache.bat
new file mode 100644
index 0000000..607fe62
--- /dev/null
+++ b/src/scripts/startRemoteCache.bat
@@ -0,0 +1,28 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+:run
+rem set DBUGPARM=-classic -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=5000,suspend=n
+
+rem -XX:+PrintTenuringDistribution
+
+java %DBUGPARM% -verbosegc  -ms10m -mx200m -classpath %CLASSPATH% "-Djava.security.policy=%CURDIR%\src\conf\cache.policy" org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory /remote.cache%1.ccf
+
+
diff --git a/src/scripts/startRemoteCache.sh b/src/scripts/startRemoteCache.sh
new file mode 100644
index 0000000..78fc23f
--- /dev/null
+++ b/src/scripts/startRemoteCache.sh
@@ -0,0 +1,51 @@
+# 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.
+# ! /bin/sh
+
+export CLASSPATH=.
+export CLASSPATH=${CLASSPATH}:`dirname $0`/../conf:/usr/java/jcs/conf:/usr/java/jcs/conf/
+
+THISDIR=`dirname $0`
+
+for i in `find ${THISDIR}/../lib -name "*.jar" `
+do
+        export CLASSPATH=${CLASSPATH}:$i
+done
+echo "Classpath = ${CLASSPATH}"
+
+# START THE REGISTRY
+if [ "$2" != "" ]; then
+  echo "Starting the registry on port $2"
+  rmiregistry $2 &
+else
+  echo "Not starting registry, since no port was supplied."
+fi
+
+POLICY="-Djava.security.policy=`dirname $0`/../conf/cache.policy"
+
+HEAP="-Xms128m -Xmx512m"
+
+DEBUG="-verbose:gc -XX:+PrintTenuringDistribution"
+
+ARGS="$HEAP $DEBUG $POLICY"
+
+echo $ARGS
+
+java  $ARGS org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory "$1"
+
+
+
diff --git a/src/scripts/stopRemoteCache.bat b/src/scripts/stopRemoteCache.bat
new file mode 100644
index 0000000..79a4826
--- /dev/null
+++ b/src/scripts/stopRemoteCache.bat
@@ -0,0 +1,25 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+:run
+rem org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory
+rem set DBUGPARM=-classic -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=5000,suspend=n
+%JAVA_HOME%\bin\java %DBUGPARM% -ms1m -mx20m -classpath %CLASSPATH% "-Djava.security.policy=C:/dev/cache/props/cache.policy" org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheServerFactory -shutdown /remote.cache.ccf
+
diff --git a/src/scripts/tester.bat b/src/scripts/tester.bat
new file mode 100644
index 0000000..8a75693
--- /dev/null
+++ b/src/scripts/tester.bat
@@ -0,0 +1,27 @@
+rem Licensed to the Apache Software Foundation (ASF) under one
+rem or more contributor license agreements.  See the NOTICE file
+rem distributed with this work for additional information
+rem regarding copyright ownership.  The ASF licenses this file
+rem to you under the Apache License, Version 2.0 (the
+rem "License"); you may not use this file except in compliance
+rem with the License.  You may obtain a copy of the License at
+rem
+rem   http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing,
+rem software distributed under the License is distributed on an
+rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+rem KIND, either express or implied.  See the License for the
+rem specific language governing permissions and limitations
+rem under the License.
+ at echo off
+
+call prep.bat
+
+rem-Dlog4j.configuration=I:/dev/jakarta-turbine-jcs/src/scripts/log4j.properties
+
+:run
+java -ms90m -mx400m -verbosegc org.apache.commons.jcs.access.TestCacheAccess /cache%1.ccf  %2 %3 %4 %5
+
+
+
diff --git a/src/scripts/tester.sh b/src/scripts/tester.sh
new file mode 100644
index 0000000..c3370e1
--- /dev/null
+++ b/src/scripts/tester.sh
@@ -0,0 +1,35 @@
+# 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.
+# !/bin/zsh -f
+THIS_DIR=$(dirname $0)
+
+export CLASSPATH=${THIS_DIR}/../../src/conf
+export CLASSPATH=${CLASSPATH}:${THIS_DIR}/../../target/test-classes
+export CLASSPATH=${CLASSPATH}:${THIS_DIR}/../../target/classes
+export CLASSPATH=${CLASSPATH}:.
+
+for i in `find ${THIS_DIR}/../../jars -name "*.jar" `
+do
+        export CLASSPATH=${CLASSPATH}:$i
+done
+
+
+
+echo ${CLASSPATH}
+# -Xrunhprof:cpu=samples,depth=6,thread=y
+
+${JAVA_HOME}/bin/java  -ms90m -mx400m -verbosegc -classpath "${CLASSPATH}" org.apache.commons.jcs.access.TestCacheAccess /cache$argv.ccf
diff --git a/src/scripts/zipcodes.txt b/src/scripts/zipcodes.txt
new file mode 100644
index 0000000..c1e3d27
--- /dev/null
+++ b/src/scripts/zipcodes.txt
@@ -0,0 +1,42195 @@
+82001,Cheyenne
+82002,Cheyenne
+82003,Cheyenne
+82005,FE Warren AFB
+82006,Cheyenne
+82007,Cheyenne
+82008,Cheyenne
+82009,Cheyenne
+82010,Cheyenne
+82050,Albin
+82051,Bosler
+82052,Buford
+82053,Burns
+82054,Carpenter
+82055,Centennial
+82058,Garrett
+82059,Granite Canon
+82060,Hillsdale
+82061,Horse Creek
+82063,Jelm
+82070,Laramie
+82071,Laramie
+82072,Laramie
+82073,Laramie
+82081,Meriden
+82082,Pine Bluffs
+82083,Rock River
+82084,Tie Siding
+82190,Yellowstone National Park
+82201,Wheatland
+82210,Chugwater
+82212,Fort Laramie
+82213,Glendo
+82214,Guernsey
+82215,Hartville
+82217,Hawk Springs
+82218,Huntley
+82219,Jay Em
+82221,Lagrange
+82222,Lance Creek
+82223,Lingle
+82224,Lost Springs
+82225,Lusk
+82227,Manville
+82229,Shawnee
+82240,Torrington
+82242,Van Tassell
+82243,Veteran
+82244,Yoder
+82301,Rawlins
+82310,Jeffrey City
+82321,Baggs
+82322,Bairoil
+82323,Dixon
+82324,Elk Mountain
+82325,Encampment
+82327,Hanna
+82329,Medicine Bow
+82331,Saratoga
+82332,Savery
+82334,Sinclair
+82335,Walcott
+82336,Wamsutter
+82401,Worland
+82410,Basin
+82411,Burlington
+82412,Byron
+82414,Cody
+82420,Cowley
+82421,Deaver
+82422,Emblem
+82423,Frannie
+82426,Greybull
+82427,Hamilton Dome
+82428,Hyattville
+82430,Kirby
+82431,Lovell
+82432,Manderson
+82433,Meeteetse
+82434,Otto
+82435,Powell
+82440,Ralston
+82441,Shell
+82442,Ten Sleep
+82443,Thermopolis
+82450,Wapiti
+82501,Riverton
+82510,Arapahoe
+82512,Crowheart
+82513,Dubois
+82514,Fort Washakie
+82515,Hudson
+82516,Kinnear
+82520,Lander
+82523,Pavillion
+82524,Saint Stephens
+82601,Casper
+82602,Casper
+82604,Casper
+82605,Casper
+82609,Casper
+82615,Shirley Basin
+82620,Alcova
+82630,Arminto
+82631,Bill
+82633,Douglas
+82635,Edgerton
+82636,Evansville
+82637,Glenrock
+82638,Hiland
+82639,Kaycee
+82640,Linch
+82642,Lysite
+82643,Midwest
+82644,Mills
+82646,Natrona
+82648,Powder River
+82649,Shoshoni
+82701,Newcastle
+82710,Aladdin
+82711,Alva
+82712,Beulah
+82713,Carlile
+82714,Devils Tower
+82715,Four Corners
+82716,Gillette
+82717,Gillette
+82718,Gillette
+82720,Hulett
+82721,Moorcroft
+82723,Osage
+82725,Recluse
+82727,Rozet
+82729,Sundance
+82730,Upton
+82731,Weston
+82732,Wright
+82801,Sheridan
+82831,Arvada
+82832,Banner
+82833,Big Horn
+82834,Buffalo
+82835,Clearmont
+82836,Dayton
+82837,Leiter
+82838,Parkman
+82839,Ranchester
+82840,Saddlestring
+82842,Story
+82844,Wolf
+82845,Wyarno
+82901,Rock Springs
+82902,Rock Springs
+82922,Bondurant
+82923,Boulder
+82925,Cora
+82929,Little America
+82930,Evanston
+82931,Evanston
+82932,Farson
+82933,Fort Bridger
+82934,Granger
+82935,Green River
+82936,Lonetree
+82937,Lyman
+82938,Mc Kinnon
+82939,Mountain View
+82941,Pinedale
+82942,Point of Rocks
+82943,Reliance
+82944,Robertson
+82945,Superior
+83001,Jackson
+83002,Jackson
+83011,Kelly
+83012,Moose
+83013,Moran
+83014,Wilson
+83025,Teton Village
+83101,Kemmerer
+83110,Afton
+83111,Auburn
+83112,Bedford
+83113,Big Piney
+83114,Cokeville
+83115,Daniel
+83116,Diamondville
+83118,Etna
+83119,Fairview
+83120,Freedom
+83121,Frontier
+83122,Grover
+83123,La Barge
+83124,Opal
+83126,Smoot
+83127,Thayne
+83128,Alpine
+24701,Bluefield
+24712,Athens
+24714,Beeson
+24715,Bramwell
+24716,Bud
+24719,Covel
+24724,Freeman
+24726,Herndon
+24729,Hiawatha
+24731,Kegley
+24732,Kellysville
+24733,Lashmeet
+24736,Matoaka
+24737,Montcalm
+24738,Nemours
+24739,Oakvale
+24740,Princeton
+24747,Rock
+24751,Wolfe
+24801,Welch
+24808,Anawalt
+24811,Avondale
+24813,Bartley
+24815,Berwind
+24816,Big Sandy
+24817,Bradshaw
+24818,Brenton
+24820,Capels
+24821,Caretta
+24822,Clear Fork
+24823,Coal Mountain
+24824,Coalwood
+24825,Crumpler
+24826,Cucumber
+24827,Cyclone
+24828,Davy
+24829,Eckman
+24830,Elbert
+24831,Elkhorn
+24832,English
+24834,Fanrock
+24836,Gary
+24839,Hanover
+24841,Havaco
+24842,Hemphill
+24843,Hensley
+24844,Iaeger
+24845,Ikes Fork
+24846,Isaban
+24847,Itmann
+24848,Jenkinjones
+24849,Jesse
+24850,Jolo
+24851,Justice
+24852,Keystone
+24853,Kimball
+24854,Kopperston
+24855,Kyle
+24856,Leckie
+24857,Lynco
+24859,Marianna
+24860,Matheny
+24861,Maybeury
+24862,Mohawk
+24866,Newhall
+24867,New Richmond
+24868,Northfork
+24869,North Spring
+24870,Oceana
+24871,Pageton
+24872,Panther
+24873,Paynesville
+24874,Pineville
+24877,Powhatan
+24878,Premier
+24879,Raysal
+24880,Rock View
+24881,Roderfield
+24882,Simon
+24883,Skygusty
+24884,Squire
+24887,Switchback
+24888,Thorpe
+24889,Twin Branch
+24892,War
+24894,Warriormine
+24895,Wilcoe
+24896,Wolf Pen
+24897,Worth
+24898,Wyoming
+24899,Yukon
+24901,Lewisburg
+24902,Fairlea
+24910,Alderson
+24915,Arbovale
+24916,Asbury
+24917,Auto
+24918,Ballard
+24919,Ballengee
+24920,Bartow
+24924,Buckeye
+24925,Caldwell
+24927,Cass
+24931,Crawley
+24934,Dunmore
+24935,Forest Hill
+24936,Fort Spring
+24938,Frankford
+24941,Gap Mills
+24942,Glace
+24943,Grassy Meadows
+24944,Green Bank
+24945,Greenville
+24946,Hillsboro
+24950,Kieffer
+24951,Lindside
+24954,Marlinton
+24957,Maxwelton
+24958,Meadow Bluff
+24961,Neola
+24962,Pence Springs
+24963,Peterstown
+24966,Renick
+24970,Ronceverte
+24974,Secondcreek
+24976,Sinks Grove
+24977,Smoot
+24981,Talcott
+24983,Union
+24984,Waiteville
+24985,Wayside
+24986,White Sulphur Springs
+24991,Williamsburg
+24993,Wolfcreek
+25002,Alloy
+25003,Alum Creek
+25004,Ameagle
+25005,Amma
+25007,Arnett
+25008,Artie
+25009,Ashford
+25010,Bald Knob
+25011,Bancroft
+25015,Belle
+25018,Bentree
+25019,Bickmore
+25021,Bim
+25022,Blair
+25024,Bloomingrose
+25025,Blount
+25026,Blue Creek
+25028,Bob White
+25030,Bomont
+25031,Boomer
+25033,Buffalo
+25035,Cabin Creek
+25036,Cannelton
+25039,Cedar Grove
+25040,Charlton Heights
+25043,Clay
+25044,Clear Creek
+25045,Clendenin
+25046,Clio
+25047,Clothier
+25048,Colcord
+25049,Comfort
+25051,Costa
+25053,Danville
+25054,Dawes
+25057,Deep Water
+25059,Dixie
+25060,Dorothy
+25061,Drybranch
+25062,Dry Creek
+25063,Duck
+25064,Dunbar
+25067,East Bank
+25070,Eleanor
+25071,Elkview
+25075,Eskdale
+25076,Ethel
+25079,Falling Rock
+25081,Foster
+25082,Fraziers Bottom
+25083,Gallagher
+25085,Gauley Bridge
+25086,Glasgow
+25088,Glen
+25090,Glen Ferris
+25093,Gordon
+25095,Grimms Landing
+25102,Handley
+25103,Hansford
+25106,Henderson
+25107,Hernshaw
+25108,Hewett
+25109,Hometown
+25110,Hugheston
+25111,Indore
+25112,Institute
+25113,Ivydale
+25114,Jeffrey
+25115,Kanawha Falls
+25118,Kimberly
+25119,Kincaid
+25121,Lake
+25122,Leewood
+25123,Leon
+25124,Liberty
+25125,Lizemores
+25126,London
+25130,Madison
+25132,Mammoth
+25133,Maysel
+25134,Miami
+25136,Montgomery
+25139,Mount Carbon
+25140,Naoma
+25141,Nebo
+25142,Nellis
+25143,Nitro
+25147,Ohley
+25148,Orgas
+25149,Ottawa
+25150,Ovapa
+25152,Page
+25154,Peytona
+25156,Pinch
+25159,Poca
+25160,Pond Gap
+25161,Powellton
+25162,Pratt
+25164,Procious
+25165,Racine
+25168,Red House
+25169,Ridgeview
+25173,Robson
+25174,Rock Creek
+25177,Saint Albans
+25180,Saxon
+25181,Seth
+25182,Sharon
+25183,Sharples
+25185,Mount Olive
+25186,Smithers
+25187,Southside
+25193,Sylvester
+25201,Tad
+25202,Tornado
+25203,Turtle Creek
+25204,Twilight
+25205,Uneeda
+25206,Van
+25208,Wharton
+25209,Whitesville
+25211,Widen
+25213,Winfield
+25214,Winifrede
+25231,Advent
+25234,Arnoldsburg
+25235,Chloe
+25239,Cottageville
+25241,Evans
+25243,Gandeeville
+25244,Gay
+25245,Given
+25247,Hartford
+25248,Kenna
+25250,Lakin
+25251,Left Hand
+25252,Le Roy
+25253,Letart
+25256,Linden
+25258,Lockney
+25259,Looneyville
+25260,Mason
+25261,Millstone
+25262,Millwood
+25264,Mount Alto
+25265,New Haven
+25266,Newton
+25267,Normantown
+25268,Orma
+25270,Reedy
+25271,Ripley
+25275,Sandyville
+25276,Spencer
+25279,Statts Mills
+25281,Tariff
+25283,Valley Fork
+25285,Wallback
+25286,Walton
+25287,West Columbia
+25301,Charleston
+25302,Charleston
+25303,Charleston
+25304,Charleston
+25305,Charleston
+25306,Charleston
+25309,Charleston
+25311,Charleston
+25312,Charleston
+25313,Charleston
+25314,Charleston
+25315,Charleston
+25317,Charleston
+25320,Charleston
+25321,Charleston
+25322,Charleston
+25323,Charleston
+25324,Charleston
+25325,Charleston
+25326,Charleston
+25327,Charleston
+25328,Charleston
+25329,Charleston
+25330,Charleston
+25331,Charleston
+25332,Charleston
+25333,Charleston
+25334,Charleston
+25335,Charleston
+25336,Charleston
+25337,Charleston
+25338,Charleston
+25339,Charleston
+25350,Charleston
+25356,Charleston
+25357,Charleston
+25358,Charleston
+25360,Charleston
+25361,Charleston
+25362,Charleston
+25364,Charleston
+25365,Charleston
+25375,Charleston
+25387,Charleston
+25389,Charleston
+25392,Charleston
+25396,Charleston
+25401,Martinsburg
+25402,Martinsburg
+25410,Bakerton
+25411,Berkeley Springs
+25413,Bunker Hill
+25414,Charles Town
+25419,Falling Waters
+25420,Gerrardstown
+25421,Glengary
+25422,Great Cacapon
+25423,Halltown
+25425,Harpers Ferry
+25427,Hedgesville
+25428,Inwood
+25429,Kearneysville
+25430,Kearneysville
+25431,Levels
+25432,Millville
+25434,Paw Paw
+25437,Points
+25438,Ranson
+25440,Ridgeway
+25441,Rippon
+25442,Shenandoah Junction
+25443,Shepherdstown
+25444,Slanesville
+25446,Summit Point
+25501,Alkol
+25502,Apple Grove
+25503,Ashton
+25504,Barboursville
+25505,Big Creek
+25506,Branchland
+25507,Ceredo
+25508,Chapmanville
+25510,Culloden
+25511,Dunlow
+25512,East Lynn
+25514,Fort Gay
+25515,Gallipolis Ferry
+25517,Genoa
+25519,Glenhayes
+25520,Glenwood
+25521,Griffithsville
+25523,Hamlin
+25524,Harts
+25526,Hurricane
+25529,Julian
+25530,Kenova
+25534,Kiahsville
+25535,Lavalette
+25537,Lesage
+25540,Midkiff
+25541,Milton
+25544,Myra
+25545,Ona
+25547,Pecks Mill
+25550,Point Pleasant
+25555,Prichard
+25557,Ranger
+25559,Salt Rock
+25560,Scott Depot
+25562,Shoals
+25564,Sod
+25565,Spurlockville
+25567,Sumerco
+25569,Teays
+25570,Wayne
+25571,West Hamlin
+25572,Woodville
+25573,Yawkey
+25601,Logan
+25606,Accoville
+25607,Amherstdale
+25608,Baisden
+25611,Bruno
+25612,Chauncey
+25614,Cora
+25617,Davin
+25621,Gilbert
+25623,Hampden
+25624,Henlawson
+25625,Holden
+25628,Kistler
+25630,Lorado
+25632,Lyburn
+25634,Mallory
+25635,Man
+25636,Monaville
+25637,Mount Gay
+25638,Omar
+25639,Peach Creek
+25644,Sarah Ann
+25645,Stirrat
+25646,Stollings
+25647,Switzer
+25649,Verdunville
+25650,Verner
+25651,Wharncliffe
+25652,Whitman
+25653,Wilkinson
+25654,Yolyn
+25661,Williamson
+25665,Borderland
+25666,Breeden
+25667,Chattaroy
+25669,Crum
+25670,Delbarton
+25671,Dingess
+25672,Edgarton
+25674,Kermit
+25676,Lenore
+25678,Matewan
+25682,Meador
+25685,Naugatuck
+25686,Newtown
+25687,Nolan
+25688,North Matewan
+25690,Ragland
+25691,Rawl
+25692,Red Jacket
+25694,Thacker
+25696,Varney
+25697,Vulcan
+25699,Wilsondale
+25701,Huntington
+25702,Huntington
+25703,Huntington
+25704,Huntington
+25705,Huntington
+25706,Huntington
+25707,Huntington
+25708,Huntington
+25709,Huntington
+25710,Huntington
+25711,Huntington
+25712,Huntington
+25713,Huntington
+25714,Huntington
+25715,Huntington
+25716,Huntington
+25717,Huntington
+25718,Huntington
+25719,Huntington
+25720,Huntington
+25721,Huntington
+25722,Huntington
+25723,Huntington
+25724,Huntington
+25725,Huntington
+25726,Huntington
+25727,Huntington
+25728,Huntington
+25729,Huntington
+25755,Huntington
+25770,Huntington
+25771,Huntington
+25772,Huntington
+25773,Huntington
+25774,Huntington
+25775,Huntington
+25776,Huntington
+25777,Huntington
+25778,Huntington
+25779,Huntington
+25801,Beckley
+25802,Beckley
+25810,Allen Junction
+25811,Amigo
+25812,Ansted
+25813,Beaver
+25816,Blue Jay
+25817,Bolt
+25818,Bradley
+25820,Camp Creek
+25823,Coal City
+25825,Cool Ridge
+25826,Corinne
+25827,Crab Orchard
+25831,Danese
+25832,Daniels
+25833,Dothan
+25836,Eccles
+25837,Edmond
+25839,Fairdale
+25840,Fayetteville
+25841,Flat Top
+25843,Ghent
+25844,Glen Daniel
+25845,Glen Fork
+25846,Glen Jean
+25847,Glen Morgan
+25848,Glen Rogers
+25849,Glen White
+25851,Harper
+25853,Helen
+25854,Hico
+25855,Hilltop
+25856,Jonben
+25857,Josephine
+25859,Kilsyth
+25860,Lanark
+25862,Lansing
+25864,Layland
+25865,Lester
+25866,Lochgelly
+25868,Lookout
+25870,Maben
+25871,Mabscott
+25873,Mac Arthur
+25875,Mc Graws
+25876,Saulsville
+25878,Midway
+25879,Minden
+25880,Mount Hope
+25882,Mullens
+25901,Oak Hill
+25902,Odd
+25904,Pax
+25906,Piney View
+25907,Prince
+25908,Princewick
+25909,Prosperity
+25911,Raleigh
+25912,Ramsey
+25913,Ravencliff
+25914,Redstar
+25915,Rhodell
+25916,Sabine
+25917,Scarbro
+25918,Shady Spring
+25919,Skelton
+25920,Slab Fork
+25921,Sophia
+25922,Spanishburg
+25926,Sprague
+25927,Stanaford
+25928,Stephenson
+25931,Summerlee
+25932,Surveyor
+25934,Terry
+25936,Thurmond
+25938,Victor
+25942,Winona
+25943,Wyco
+25951,Hinton
+25958,Charmco
+25961,Crichton
+25962,Rainelle
+25965,Elton
+25966,Green Sulphur Springs
+25967,Hines
+25969,Jumping Branch
+25971,Lerona
+25972,Leslie
+25976,Meadow Bridge
+25977,Meadow Creek
+25978,Nimitz
+25979,Pipestem
+25981,Quinwood
+25984,Rupert
+25985,Sandstone
+25986,Spring Dale
+25988,TRUE
+25989,White Oak
+26003,Wheeling
+26030,Beech Bottom
+26031,Benwood
+26032,Bethany
+26033,Cameron
+26034,Chester
+26035,Colliers
+26036,Dallas
+26037,Follansbee
+26038,Glen Dale
+26039,Glen Easton
+26040,Mc Mechen
+26041,Moundsville
+26047,New Cumberland
+26050,Newell
+26055,Proctor
+26056,New Manchester
+26058,Short Creek
+26059,Triadelphia
+26060,Valley Grove
+26062,Weirton
+26070,Wellsburg
+26074,West Liberty
+26075,Windsor Heights
+26101,Parkersburg
+26102,Parkersburg
+26103,Parkersburg
+26104,Parkersburg
+26105,Vienna
+26106,Parkersburg
+26120,Mineral Wells
+26121,Mineral Wells
+26133,Belleville
+26134,Belmont
+26135,Bens Run
+26136,Big Bend
+26137,Big Springs
+26138,Brohard
+26141,Creston
+26142,Davisville
+26143,Elizabeth
+26146,Friendly
+26147,Grantsville
+26148,Macfarlan
+26149,Middlebourne
+26150,Mineral Wells
+26151,Mount Zion
+26152,Munday
+26155,New Martinsville
+26159,Paden City
+26160,Palestine
+26161,Petroleum
+26162,Porters Falls
+26164,Ravenswood
+26167,Reader
+26169,Rockport
+26170,Saint Marys
+26173,Sherman
+26175,Sistersville
+26178,Smithville
+26180,Walker
+26181,Washington
+26184,Waverly
+26186,Wileyville
+26187,Williamstown
+26201,Buckhannon
+26202,Fenwick
+26203,Erbacon
+26205,Craigsville
+26206,Cowen
+26208,Camden on Gauley
+26209,Snowshoe
+26210,Adrian
+26215,Cleveland
+26217,Diana
+26218,French Creek
+26219,Frenchton
+26222,Hacker Valley
+26224,Helvetia
+26228,Kanawha Head
+26229,Lorentz
+26230,Pickens
+26234,Rock Cave
+26236,Selbyville
+26237,Tallmansville
+26238,Volga
+26241,Elkins
+26250,Belington
+26253,Beverly
+26254,Bowden
+26257,Coalton
+26259,Dailey
+26260,Davis
+26261,Richwood
+26263,Dryfork
+26264,Durbin
+26266,Upperglade
+26267,Ellamore
+26268,Glady
+26269,Hambleton
+26270,Harman
+26271,Hendricks
+26273,Huttonsville
+26275,Junior
+26276,Kerens
+26278,Mabie
+26280,Mill Creek
+26282,Monterville
+26283,Montrose
+26285,Norton
+26287,Parsons
+26288,Webster Springs
+26289,Red Creek
+26291,Slatyfork
+26292,Thomas
+26293,Valley Bend
+26294,Valley Head
+26296,Whitmer
+26298,Bergoo
+26301,Clarksburg
+26302,Clarksburg
+26306,Clarksburg
+26320,Alma
+26321,Alum Bridge
+26323,Anmoore
+26325,Auburn
+26327,Berea
+26328,Blandville
+26330,Bridgeport
+26332,Bristol
+26334,Brownton
+26335,Burnsville
+26337,Cairo
+26338,Camden
+26339,Center Point
+26342,Coxs Mills
+26343,Crawford
+26346,Ellenboro
+26347,Flemington
+26348,Folsom
+26349,Galloway
+26350,Gilmer
+26351,Glenville
+26354,Grafton
+26361,Gypsy
+26362,Harrisville
+26366,Haywood
+26369,Hepzibah
+26372,Horner
+26374,Independence
+26375,Industrial
+26376,Ireland
+26377,Jacksonburg
+26378,Jane Lew
+26384,Linn
+26385,Lost Creek
+26386,Lumberport
+26404,Meadowbrook
+26405,Moatsville
+26407,Mountain
+26408,Mount Clare
+26410,Newburg
+26411,New Milton
+26412,Orlando
+26415,Pennsboro
+26416,Philippi
+26419,Pine Grove
+26421,Pullman
+26422,Reynoldsville
+26424,Rosemont
+26425,Rowlesburg
+26426,Salem
+26430,Sand Fork
+26431,Shinnston
+26434,Shirley
+26435,Simpson
+26436,Smithburg
+26437,Smithfield
+26438,Spelter
+26440,Thornton
+26443,Troy
+26444,Tunnelton
+26447,Walkersville
+26448,Wallace
+26451,West Milford
+26452,Weston
+26456,West Union
+26461,Wilsonburg
+26463,Wyatt
+26501,Morgantown
+26502,Morgantown
+26503,Morgantown
+26504,Morgantown
+26505,Morgantown
+26506,Morgantown
+26507,Morgantown
+26508,Morgantown
+26519,Albright
+26520,Arthurdale
+26521,Blacksville
+26522,Booth
+26524,Bretz
+26525,Bruceton Mills
+26527,Cassville
+26529,Core
+26531,Dellslow
+26533,Everettville
+26534,Granville
+26535,Hazelton
+26537,Kingwood
+26541,Maidsville
+26542,Masontown
+26543,Osage
+26544,Pentress
+26546,Pursglove
+26547,Reedsville
+26554,Fairmont
+26555,Fairmont
+26559,Barrackville
+26560,Baxter
+26561,Big Run
+26562,Burton
+26563,Carolina
+26566,Colfax
+26568,Enterprise
+26570,Fairview
+26571,Farmington
+26572,Four States
+26574,Grant Town
+26575,Hundred
+26576,Idamay
+26578,Kingmont
+26581,Littleton
+26582,Mannington
+26585,Metz
+26586,Montana Mines
+26587,Rachel
+26588,Rivesville
+26589,Wadestown
+26590,Wana
+26591,Worthington
+26601,Sutton
+26610,Birch River
+26611,Cedarville
+26612,Centralia
+26615,Copen
+26617,Dille
+26618,Elmira
+26619,Exchange
+26621,Flatwoods
+26623,Frametown
+26624,Gassaway
+26627,Heaters
+26629,Little Birch
+26631,Napier
+26634,Perkins
+26636,Rosedale
+26638,Shock
+26639,Strange Creek
+26641,Wilsie
+26651,Summersville
+26656,Belva
+26660,Calvin
+26662,Canvas
+26667,Drennen
+26671,Gilboa
+26674,Jodie
+26675,Keslers Cross Lanes
+26676,Leivasy
+26678,Mount Lookout
+26679,Mount Nebo
+26680,Nallen
+26681,Nettie
+26684,Pool
+26690,Swiss
+26691,Tioga
+26704,Augusta
+26705,Aurora
+26707,Bayard
+26710,Burlington
+26711,Capon Bridge
+26714,Delray
+26716,Eglon
+26717,Elk Garden
+26719,Fort Ashby
+26720,Gormania
+26722,Green Spring
+26726,Keyser
+26731,Lahmansville
+26734,Medley
+26739,Mount Storm
+26743,New Creek
+26750,Piedmont
+26753,Ridgeley
+26755,Rio
+26757,Romney
+26761,Shanks
+26763,Springfield
+26764,Terra Alta
+26767,Wiley Ford
+26801,Baker
+26802,Brandywine
+26804,Circleville
+26807,Franklin
+26808,High View
+26810,Lost City
+26812,Mathias
+26814,Riverton
+26815,Sugar Grove
+26817,Bloomery
+26818,Fisher
+26823,Capon Springs
+26824,Junction
+26833,Maysville
+26836,Moorefield
+26838,Milam
+26845,Old Fields
+26847,Petersburg
+26851,Wardensville
+26852,Purgitsville
+26855,Cabins
+26865,Yellow Spring
+26866,Upper Tract
+26884,Seneca Rocks
+26886,Onego
+53001,Adell
+53002,Allenton
+53003,Ashippun
+53004,Belgium
+53005,Brookfield
+53006,Brownsville
+53007,Butler
+53008,Brookfield
+53009,Byron
+53010,Campbellsport
+53011,Cascade
+53012,Cedarburg
+53013,Cedar Grove
+53014,Chilton
+53015,Cleveland
+53016,Clyman
+53017,Colgate
+53018,Delafield
+53019,Eden
+53020,Elkhart Lake
+53021,Fredonia
+53022,Germantown
+53023,Glenbeulah
+53024,Grafton
+53026,Greenbush
+53027,Hartford
+53029,Hartland
+53031,Hingham
+53032,Horicon
+53033,Hubertus
+53034,Hustisford
+53035,Iron Ridge
+53036,Ixonia
+53037,Jackson
+53038,Johnson Creek
+53039,Juneau
+53040,Kewaskum
+53042,Kiel
+53044,Kohler
+53045,Brookfield
+53046,Lannon
+53047,Lebanon
+53048,Lomira
+53049,Malone
+53050,Mayville
+53051,Menomonee Falls
+53052,Menomonee Falls
+53056,Merton
+53057,Mount Calvary
+53058,Nashotah
+53059,Neosho
+53060,Newburg
+53061,New Holstein
+53062,New Holstein
+53063,Newton
+53064,North Lake
+53065,Oakfield
+53066,Oconomowoc
+53069,Okauchee
+53070,Oostburg
+53072,Pewaukee
+53073,Plymouth
+53074,Port Washington
+53075,Random Lake
+53076,Richfield
+53078,Rubicon
+53079,Saint Cloud
+53080,Saukville
+53081,Sheboygan
+53082,Sheboygan
+53083,Sheboygan
+53085,Sheboygan Falls
+53086,Slinger
+53088,Stockbridge
+53089,Sussex
+53090,West Bend
+53091,Theresa
+53092,Thiensville
+53093,Waldo
+53094,Watertown
+53095,West Bend
+53097,Mequon
+53098,Watertown
+53099,Woodland
+53101,Bassett
+53102,Benet Lake
+53103,Big Bend
+53104,Bristol
+53105,Burlington
+53108,Caledonia
+53109,Camp Lake
+53110,Cudahy
+53114,Darien
+53115,Delavan
+53118,Dousman
+53119,Eagle
+53120,East Troy
+53121,Elkhorn
+53122,Elm Grove
+53125,Fontana
+53126,Franksville
+53127,Genesee Depot
+53128,Genoa City
+53129,Greendale
+53130,Hales Corners
+53132,Franklin
+53137,Helenville
+53138,Honey Creek
+53139,Kansasville
+53140,Kenosha
+53141,Kenosha
+53142,Kenosha
+53143,Kenosha
+53144,Kenosha
+53146,New Berlin
+53147,Lake Geneva
+53148,Lyons
+53149,Mukwonago
+53150,Muskego
+53151,New Berlin
+53152,New Munster
+53153,North Prairie
+53154,Oak Creek
+53156,Palmyra
+53157,Pell Lake
+53158,Pleasant Prairie
+53159,Powers Lake
+53167,Rochester
+53168,Salem
+53170,Silver Lake
+53171,Somers
+53172,South Milwaukee
+53176,Springfield
+53177,Sturtevant
+53178,Sullivan
+53179,Trevor
+53181,Twin Lakes
+53182,Union Grove
+53183,Wales
+53184,Walworth
+53185,Waterford
+53186,Waukesha
+53187,Waukesha
+53188,Waukesha
+53189,Waukesha
+53190,Whitewater
+53191,Williams Bay
+53192,Wilmot
+53194,Woodworth
+53195,Zenda
+53201,Milwaukee
+53202,Milwaukee
+53203,Milwaukee
+53204,Milwaukee
+53205,Milwaukee
+53206,Milwaukee
+53207,Milwaukee
+53208,Milwaukee
+53209,Milwaukee
+53210,Milwaukee
+53211,Milwaukee
+53212,Milwaukee
+53213,Milwaukee
+53214,Milwaukee
+53215,Milwaukee
+53216,Milwaukee
+53217,Milwaukee
+53218,Milwaukee
+53219,Milwaukee
+53220,Milwaukee
+53221,Milwaukee
+53222,Milwaukee
+53223,Milwaukee
+53224,Milwaukee
+53225,Milwaukee
+53226,Milwaukee
+53227,Milwaukee
+53228,Milwaukee
+53233,Milwaukee
+53234,Milwaukee
+53235,Saint Francis
+53237,Milwaukee
+53259,Milwaukee
+53263,Milwaukee
+53267,Milwaukee
+53268,Milwaukee
+53270,Milwaukee
+53274,Milwaukee
+53277,Milwaukee
+53278,Milwaukee
+53280,Milwaukee
+53281,Milwaukee
+53284,Milwaukee
+53285,Milwaukee
+53288,Milwaukee
+53290,Milwaukee
+53293,Milwaukee
+53295,Milwaukee
+53401,Racine
+53402,Racine
+53403,Racine
+53404,Racine
+53405,Racine
+53406,Racine
+53407,Racine
+53408,Racine
+53490,Racine
+53501,Afton
+53502,Albany
+53503,Arena
+53504,Argyle
+53505,Avalon
+53506,Avoca
+53507,Barneveld
+53508,Belleville
+53510,Belmont
+53511,Beloit
+53512,Beloit
+53515,Black Earth
+53516,Blanchardville
+53517,Blue Mounds
+53518,Blue River
+53520,Brodhead
+53521,Brooklyn
+53522,Browntown
+53523,Cambridge
+53525,Clinton
+53526,Cobb
+53527,Cottage Grove
+53528,Cross Plains
+53529,Dane
+53530,Darlington
+53531,Deerfield
+53532,De Forest
+53533,Dodgeville
+53534,Edgerton
+53535,Edmund
+53536,Evansville
+53537,Footville
+53538,Fort Atkinson
+53540,Gotham
+53541,Gratiot
+53542,Hanover
+53543,Highland
+53544,Hollandale
+53545,Janesville
+53546,Janesville
+53547,Janesville
+53549,Jefferson
+53550,Juda
+53551,Lake Mills
+53553,Linden
+53554,Livingston
+53555,Lodi
+53556,Lone Rock
+53557,Lowell
+53558,Mc Farland
+53559,Marshall
+53560,Mazomanie
+53561,Merrimac
+53562,Middleton
+53563,Milton
+53565,Mineral Point
+53566,Monroe
+53569,Montfort
+53570,Monticello
+53571,Morrisonville
+53572,Mount Horeb
+53573,Muscoda
+53574,New Glarus
+53575,Oregon
+53576,Orfordville
+53577,Plain
+53578,Prairie du Sac
+53579,Reeseville
+53580,Rewey
+53581,Richland Center
+53582,Ridgeway
+53583,Sauk City
+53584,Sextonville
+53585,Sharon
+53586,Shullsburg
+53587,South Wayne
+53588,Spring Green
+53589,Stoughton
+53590,Sun Prairie
+53591,Sun Prairie
+53593,Verona
+53594,Waterloo
+53595,Dodgeville
+53596,Sun Prairie
+53597,Waunakee
+53598,Windsor
+53599,Woodford
+53701,Madison
+53702,Madison
+53703,Madison
+53704,Madison
+53705,Madison
+53706,Madison
+53707,Madison
+53708,Madison
+53709,Madison
+53710,Madison
+53711,Madison
+53713,Madison
+53714,Madison
+53715,Madison
+53716,Madison
+53717,Madison
+53718,Madison
+53719,Madison
+53725,Madison
+53726,Madison
+53744,Madison
+53777,Madison
+53778,Madison
+53779,Madison
+53780,Madison
+53782,Madison
+53783,Madison
+53784,Madison
+53785,Madison
+53786,Madison
+53787,Madison
+53788,Madison
+53789,Madison
+53790,Madison
+53791,Madison
+53792,Madison
+53793,Madison
+53794,Madison
+53801,Bagley
+53802,Beetown
+53803,Benton
+53804,Bloomington
+53805,Boscobel
+53806,Cassville
+53807,Cuba City
+53808,Dickeyville
+53809,Fennimore
+53810,Glen Haven
+53811,Hazel Green
+53812,Kieler
+53813,Lancaster
+53816,Mount Hope
+53817,Patch Grove
+53818,Platteville
+53820,Potosi
+53821,Prairie du Chien
+53824,Sinsinawa
+53825,Stitzer
+53826,Wauzeka
+53827,Woodman
+53901,Portage
+53910,Adams
+53911,Arlington
+53913,Baraboo
+53916,Beaver Dam
+53917,Beaver Dam
+53919,Brandon
+53920,Briggsville
+53922,Burnett
+53923,Cambria
+53924,Cazenovia
+53925,Columbus
+53926,Dalton
+53927,Dellwood
+53928,Doylestown
+53929,Elroy
+53930,Endeavor
+53931,Fairwater
+53932,Fall River
+53933,Fox Lake
+53934,Friendship
+53935,Friesland
+53936,Grand Marsh
+53937,Hillpoint
+53939,Kingston
+53940,Lake Delton
+53941,La Valle
+53942,Lime Ridge
+53943,Loganville
+53944,Lyndon Station
+53946,Markesan
+53947,Marquette
+53948,Mauston
+53949,Montello
+53950,New Lisbon
+53951,North Freedom
+53952,Oxford
+53953,Packwaukee
+53954,Pardeeville
+53955,Poynette
+53956,Randolph
+53957,Randolph
+53958,Reedsburg
+53959,Reedsburg
+53960,Rio
+53961,Rock Springs
+53962,Union Center
+53963,Waupun
+53964,Westfield
+53965,Wisconsin Dells
+53968,Wonewoc
+53969,Wyocena
+54001,Amery
+54002,Baldwin
+54003,Beldenville
+54004,Clayton
+54005,Clear Lake
+54006,Cushing
+54007,Deer Park
+54009,Dresser
+54010,East Ellsworth
+54011,Ellsworth
+54012,Emerald
+54013,Glenwood City
+54014,Hager City
+54015,Hammond
+54016,Hudson
+54017,New Richmond
+54020,Osceola
+54021,Prescott
+54022,River Falls
+54023,Roberts
+54024,Saint Croix Falls
+54025,Somerset
+54026,Star Prairie
+54027,Wilson
+54028,Woodville
+54082,Houlton
+54101,Abrams
+54102,Amberg
+54103,Armstrong Creek
+54104,Athelstane
+54106,Black Creek
+54107,Bonduel
+54110,Brillion
+54111,Cecil
+54112,Coleman
+54113,Combined Locks
+54114,Crivitz
+54115,De Pere
+54119,Dunbar
+54120,Fence
+54121,Florence
+54123,Forest Junction
+54124,Gillett
+54125,Goodman
+54126,Greenleaf
+54127,Green Valley
+54128,Gresham
+54129,Hilbert
+54130,Kaukauna
+54131,Freedom
+54135,Keshena
+54136,Kimberly
+54137,Krakow
+54138,Lakewood
+54139,Lena
+54140,Little Chute
+54141,Little Suamico
+54143,Marinette
+54149,Mountain
+54150,Neopit
+54151,Niagara
+54152,Nichols
+54153,Oconto
+54154,Oconto Falls
+54155,Oneida
+54156,Pembine
+54157,Peshtigo
+54159,Porterfield
+54160,Potter
+54161,Pound
+54162,Pulaski
+54165,Seymour
+54166,Shawano
+54169,Sherwood
+54170,Shiocton
+54171,Sobieski
+54173,Suamico
+54174,Suring
+54175,Townsend
+54177,Wausaukee
+54180,Wrightstown
+54182,Zachow
+54201,Algoma
+54202,Baileys Harbor
+54203,Branch
+54204,Brussels
+54205,Casco
+54207,Collins
+54208,Denmark
+54209,Egg Harbor
+54210,Ellison Bay
+54211,Ephraim
+54212,Fish Creek
+54213,Forestville
+54214,Francis Creek
+54215,Kellnersville
+54216,Kewaunee
+54217,Luxemburg
+54220,Manitowoc
+54221,Manitowoc
+54226,Maplewood
+54227,Maribel
+54228,Mishicot
+54229,New Franken
+54230,Reedsville
+54232,Saint Nazianz
+54234,Sister Bay
+54235,Sturgeon Bay
+54240,Tisch Mills
+54241,Two Rivers
+54245,Valders
+54246,Washington Island
+54247,Whitelaw
+54301,Green Bay
+54302,Green Bay
+54303,Green Bay
+54304,Green Bay
+54305,Green Bay
+54306,Green Bay
+54307,Green Bay
+54308,Green Bay
+54311,Green Bay
+54313,Green Bay
+54324,Green Bay
+54344,Green Bay
+54401,Wausau
+54402,Wausau
+54403,Wausau
+54404,Marshfield
+54405,Abbotsford
+54406,Amherst
+54407,Amherst Junction
+54408,Aniwa
+54409,Antigo
+54410,Arpin
+54411,Athens
+54412,Auburndale
+54413,Babcock
+54414,Birnamwood
+54415,Blenker
+54416,Bowler
+54417,Brokaw
+54418,Bryant
+54420,Chili
+54421,Colby
+54422,Curtiss
+54423,Custer
+54424,Deerbrook
+54425,Dorchester
+54426,Edgar
+54427,Eland
+54428,Elcho
+54429,Elderon
+54430,Elton
+54432,Galloway
+54433,Gilman
+54434,Jump River
+54435,Gleason
+54436,Granton
+54437,Greenwood
+54439,Hannibal
+54440,Hatley
+54441,Hewitt
+54442,Irma
+54443,Junction City
+54444,Kempster
+54446,Loyal
+54447,Lublin
+54448,Marathon
+54449,Marshfield
+54450,Mattoon
+54451,Medford
+54452,Merrill
+54454,Milladore
+54455,Mosinee
+54456,Neillsville
+54457,Nekoosa
+54458,Nelsonville
+54459,Ogema
+54460,Owen
+54462,Pearson
+54463,Pelican Lake
+54464,Phlox
+54465,Pickerel
+54466,Pittsville
+54467,Plover
+54469,Port Edwards
+54470,Rib Lake
+54471,Ringle
+54472,Marshfield
+54473,Rosholt
+54474,Rothschild
+54475,Rudolph
+54476,Schofield
+54479,Spencer
+54480,Stetsonville
+54481,Stevens Point
+54484,Stratford
+54485,Summit Lake
+54486,Tigerton
+54487,Tomahawk
+54488,Unity
+54489,Vesper
+54490,Westboro
+54491,White Lake
+54492,Stevens Point
+54493,Willard
+54494,Wisconsin Rapids
+54495,Wisconsin Rapids
+54498,Withee
+54499,Wittenberg
+54501,Rhinelander
+54511,Argonne
+54512,Boulder Junction
+54513,Brantwood
+54514,Butternut
+54515,Catawba
+54517,Clam Lake
+54519,Conover
+54520,Crandon
+54521,Eagle River
+54524,Fifield
+54525,Gile
+54526,Glen Flora
+54527,Glidden
+54529,Harshaw
+54530,Hawkins
+54531,Hazelhurst
+54532,Heafford Junction
+54534,Hurley
+54536,Iron Belt
+54537,Kennan
+54538,Lac du Flambeau
+54539,Lake Tomahawk
+54540,Land O Lakes
+54541,Laona
+54542,Long Lake
+54543,Mc Naughton
+54545,Manitowish Waters
+54546,Mellen
+54547,Mercer
+54548,Minocqua
+54550,Montreal
+54552,Park Falls
+54554,Phelps
+54555,Phillips
+54556,Prentice
+54557,Presque Isle
+54558,Saint Germain
+54559,Saxon
+54560,Sayner
+54561,Star Lake
+54562,Three Lakes
+54563,Tony
+54564,Tripoli
+54565,Upson
+54566,Wabeno
+54568,Woodruff
+54601,La Crosse
+54602,La Crosse
+54603,La Crosse
+54610,Alma
+54611,Alma Center
+54612,Arcadia
+54613,Arkdale
+54614,Bangor
+54615,Black River Falls
+54616,Blair
+54618,Camp Douglas
+54619,Cashton
+54620,Cataract
+54621,Chaseburg
+54622,Cochrane
+54623,Coon Valley
+54624,De Soto
+54625,Dodge
+54626,Eastman
+54627,Ettrick
+54628,Ferryville
+54629,Fountain City
+54630,Galesville
+54631,Gays Mills
+54632,Genoa
+54634,Hillsboro
+54635,Hixton
+54636,Holmen
+54637,Hustler
+54638,Kendall
+54639,La Farge
+54640,Lynxville
+54641,Mather
+54642,Melrose
+54643,Millston
+54644,Mindoro
+54645,Mount Sterling
+54646,Necedah
+54648,Norwalk
+54649,Oakdale
+54650,Onalaska
+54651,Ontario
+54652,Readstown
+54653,Rockland
+54654,Seneca
+54655,Soldiers Grove
+54656,Sparta
+54657,Steuben
+54658,Stoddard
+54659,Taylor
+54660,Tomah
+54661,Trempealeau
+54662,Tunnel City
+54664,Viola
+54665,Viroqua
+54666,Warrens
+54667,Westby
+54669,West Salem
+54670,Wilton
+54701,Eau Claire
+54702,Eau Claire
+54703,Eau Claire
+54720,Altoona
+54721,Arkansaw
+54722,Augusta
+54723,Bay City
+54724,Bloomer
+54725,Boyceville
+54726,Boyd
+54727,Cadott
+54728,Chetek
+54729,Chippewa Falls
+54730,Colfax
+54731,Conrath
+54732,Cornell
+54733,Dallas
+54734,Downing
+54735,Downsville
+54736,Durand
+54737,Eau Galle
+54738,Eleva
+54739,Elk Mound
+54740,Elmwood
+54741,Fairchild
+54742,Fall Creek
+54743,Gilmanton
+54744,Hillsdale
+54745,Holcombe
+54746,Humbird
+54747,Independence
+54748,Jim Falls
+54749,Knapp
+54750,Maiden Rock
+54751,Menomonie
+54754,Merrillan
+54755,Mondovi
+54756,Nelson
+54757,New Auburn
+54758,Osseo
+54759,Pepin
+54760,Pigeon Falls
+54761,Plum City
+54762,Prairie Farm
+54763,Ridgeland
+54764,Rock Falls
+54765,Sand Creek
+54766,Sheldon
+54767,Spring Valley
+54768,Stanley
+54769,Stockholm
+54770,Strum
+54771,Thorp
+54772,Wheeler
+54773,Whitehall
+54774,Chippewa Falls
+54801,Spooner
+54805,Almena
+54806,Ashland
+54810,Balsam Lake
+54812,Barron
+54813,Barronett
+54814,Bayfield
+54816,Benoit
+54817,Birchwood
+54818,Brill
+54819,Bruce
+54820,Brule
+54821,Cable
+54822,Cameron
+54824,Centuria
+54826,Comstock
+54827,Cornucopia
+54828,Couderay
+54829,Cumberland
+54830,Danbury
+54832,Drummond
+54834,Edgewater
+54835,Exeland
+54836,Foxboro
+54837,Frederic
+54838,Gordon
+54839,Grand View
+54840,Grantsburg
+54841,Haugen
+54842,Hawthorne
+54843,Hayward
+54844,Herbster
+54845,Hertel
+54846,High Bridge
+54847,Iron River
+54848,Ladysmith
+54849,Lake Nebagamon
+54850,La Pointe
+54851,Lewis
+54853,Luck
+54854,Maple
+54855,Marengo
+54856,Mason
+54857,Mikana
+54858,Milltown
+54859,Minong
+54861,Odanah
+54862,Ojibwa
+54864,Poplar
+54865,Port Wing
+54867,Radisson
+54868,Rice Lake
+54870,Sarona
+54871,Shell Lake
+54872,Siren
+54873,Solon Springs
+54874,South Range
+54875,Springbrook
+54876,Stone Lake
+54880,Superior
+54888,Trego
+54889,Turtle Lake
+54890,Wascott
+54891,Washburn
+54893,Webster
+54895,Weyerhaeuser
+54896,Winter
+54901,Oshkosh
+54902,Oshkosh
+54903,Oshkosh
+54904,Oshkosh
+54906,Oshkosh
+54909,Almond
+54911,Appleton
+54912,Appleton
+54913,Appleton
+54914,Appleton
+54915,Appleton
+54919,Appleton
+54921,Bancroft
+54922,Bear Creek
+54923,Berlin
+54926,Big Falls
+54927,Butte des Morts
+54928,Caroline
+54929,Clintonville
+54930,Coloma
+54931,Dale
+54932,Eldorado
+54933,Embarrass
+54934,Eureka
+54935,Fond du Lac
+54936,Fond du Lac
+54937,Fond du Lac
+54940,Fremont
+54941,Green Lake
+54942,Greenville
+54943,Hancock
+54944,Hortonville
+54945,Iola
+54946,King
+54947,Larsen
+54948,Leopolis
+54949,Manawa
+54950,Marion
+54951,Medina
+54952,Menasha
+54956,Neenah
+54957,Neenah
+54960,Neshkoro
+54961,New London
+54962,Ogdensburg
+54963,Omro
+54964,Pickett
+54965,Pine River
+54966,Plainfield
+54967,Poy Sippi
+54968,Princeton
+54969,Readfield
+54970,Redgranite
+54971,Ripon
+54974,Rosendale
+54975,Royalton
+54976,Saxeville
+54977,Scandinavia
+54978,Tilleda
+54979,Van Dyne
+54980,Waukau
+54981,Waupaca
+54982,Wautoma
+54983,Weyauwega
+54984,Wild Rose
+54985,Winnebago
+54986,Winneconne
+54990,Iola
+98001,Auburn
+98002,Auburn
+98003,Federal Way
+98004,Bellevue
+98005,Bellevue
+98006,Bellevue
+98007,Bellevue
+98008,Bellevue
+98009,Bellevue
+98010,Black Diamond
+98011,Bothell
+98012,Bothell
+98013,Burton
+98014,Carnation
+98015,Bellevue
+98019,Duvall
+98020,Edmonds
+98021,Bothell
+98022,Enumclaw
+98023,Federal Way
+98024,Fall City
+98025,Hobart
+98026,Edmonds
+98027,Issaquah
+98028,Kenmore
+98029,Issaquah
+98031,Kent
+98032,Kent
+98033,Kirkland
+98034,Kirkland
+98035,Kent
+98036,Lynnwood
+98037,Lynnwood
+98038,Maple Valley
+98039,Medina
+98040,Mercer Island
+98041,Bothell
+98042,Kent
+98043,Mountlake Terrace
+98045,North Bend
+98046,Lynnwood
+98047,Pacific
+98050,Preston
+98051,Ravensdale
+98052,Redmond
+98053,Redmond
+98054,Redondo
+98055,Renton
+98056,Renton
+98057,Renton
+98058,Renton
+98059,Renton
+98060,Seattle
+98061,Rollingbay
+98062,Seahurst
+98063,Federal Way
+98064,Kent
+98065,Snoqualmie
+98068,Snoqualmie Pass
+98070,Vashon
+98071,Auburn
+98072,Woodinville
+98073,Redmond
+98082,Bothell
+98083,Kirkland
+98092,Auburn
+98093,Federal Way
+98101,Seattle
+98102,Seattle
+98103,Seattle
+98104,Seattle
+98105,Seattle
+98106,Seattle
+98107,Seattle
+98108,Seattle
+98109,Seattle
+98110,Bainbridge Island
+98111,Seattle
+98112,Seattle
+98114,Seattle
+98115,Seattle
+98116,Seattle
+98117,Seattle
+98118,Seattle
+98119,Seattle
+98121,Seattle
+98122,Seattle
+98124,Seattle
+98125,Seattle
+98126,Seattle
+98129,Seattle
+98130,Seattle
+98131,Seattle
+98132,Seattle
+98133,Seattle
+98134,Seattle
+98136,Seattle
+98138,Seattle
+98140,Seattle
+98144,Seattle
+98145,Seattle
+98146,Seattle
+98148,Seattle
+98150,Seattle
+98151,Seattle
+98154,Seattle
+98155,Seattle
+98158,Seattle
+98160,Seattle
+98161,Seattle
+98164,Seattle
+98166,Seattle
+98168,Seattle
+98170,Seattle
+98171,Seattle
+98174,Seattle
+98177,Seattle
+98178,Seattle
+98181,Seattle
+98184,Seattle
+98185,Seattle
+98188,Seattle
+98190,Seattle
+98191,Seattle
+98195,Seattle
+98198,Seattle
+98199,Seattle
+98201,Everett
+98203,Everett
+98204,Everett
+98205,Everett
+98206,Everett
+98207,Everett
+98208,Everett
+98220,Acme
+98221,Anacortes
+98222,Blakely Island
+98223,Arlington
+98224,Baring
+98225,Bellingham
+98226,Bellingham
+98227,Bellingham
+98228,Bellingham
+98230,Blaine
+98231,Blaine
+98232,Bow
+98233,Burlington
+98235,Clearlake
+98236,Clinton
+98237,Concrete
+98238,Conway
+98239,Coupeville
+98240,Custer
+98241,Darrington
+98243,Deer Harbor
+98244,Deming
+98245,Eastsound
+98246,Bow
+98247,Everson
+98248,Ferndale
+98249,Freeland
+98250,Friday Harbor
+98251,Gold Bar
+98252,Granite Falls
+98253,Greenbank
+98255,Hamilton
+98256,Index
+98257,La Conner
+98258,Lake Stevens
+98259,North Lakewood
+98260,Langley
+98261,Lopez Island
+98262,Lummi Island
+98263,Lyman
+98264,Lynden
+98266,Maple Falls
+98267,Marblemount
+98270,Marysville
+98271,Marysville
+98272,Monroe
+98273,Mount Vernon
+98274,Mount Vernon
+98275,Mukilteo
+98276,Nooksack
+98277,Oak Harbor
+98278,Oak Harbor
+98279,Olga
+98280,Orcas
+98281,Point Roberts
+98283,Rockport
+98284,Sedro Woolley
+98286,Shaw Island
+98287,Silvana
+98288,Skykomish
+98290,Snohomish
+98291,Snohomish
+98292,Stanwood
+98293,Startup
+98294,Sultan
+98295,Sumas
+98296,Snohomish
+98297,Waldron
+98303,Anderson Island
+98304,Ashford
+98305,Beaver
+98310,Bremerton
+98311,Bremerton
+98312,Bremerton
+98314,Bremerton
+98315,Silverdale
+98320,Brinnon
+98321,Buckley
+98322,Burley
+98323,Carbonado
+98324,Carlsborg
+98325,Chimacum
+98326,Clallam Bay
+98327,Dupont
+98328,Eatonville
+98329,Gig Harbor
+98330,Elbe
+98331,Forks
+98332,Gig Harbor
+98333,Fox Island
+98335,Gig Harbor
+98336,Glenoma
+98337,Bremerton
+98338,Graham
+98339,Port Hadlock
+98340,Hansville
+98342,Indianola
+98343,Joyce
+98344,Kapowsin
+98345,Keyport
+98346,Kingston
+98348,La Grande
+98349,Lakebay
+98350,La Push
+98351,Longbranch
+98352,Sumner
+98353,Manchester
+98354,Milton
+98355,Mineral
+98356,Morton
+98357,Neah Bay
+98358,Nordland
+98359,Olalla
+98360,Orting
+98361,Packwood
+98362,Port Angeles
+98363,Port Angeles
+98364,Port Gamble
+98365,Port Ludlow
+98366,Port Orchard
+98367,Port Orchard
+98368,Port Townsend
+98370,Poulsbo
+98371,Puyallup
+98372,Puyallup
+98373,Puyallup
+98374,Puyallup
+98375,Puyallup
+98376,Quilcene
+98377,Randle
+98378,Retsil
+98380,Seabeck
+98381,Sekiu
+98382,Sequim
+98383,Silverdale
+98384,South Colby
+98385,South Prairie
+98386,Southworth
+98387,Spanaway
+98388,Steilacoom
+98390,Sumner
+98392,Suquamish
+98393,Tracyton
+98394,Vaughn
+98395,Wauna
+98396,Wilkeson
+98397,Longmire
+98398,Paradise Inn
+98401,Tacoma
+98402,Tacoma
+98403,Tacoma
+98404,Tacoma
+98405,Tacoma
+98406,Tacoma
+98407,Tacoma
+98408,Tacoma
+98409,Tacoma
+98411,Tacoma
+98412,Tacoma
+98413,Tacoma
+98415,Tacoma
+98416,Tacoma
+98418,Tacoma
+98421,Tacoma
+98422,Tacoma
+98424,Tacoma
+98430,Camp Murray
+98431,Tacoma
+98433,Tacoma
+98434,Tacoma
+98438,Tacoma
+98439,Lakewood
+98442,Tacoma
+98443,Tacoma
+98444,Tacoma
+98445,Tacoma
+98446,Tacoma
+98447,Tacoma
+98450,Tacoma
+98455,Tacoma
+98460,Tacoma
+98464,Tacoma
+98465,Tacoma
+98466,Tacoma
+98467,University Place
+98471,Tacoma
+98477,Tacoma
+98481,Tacoma
+98492,Lakewood
+98493,Tacoma
+98494,Tacoma
+98497,Lakewood
+98498,Lakewood
+98499,Lakewood
+98501,Olympia
+98502,Olympia
+98503,Lacey
+98504,Olympia
+98505,Olympia
+98506,Olympia
+98507,Olympia
+98508,Olympia
+98509,Lacey
+98511,Olympia
+98512,Olympia
+98513,Olympia
+98516,Olympia
+98520,Aberdeen
+98522,Adna
+98524,Allyn
+98526,Amanda Park
+98527,Bay Center
+98528,Belfair
+98530,Bucoda
+98531,Centralia
+98532,Chehalis
+98533,Cinebar
+98535,Copalis Beach
+98536,Copalis Crossing
+98537,Cosmopolis
+98538,Curtis
+98539,Doty
+98540,East Olympia
+98541,Elma
+98542,Ethel
+98544,Galvin
+98546,Grapeview
+98547,Grayland
+98548,Hoodsport
+98550,Hoquiam
+98552,Humptulips
+98554,Lebam
+98555,Lilliwaup
+98556,Littlerock
+98557,McCleary
+98558,McKenna
+98559,Malone
+98560,Matlock
+98561,Menlo
+98562,Moclips
+98563,Montesano
+98564,Mossyrock
+98565,Napavine
+98566,Neilton
+98568,Oakville
+98569,Ocean Shores
+98570,Onalaska
+98571,Pacific Beach
+98572,Pe Ell
+98575,Quinault
+98576,Rainier
+98577,Raymond
+98579,Rochester
+98580,Roy
+98581,Ryderwood
+98582,Salkum
+98583,Satsop
+98584,Shelton
+98585,Silver Creek
+98586,South Bend
+98587,Taholah
+98588,Tahuya
+98589,Tenino
+98590,Tokeland
+98591,Toledo
+98592,Union
+98593,Vader
+98595,Westport
+98596,Winlock
+98597,Yelm
+98599,Olympia
+98601,Amboy
+98602,Appleton
+98603,Ariel
+98604,Battle Ground
+98605,Bingen
+98606,Brush Prairie
+98607,Camas
+98609,Carrolls
+98610,Carson
+98611,Castle Rock
+98612,Cathlamet
+98613,Centerville
+98614,Chinook
+98616,Cougar
+98617,Dallesport
+98619,Glenwood
+98620,Goldendale
+98621,Grays River
+98622,Heisson
+98623,Husum
+98624,Ilwaco
+98625,Kalama
+98626,Kelso
+98628,Klickitat
+98629,La Center
+98631,Long Beach
+98632,Longview
+98635,Lyle
+98637,Nahcotta
+98638,Naselle
+98639,North Bonneville
+98640,Ocean Park
+98641,Oysterville
+98642,Ridgefield
+98643,Rosburg
+98644,Seaview
+98645,Silverlake
+98647,Skamokawa
+98648,Stevenson
+98649,Toutle
+98650,Trout Lake
+98651,Underwood
+98660,Vancouver
+98661,Vancouver
+98662,Vancouver
+98663,Vancouver
+98664,Vancouver
+98665,Vancouver
+98666,Vancouver
+98667,Vancouver
+98668,Vancouver
+98670,Wahkiacus
+98671,Washougal
+98672,White Salmon
+98673,Wishram
+98674,Woodland
+98675,Yacolt
+98682,Vancouver
+98683,Vancouver
+98684,Vancouver
+98685,Vancouver
+98686,Vancouver
+98687,Vancouver
+98801,Wenatchee
+98802,East Wenatchee
+98807,Wenatchee
+98811,Ardenvoir
+98812,Brewster
+98813,Bridgeport
+98814,Carlton
+98815,Cashmere
+98816,Chelan
+98817,Chelan Falls
+98819,Conconully
+98821,Dryden
+98822,Entiat
+98823,Ephrata
+98824,George
+98826,Leavenworth
+98827,Loomis
+98828,Malaga
+98829,Malott
+98830,Mansfield
+98831,Manson
+98832,Marlin
+98833,Mazama
+98834,Methow
+98836,Monitor
+98837,Moses Lake
+98840,Okanogan
+98841,Omak
+98843,Orondo
+98844,Oroville
+98845,Palisades
+98846,Pateros
+98847,Peshastin
+98848,Quincy
+98849,Riverside
+98850,Rock Island
+98851,Soap Lake
+98852,Stehekin
+98853,Stratford
+98855,Tonasket
+98856,Twisp
+98857,Warden
+98858,Waterville
+98859,Wauconda
+98860,Wilson Creek
+98862,Winthrop
+98901,Yakima
+98902,Yakima
+98903,Yakima
+98904,Yakima
+98907,Yakima
+98908,Yakima
+98909,Yakima
+98920,Brownstown
+98921,Buena
+98922,Cle Elum
+98923,Cowiche
+98925,Easton
+98926,Ellensburg
+98929,Goose Prairie
+98930,Grandview
+98932,Granger
+98933,Harrah
+98934,Kittitas
+98935,Mabton
+98936,Moxee
+98937,Naches
+98938,Outlook
+98939,Parker
+98940,Ronald
+98941,Roslyn
+98942,Selah
+98943,South Cle Elum
+98944,Sunnyside
+98946,Thorp
+98947,Tieton
+98948,Toppenish
+98950,Vantage
+98951,Wapato
+98952,White Swan
+98953,Zillah
+99001,Airway Heights
+99003,Chattaroy
+99004,Cheney
+99005,Colbert
+99006,Deer Park
+99008,Edwall
+99009,Elk
+99011,Fairchild Air Force Base
+99012,Fairfield
+99013,Ford
+99014,Four Lakes
+99015,Freeman
+99016,Greenacres
+99017,Lamont
+99018,Latah
+99019,Liberty Lake
+99020,Marshall
+99021,Mead
+99022,Medical Lake
+99023,Mica
+99025,Newman Lake
+99026,Nine Mile Falls
+99027,Otis Orchards
+99029,Reardan
+99030,Rockford
+99031,Spangle
+99032,Sprague
+99033,Tekoa
+99034,Tumtum
+99036,Valleyford
+99037,Veradale
+99039,Waverly
+99040,Wellpinit
+99101,Addy
+99102,Albion
+99103,Almira
+99104,Belmont
+99105,Benge
+99107,Boyds
+99109,Chewelah
+99110,Clayton
+99111,Colfax
+99113,Colton
+99114,Colville
+99115,Coulee City
+99116,Coulee Dam
+99117,Creston
+99118,Curlew
+99119,Cusick
+99121,Danville
+99122,Davenport
+99123,Electric City
+99124,Elmer City
+99125,Endicott
+99126,Evans
+99127,Saint John
+99128,Farmington
+99129,Fruitland
+99130,Garfield
+99131,Gifford
+99133,Grand Coulee
+99134,Harrington
+99135,Hartline
+99136,Hay
+99137,Hunters
+99138,Inchelium
+99139,Ione
+99140,Keller
+99141,Kettle Falls
+99143,Lacrosse
+99144,Lamona
+99146,Laurier
+99147,Lincoln
+99148,Loon Lake
+99149,Malden
+99150,Malo
+99151,Marcus
+99152,Metaline
+99153,Metaline Falls
+99154,Mohler
+99155,Nespelem
+99156,Newport
+99157,Northport
+99158,Oakesdale
+99159,Odessa
+99160,Orient
+99161,Palouse
+99163,Pullman
+99164,Pullman
+99165,Pullman
+99166,Republic
+99167,Rice
+99169,Ritzville
+99170,Rosalia
+99171,Saint John
+99173,Springdale
+99174,Steptoe
+99176,Thornton
+99179,Uniontown
+99180,Usk
+99181,Valley
+99185,Wilbur
+99201,Spokane
+99202,Spokane
+99203,Spokane
+99204,Spokane
+99205,Spokane
+99206,Spokane
+99207,Spokane
+99208,Spokane
+99209,Spokane
+99210,Spokane
+99211,Spokane
+99212,Spokane
+99213,Spokane
+99214,Spokane
+99215,Spokane
+99216,Spokane
+99217,Spokane
+99218,Spokane
+99219,Spokane
+99220,Spokane
+99223,Spokane
+99224,Spokane
+99228,Spokane
+99251,Spokane
+99252,Spokane
+99256,Spokane
+99258,Spokane
+99260,Spokane
+99299,Spokane
+99301,Pasco
+99302,Pasco
+99320,Benton City
+99321,Beverly
+99322,Bickleton
+99323,Burbank
+99324,College Place
+99326,Connell
+99327,Cunningham
+99328,Dayton
+99329,Dixie
+99330,Eltopia
+99332,Hatton
+99333,Hooper
+99335,Kahlotus
+99336,Kennewick
+99337,Kennewick
+99338,Kennewick
+99341,Lind
+99343,Mesa
+99344,Othello
+99345,Paterson
+99346,Plymouth
+99347,Pomeroy
+99348,Prescott
+99349,Mattawa
+99350,Prosser
+99352,Richland
+99353,West Richland
+99356,Roosevelt
+99357,Royal City
+99359,Starbuck
+99360,Touchet
+99361,Waitsburg
+99362,Walla Walla
+99363,Wallula
+99371,Washtucna
+99401,Anatone
+99402,Asotin
+99403,Clarkston
+05001,White River Junction
+05009,White River Junction
+05030,Ascutney
+05031,Barnard
+05032,Bethel
+05033,Bradford
+05034,Bridgewater
+05035,Bridgewater Corners
+05036,Brookfield
+05037,Brownsville
+05038,Chelsea
+05039,Corinth
+05040,East Corinth
+05041,East Randolph
+05042,East Ryegate
+05043,East Thetford
+05045,Fairlee
+05046,Groton
+05047,Hartford
+05048,Hartland
+05049,Hartland Four Corners
+05050,Mc Indoe Falls
+05051,Newbury
+05052,North Hartland
+05053,North Pomfret
+05054,North Thetford
+05055,Norwich
+05056,Plymouth
+05058,Post Mills
+05059,Quechee
+05060,Randolph
+05061,Randolph Center
+05062,Reading
+05065,Sharon
+05067,South Pomfret
+05068,South Royalton
+05069,South Ryegate
+05070,South Strafford
+05071,South Woodstock
+05072,Strafford
+05073,Taftsville
+05074,Thetford
+05075,Thetford Center
+05076,East Corinth
+05077,Tunbridge
+05079,Vershire
+05081,Wells River
+05083,West Fairlee
+05084,West Hartford
+05085,West Newbury
+05086,West Topsham
+05088,Wilder
+05089,Windsor
+05091,Woodstock
+05101,Bellows Falls
+05141,Cambridgeport
+05142,Cavendish
+05143,Chester
+05144,Chester Depot
+05146,Grafton
+05148,Londonderry
+05149,Ludlow
+05150,North Springfield
+05151,Perkinsville
+05152,Peru
+05153,Proctorsville
+05154,Saxtons River
+05155,South Londonderry
+05156,Springfield
+05158,Westminster
+05159,Westminster Station
+05161,Weston
+05201,Bennington
+05250,Arlington
+05251,Dorset
+05252,East Arlington
+05253,East Dorset
+05254,Manchester
+05255,Manchester Center
+05257,North Bennington
+05260,North Pownal
+05261,Pownal
+05262,Shaftsbury
+05301,Brattleboro
+05302,Brattleboro
+05303,Brattleboro
+05304,Brattleboro
+05340,Bondville
+05341,East Dover
+05342,Jacksonville
+05343,Jamaica
+05344,Marlboro
+05345,Newfane
+05346,Putney
+05350,Readsboro
+05351,South Newfane
+05352,Readsboro
+05353,Townshend
+05354,Vernon
+05355,Wardsboro
+05356,West Dover
+05357,West Dummerston
+05358,West Halifax
+05359,West Townshend
+05360,West Wardsboro
+05361,Whitingham
+05362,Williamsville
+05363,Wilmington
+05401,Burlington
+05402,Burlington
+05403,South Burlington
+05404,Winooski
+05405,Burlington
+05406,Burlington
+05407,South Burlington
+05439,Colchester
+05440,Alburg
+05441,Bakersfield
+05442,Belvidere Center
+05443,Bristol
+05444,Cambridge
+05445,Charlotte
+05446,Colchester
+05447,East Berkshire
+05448,East Fairfield
+05449,Colchester
+05450,Enosburg Falls
+05451,Essex
+05452,Essex Junction
+05453,Essex Junction
+05454,Fairfax
+05455,Fairfield
+05456,Ferrisburg
+05457,Franklin
+05458,Grand Isle
+05459,Highgate Center
+05460,Highgate Springs
+05461,Hinesburg
+05462,Huntington
+05463,Isle La Motte
+05464,Jeffersonville
+05465,Jericho
+05466,Jonesville
+05468,Milton
+05469,Monkton
+05470,Montgomery
+05471,Montgomery Center
+05472,New Haven
+05473,North Ferrisburg
+05474,North Hero
+05476,Richford
+05477,Richmond
+05478,Saint Albans
+05479,Saint Albans
+05481,Saint Albans Bay
+05482,Shelburne
+05483,Sheldon
+05485,Sheldon Springs
+05486,South Hero
+05487,Starksboro
+05488,Swanton
+05489,Underhill
+05490,Underhill Center
+05491,Vergennes
+05492,Waterville
+05494,Westford
+05495,Williston
+05601,Montpelier
+05602,Montpelier
+05603,Montpelier
+05604,Montpelier
+05609,Montpelier
+05620,Montpelier
+05633,Montpelier
+05640,Adamant
+05641,Barre
+05647,Cabot
+05648,Calais
+05649,East Barre
+05650,East Calais
+05651,East Montpelier
+05652,Eden
+05653,Eden Mills
+05654,Graniteville
+05655,Hyde Park
+05656,Johnson
+05657,Lake Elmore
+05658,Marshfield
+05660,Moretown
+05661,Morrisville
+05662,Moscow
+05663,Northfield
+05664,Northfield Falls
+05665,North Hyde Park
+05666,North Montpelier
+05667,Plainfield
+05669,Roxbury
+05670,South Barre
+05671,Waterbury
+05672,Stowe
+05673,Waitsfield
+05674,Warren
+05675,Washington
+05676,Waterbury
+05677,Waterbury Center
+05678,Websterville
+05679,Williamstown
+05680,Wolcott
+05681,Woodbury
+05682,Worcester
+05701,Rutland
+05702,Rutland
+05730,Belmont
+05731,Benson
+05732,Bomoseen
+05733,Brandon
+05734,Bridport
+05735,Castleton
+05736,Center Rutland
+05737,Chittenden
+05738,Cuttingsville
+05739,Danby
+05740,East Middlebury
+05741,East Poultney
+05742,East Wallingford
+05743,Fair Haven
+05744,Florence
+05745,Forest Dale
+05746,Gaysville
+05747,Granville
+05748,Hancock
+05750,Hydeville
+05751,Killington
+05753,Middlebury
+05757,Middletown Springs
+05758,Mount Holly
+05759,North Clarendon
+05760,Orwell
+05761,Pawlet
+05762,Pittsfield
+05763,Pittsford
+05764,Poultney
+05765,Proctor
+05766,Ripton
+05767,Rochester
+05768,Rupert
+05769,Salisbury
+05770,Shoreham
+05772,Stockbridge
+05773,Wallingford
+05774,Wells
+05775,West Pawlet
+05776,West Rupert
+05777,West Rutland
+05778,Whiting
+05819,Saint Johnsbury
+05820,Albany
+05821,Barnet
+05822,Barton
+05823,Beebe Plain
+05824,Concord
+05825,Coventry
+05826,Craftsbury
+05827,Craftsbury Common
+05828,Danville
+05829,Derby
+05830,Derby Line
+05832,East Burke
+05833,East Charleston
+05836,East Hardwick
+05837,East Haven
+05838,East Saint Johnsbury
+05839,Glover
+05840,Granby
+05841,Greensboro
+05842,Greensboro Bend
+05843,Hardwick
+05845,Irasburg
+05846,Island Pond
+05847,Lowell
+05848,Lower Waterford
+05849,Lyndon
+05850,Lyndon Center
+05851,Lyndonville
+05853,Morgan
+05855,Newport
+05857,Newport Center
+05858,North Concord
+05859,North Troy
+05860,Orleans
+05861,Passumpsic
+05862,Peacham
+05863,Saint Johnsbury Center
+05866,Sheffield
+05867,Sutton
+05868,Troy
+05871,West Burke
+05872,West Charleston
+05873,West Danville
+05874,Westfield
+05875,West Glover
+05901,Averill
+05902,Beecher Falls
+05903,Canaan
+05904,Gilman
+05905,Guildhall
+05906,Lunenburg
+05907,Norton
+00801,St Thomas
+00802,St Thomas
+00803,St Thomas
+00804,St Thomas
+00805,St Thomas
+00820,Christiansted
+00821,Christiansted
+00822,Christiansted
+00823,Christiansted
+00824,Christiansted
+00830,St John
+00831,St John
+00840,Frederiksted
+00841,Frederiksted
+00850,Kingshill
+00851,Kingshill
+20101,Dulles
+20102,Dulles
+20103,Dulles
+20104,Dulles
+20105,Aldie
+20106,Amissville
+20107,Arcola
+20108,Manassas
+20109,Manassas
+20110,Manassas
+20111,Manassas
+20112,Manassas
+20113,Manassas
+20115,Marshall
+20116,Marshall
+20117,Middleburg
+20118,Middleburg
+20119,Catlett
+20120,Centreville
+20121,Centreville
+20122,Centreville
+20124,Clifton
+20128,Orlean
+20129,Paeonian Springs
+20130,Paris
+20131,Philomont
+20132,Purcellville
+20134,Purcellville
+20135,Bluemont
+20136,Bristow
+20137,Broad Run
+20138,Calverton
+20139,Casanova
+20140,Rectortown
+20141,Round Hill
+20142,Round Hill
+20143,Catharpin
+20144,Delaplane
+20146,Ashburn
+20147,Ashburn
+20148,Ashburn
+20149,Ashburn
+20151,Chantilly
+20152,Chantilly
+20153,Chantilly
+20155,Gainesville
+20156,Gainesville
+20158,Hamilton
+20159,Hamilton
+20160,Lincoln
+20163,Sterling
+20164,Sterling
+20165,Sterling
+20166,Sterling
+20167,Sterling
+20168,Haymarket
+20169,Haymarket
+20170,Herndon
+20171,Herndon
+20172,Herndon
+20175,Leesburg
+20176,Leesburg
+20177,Leesburg
+20178,Leesburg
+20180,Lovettsville
+20181,Nokesville
+20182,Nokesville
+20184,Upperville
+20185,Upperville
+20186,Warrenton
+20187,Warrenton
+20188,Warrenton
+20190,Reston
+20191,Reston
+20192,Herndon
+20193,Reston
+20194,Reston
+20195,Reston
+20196,Reston
+20197,Waterford
+20198,The Plains
+20199,Dulles
+22002,Amissville
+22003,Annandale
+22009,Burke
+22015,Burke
+22026,Dumfries
+22027,Dunn Loring
+22030,Fairfax
+22031,Fairfax
+22032,Fairfax
+22033,Fairfax
+22034,Fairfax
+22035,Fairfax
+22036,Fairfax
+22037,Fairfax
+22038,Fairfax
+22039,Fairfax Station
+22040,Falls Church
+22041,Falls Church
+22042,Falls Church
+22043,Falls Church
+22044,Falls Church
+22046,Falls Church
+22047,Falls Church
+22060,Fort Belvoir
+22066,Great Falls
+22067,Greenway
+22079,Lorton
+22081,Merrifield
+22082,Merrifield
+22092,Herndon
+22093,Ashburn
+22095,Herndon
+22096,Reston
+22101,Mc Lean
+22102,Mc Lean
+22103,West McLean
+22106,Mc Lean
+22109,Mc Lean
+22116,Merrifield
+22118,Merrifield
+22119,Merrifield
+22120,Merrifield
+22121,Mount Vernon
+22122,Newington
+22124,Oakton
+22125,Occoquan
+22134,Quantico
+22135,Quantico
+22150,Springfield
+22151,Springfield
+22152,Springfield
+22153,Springfield
+22156,Springfield
+22158,Springfield
+22159,Springfield
+22160,Springfield
+22161,Springfield
+22172,Triangle
+22180,Vienna
+22181,Vienna
+22182,Vienna
+22183,Vienna
+22184,Vienna
+22185,Vienna
+22191,Woodbridge
+22192,Woodbridge
+22193,Woodbridge
+22194,Woodbridge
+22195,Woodbridge
+22199,Lorton
+22201,Arlington
+22202,Arlington
+22203,Arlington
+22204,Arlington
+22205,Arlington
+22206,Arlington
+22207,Arlington
+22209,Arlington
+22210,Arlington
+22211,Ft Myer
+22212,Arlington
+22213,Arlington
+22214,Arlington
+22215,Arlington
+22216,Arlington
+22217,Arlington
+22218,Arlington
+22219,Arlington
+22222,Arlington
+22223,Arlington
+22225,Arlington
+22226,Arlington
+22227,Arlington
+22229,Arlington
+22230,Arlington
+22234,Arlington
+22240,Arlington
+22241,Arlington
+22242,Arlington
+22243,Arlington
+22244,Arlington
+22245,Arlington
+22246,Arlington
+22301,Alexandria
+22302,Alexandria
+22303,Alexandria
+22304,Alexandria
+22305,Alexandria
+22306,Alexandria
+22307,Alexandria
+22308,Alexandria
+22309,Alexandria
+22310,Alexandria
+22311,Alexandria
+22312,Alexandria
+22313,Alexandria
+22314,Alexandria
+22315,Alexandria
+22320,Alexandria
+22321,Alexandria
+22331,Alexandria
+22332,Alexandria
+22333,Alexandria
+22334,Alexandria
+22336,Alexandria
+22401,Fredericksburg
+22402,Fredericksburg
+22403,Fredericksburg
+22404,Fredericksburg
+22405,Fredericksburg
+22406,Fredericksburg
+22407,Fredericksburg
+22408,Fredericksburg
+22412,Fredericksburg
+22427,Bowling Green
+22428,Bowling Green
+22430,Brooke
+22432,Burgess
+22433,Burr Hill
+22435,Callao
+22436,Caret
+22437,Center Cross
+22438,Champlain
+22442,Coles Point
+22443,Colonial Beach
+22446,Corbin
+22448,Dahlgren
+22451,Dogue
+22454,Dunnsville
+22456,Edwardsville
+22460,Farnham
+22463,Garrisonville
+22469,Hague
+22471,Hartwood
+22472,Haynesville
+22473,Heathsville
+22476,Hustle
+22480,Irvington
+22481,Jersey
+22482,Kilmarnock
+22485,King George
+22488,Kinsale
+22501,Ladysmith
+22503,Lancaster
+22504,Laneview
+22507,Lively
+22508,Locust Grove
+22509,Loretto
+22511,Lottsburg
+22513,Merry Point
+22514,Milford
+22517,Mollusk
+22520,Montross
+22523,Morattico
+22524,Mount Holly
+22526,Ninde
+22528,Nuttsville
+22529,Oldhams
+22530,Ophelia
+22534,Partlow
+22535,Port Royal
+22538,Rappahannock Academy
+22539,Reedville
+22542,Rhoadesville
+22544,Rollins Fork
+22545,Ruby
+22546,Ruther Glen
+22547,Sealston
+22548,Sharps
+22552,Sparta
+22553,Spotsylvania
+22554,Stafford
+22555,Stafford
+22558,Stratford
+22560,Tappahannock
+22565,Thornburg
+22567,Unionville
+22570,Village
+22572,Warsaw
+22576,Weems
+22577,Sandy Point
+22578,White Stone
+22579,Wicomico Church
+22580,Woodford
+22581,Zacata
+22601,Winchester
+22602,Winchester
+22603,Winchester
+22604,Winchester
+22610,Bentonville
+22611,Berryville
+22620,Boyce
+22622,Brucetown
+22623,Chester Gap
+22624,Clear Brook
+22625,Cross Junction
+22626,Fishers Hill
+22627,Flint Hill
+22630,Front Royal
+22637,Gore
+22638,Winchester
+22639,Hume
+22640,Huntly
+22641,Strasburg
+22642,Linden
+22643,Markham
+22644,Maurertown
+22645,Middletown
+22646,Millwood
+22649,Middletown
+22650,Rileyville
+22652,Fort Valley
+22654,Star Tannery
+22655,Stephens City
+22656,Stephenson
+22657,Strasburg
+22660,Toms Brook
+22663,White Post
+22664,Woodstock
+22701,Culpeper
+22709,Aroda
+22711,Banco
+22712,Bealeton
+22713,Boston
+22714,Brandy Station
+22715,Brightwood
+22716,Castleton
+22718,Elkwood
+22719,Etlan
+22720,Goldvein
+22721,Graves Mill
+22722,Haywood
+22723,Hood
+22724,Jeffersonton
+22725,Leon
+22726,Lignum
+22727,Madison
+22728,Midland
+22729,Mitchells
+22730,Oakpark
+22731,Pratts
+22732,Radiant
+22733,Rapidan
+22734,Remington
+22735,Reva
+22736,Richardsville
+22737,Rixeyville
+22738,Rochelle
+22739,Somerville
+22740,Sperryville
+22741,Stevensburg
+22742,Sumerduck
+22743,Syria
+22746,Viewtown
+22747,Washington
+22748,Wolftown
+22749,Woodville
+22801,Harrisonburg
+22802,Harrisonburg
+22807,Harrisonburg
+22810,Basye
+22811,Bergton
+22812,Bridgewater
+22815,Broadway
+22820,Criders
+22821,Dayton
+22824,Edinburg
+22827,Elkton
+22830,Fulks Run
+22831,Hinton
+22832,Keezletown
+22833,Lacey Spring
+22834,Linville
+22835,Luray
+22840,Mc Gaheysville
+22841,Mount Crawford
+22842,Mount Jackson
+22843,Mount Solon
+22844,New Market
+22845,Orkney Springs
+22846,Penn Laird
+22847,Quicksburg
+22848,Pleasant Valley
+22849,Shenandoah
+22850,Singers Glen
+22851,Stanley
+22853,Timberville
+22901,Charlottesville
+22902,Charlottesville
+22903,Charlottesville
+22904,Charlottesville
+22905,Charlottesville
+22906,Charlottesville
+22907,Charlottesville
+22908,Charlottesville
+22909,Charlottesville
+22910,Charlottesville
+22911,Charlottesville
+22920,Afton
+22922,Arrington
+22923,Barboursville
+22924,Batesville
+22931,Covesville
+22932,Crozet
+22935,Dyke
+22936,Earlysville
+22937,Esmont
+22938,Faber
+22939,Fishersville
+22940,Free Union
+22942,Gordonsville
+22943,Greenwood
+22945,Ivy
+22946,Keene
+22947,Keswick
+22948,Locust Dale
+22949,Lovingston
+22952,Lyndhurst
+22953,Madison Mills
+22954,Massies Mill
+22957,Montpelier Station
+22958,Nellysford
+22959,North Garden
+22960,Orange
+22963,Palmyra
+22964,Piney River
+22965,Quinque
+22967,Roseland
+22968,Ruckersville
+22969,Schuyler
+22971,Shipman
+22972,Somerset
+22973,Stanardsville
+22974,Troy
+22976,Tyro
+22980,Waynesboro
+22987,White Hall
+22989,Woodberry Forest
+23001,Achilles
+23002,Amelia Court House
+23003,Ark
+23004,Arvonia
+23005,Ashland
+23009,Aylett
+23011,Barhamsville
+23014,Beaumont
+23015,Beaverdam
+23017,Bellamy
+23018,Bena
+23021,Bohannon
+23022,Bremo Bluff
+23023,Bruington
+23024,Bumpass
+23025,Cardinal
+23027,Cartersville
+23030,Charles City
+23031,Christchurch
+23032,Church View
+23035,Cobbs Creek
+23038,Columbia
+23039,Crozier
+23040,Cumberland
+23043,Deltaville
+23045,Diggs
+23047,Doswell
+23050,Dutton
+23054,Fife
+23055,Fork Union
+23056,Foster
+23058,Glen Allen
+23059,Glen Allen
+23060,Glen Allen
+23061,Gloucester
+23062,Gloucester Point
+23063,Goochland
+23064,Grimstead
+23065,Gum Spring
+23066,Gwynn
+23067,Hadensville
+23068,Hallieford
+23069,Hanover
+23070,Hardyville
+23071,Hartfield
+23072,Hayes
+23075,Highland Springs
+23076,Hudgins
+23079,Jamaica
+23081,Jamestown
+23083,Jetersville
+23084,Kents Store
+23085,King and Queen Court Hous
+23086,King William
+23089,Lanexa
+23090,Lightfoot
+23091,Little Plymouth
+23092,Locust Hill
+23093,Louisa
+23101,Macon
+23102,Maidens
+23103,Manakin Sabot
+23105,Mannboro
+23106,Manquin
+23107,Maryus
+23108,Mascot
+23109,Mathews
+23110,Mattaponi
+23111,Mechanicsville
+23112,Midlothian
+23113,Midlothian
+23115,Millers Tavern
+23116,Mechanicsville
+23117,Mineral
+23119,Moon
+23120,Moseley
+23123,New Canton
+23124,New Kent
+23125,New Point
+23126,Newtown
+23127,Norge
+23128,North
+23129,Oilville
+23130,Onemo
+23131,Ordinary
+23138,Port Haywood
+23139,Powhatan
+23140,Providence Forge
+23141,Quinton
+23146,Rockville
+23147,Ruthville
+23148,Saint Stephens Church
+23149,Saluda
+23150,Sandston
+23153,Sandy Hook
+23154,Schley
+23155,Severn
+23156,Shacklefords
+23160,State Farm
+23161,Stevensville
+23162,Studley
+23163,Susan
+23168,Toano
+23169,Topping
+23170,Trevilians
+23173,University of Richmond
+23175,Urbanna
+23176,Wake
+23177,Walkerton
+23178,Ware Neck
+23180,Water View
+23181,West Point
+23183,White Marsh
+23184,Wicomico
+23185,Williamsburg
+23186,Williamsburg
+23187,Williamsburg
+23188,Williamsburg
+23190,Woods Cross Roads
+23191,Zanoni
+23192,Montpelier
+23218,Richmond
+23219,Richmond
+23220,Richmond
+23221,Richmond
+23222,Richmond
+23223,Richmond
+23224,Richmond
+23225,Richmond
+23226,Richmond
+23227,Richmond
+23228,Richmond
+23229,Richmond
+23230,Richmond
+23231,Richmond
+23232,Richmond
+23233,Richmond
+23234,Richmond
+23235,Richmond
+23236,Richmond
+23237,Richmond
+23238,Richmond
+23240,Richmond
+23241,Richmond
+23242,Richmond
+23249,Richmond
+23250,Richmond
+23255,Richmond
+23260,Richmond
+23261,Richmond
+23266,Richmond
+23269,Richmond
+23270,Richmond
+23272,Richmond
+23273,Richmond
+23274,Richmond
+23275,Richmond
+23276,Richmond
+23278,Richmond
+23279,Richmond
+23280,Richmond
+23282,Richmond
+23284,Richmond
+23285,Richmond
+23286,Richmond
+23288,Richmond
+23289,Richmond
+23290,Richmond
+23291,Richmond
+23292,Richmond
+23293,Richmond
+23294,Richmond
+23297,Richmond
+23298,Richmond
+23301,Accomac
+23302,Assawoman
+23303,Atlantic
+23304,Battery Park
+23306,Belle Haven
+23307,Birdsnest
+23308,Bloxom
+23310,Cape Charles
+23313,Capeville
+23314,Carrollton
+23315,Carrsville
+23316,Cheriton
+23320,Chesapeake
+23321,Chesapeake
+23322,Chesapeake
+23323,Chesapeake
+23324,Chesapeake
+23325,Chesapeake
+23326,Chesapeake
+23327,Chesapeake
+23328,Chesapeake
+23336,Chincoteague Island
+23337,Wallops Island
+23341,Craddockville
+23345,Davis Wharf
+23347,Eastville
+23350,Exmore
+23354,Franktown
+23356,Greenbackville
+23357,Greenbush
+23358,Hacksneck
+23359,Hallwood
+23389,Harborton
+23395,Horntown
+23396,Oak Hall
+23397,Isle of Wight
+23398,Jamesville
+23399,Jenkins Bridge
+23401,Keller
+23404,Locustville
+23405,Machipongo
+23407,Mappsville
+23408,Marionville
+23409,Mears
+23410,Melfa
+23412,Modest Town
+23413,Nassawadox
+23414,Nelsonia
+23415,New Church
+23416,Oak Hall
+23417,Onancock
+23418,Onley
+23419,Oyster
+23420,Painter
+23421,Parksley
+23422,Pungoteague
+23423,Quinby
+23424,Rescue
+23426,Sanford
+23427,Saxis
+23429,Seaview
+23430,Smithfield
+23431,Smithfield
+23432,Suffolk
+23433,Suffolk
+23434,Suffolk
+23435,Suffolk
+23436,Suffolk
+23437,Suffolk
+23438,Suffolk
+23439,Suffolk
+23440,Tangier
+23441,Tasley
+23442,Temperanceville
+23443,Townsend
+23450,Virginia Beach
+23451,Virginia Beach
+23452,Virginia Beach
+23454,Virginia Beach
+23455,Virginia Beach
+23456,Virginia Beach
+23457,Virginia Beach
+23458,Virginia Beach
+23459,Virginia Beach
+23460,Virginia Beach
+23461,Virginia Beach
+23462,Virginia Beach
+23463,Virginia Beach
+23464,Virginia Beach
+23465,Virginia Beach
+23466,Virginia Beach
+23467,Virginia Beach
+23468,Virginia Beach
+23471,Virginia Beach
+23479,Virginia Beach
+23480,Wachapreague
+23482,Wardtown
+23483,Wattsville
+23486,Willis Wharf
+23487,Windsor
+23488,Withams
+23501,Norfolk
+23502,Norfolk
+23503,Norfolk
+23504,Norfolk
+23505,Norfolk
+23506,Norfolk
+23507,Norfolk
+23508,Norfolk
+23509,Norfolk
+23510,Norfolk
+23511,Norfolk
+23512,Norfolk
+23513,Norfolk
+23514,Norfolk
+23515,Norfolk
+23517,Norfolk
+23518,Norfolk
+23519,Norfolk
+23520,Norfolk
+23521,Norfolk
+23523,Norfolk
+23529,Norfolk
+23530,Norfolk
+23541,Norfolk
+23551,Norfolk
+23601,Newport News
+23602,Newport News
+23603,Newport News
+23604,Fort Eustis
+23605,Newport News
+23606,Newport News
+23607,Newport News
+23608,Newport News
+23609,Newport News
+23612,Newport News
+23628,Newport News
+23630,Hampton
+23631,Hampton
+23651,Fort Monroe
+23653,Hampton
+23661,Hampton
+23662,Poquoson
+23663,Hampton
+23664,Hampton
+23665,Hampton
+23666,Hampton
+23667,Hampton
+23668,Hampton
+23669,Hampton
+23670,Hampton
+23681,Hampton
+23690,Yorktown
+23691,Yorktown
+23692,Yorktown
+23693,Yorktown
+23694,Lackey
+23696,Seaford
+23701,Portsmouth
+23702,Portsmouth
+23703,Portsmouth
+23704,Portsmouth
+23705,Portsmouth
+23707,Portsmouth
+23708,Portsmouth
+23709,Portsmouth
+23801,Fort Lee
+23803,Petersburg
+23804,Petersburg
+23805,Petersburg
+23806,Petersburg
+23821,Alberta
+23822,Ammon
+23824,Blackstone
+23827,Boykins
+23828,Branchville
+23829,Capron
+23830,Carson
+23831,Chester
+23832,Chesterfield
+23833,Church Road
+23834,Colonial Heights
+23836,Chester
+23837,Courtland
+23838,Chesterfield
+23839,Dendron
+23840,Dewitt
+23841,Dinwiddie
+23842,Disputanta
+23843,Dolphin
+23844,Drewryville
+23845,Ebony
+23846,Elberon
+23847,Emporia
+23850,Ford
+23851,Franklin
+23856,Freeman
+23857,Gasburg
+23860,Hopewell
+23866,Ivor
+23867,Jarratt
+23868,Lawrenceville
+23870,Jarratt
+23872,Mc Kenney
+23873,Meredithville
+23874,Newsoms
+23875,Prince George
+23876,Rawlings
+23878,Sedley
+23879,Skippers
+23881,Spring Grove
+23882,Stony Creek
+23883,Surry
+23884,Sussex
+23885,Sutherland
+23887,Valentines
+23888,Wakefield
+23889,Warfield
+23890,Waverly
+23891,Waverly
+23893,White Plains
+23894,Wilsons
+23897,Yale
+23898,Zuni
+23899,Claremont
+23901,Farmville
+23909,Farmville
+23915,Baskerville
+23917,Boydton
+23919,Bracey
+23920,Brodnax
+23921,Buckingham
+23922,Burkeville
+23923,Charlotte Court House
+23924,Chase City
+23927,Clarksville
+23930,Crewe
+23934,Cullen
+23936,Dillwyn
+23937,Drakes Branch
+23938,Dundas
+23939,Evergreen
+23941,Fort Mitchell
+23942,Green Bay
+23943,Hampden Sydney
+23944,Kenbridge
+23947,Keysville
+23950,La Crosse
+23952,Lunenburg
+23954,Meherrin
+23955,Nottoway
+23958,Pamplin
+23959,Phenix
+23960,Prospect
+23962,Randolph
+23963,Red House
+23964,Red Oak
+23966,Rice
+23967,Saxe
+23968,Skipwith
+23970,South Hill
+23974,Victoria
+23976,Wylliesburg
+24001,Roanoke
+24002,Roanoke
+24003,Roanoke
+24004,Roanoke
+24005,Roanoke
+24006,Roanoke
+24007,Roanoke
+24008,Roanoke
+24009,Roanoke
+24010,Roanoke
+24011,Roanoke
+24012,Roanoke
+24013,Roanoke
+24014,Roanoke
+24015,Roanoke
+24016,Roanoke
+24017,Roanoke
+24018,Roanoke
+24019,Roanoke
+24020,Roanoke
+24022,Roanoke
+24023,Roanoke
+24024,Roanoke
+24025,Roanoke
+24026,Roanoke
+24027,Roanoke
+24028,Roanoke
+24029,Roanoke
+24030,Roanoke
+24031,Roanoke
+24032,Roanoke
+24033,Roanoke
+24034,Roanoke
+24035,Roanoke
+24036,Roanoke
+24037,Roanoke
+24038,Roanoke
+24040,Roanoke
+24042,Roanoke
+24043,Roanoke
+24044,Roanoke
+24045,Roanoke
+24048,Roanoke
+24050,Roanoke
+24053,Ararat
+24054,Axton
+24055,Bassett
+24058,Belspring
+24059,Bent Mountain
+24060,Blacksburg
+24061,Blacksburg
+24062,Blacksburg
+24063,Blacksburg
+24064,Blue Ridge
+24065,Boones Mill
+24066,Buchanan
+24067,Callaway
+24068,Christiansburg
+24069,Cascade
+24070,Catawba
+24072,Check
+24073,Christiansburg
+24076,Claudville
+24077,Cloverdale
+24078,Collinsville
+24079,Copper Hill
+24082,Critz
+24083,Daleville
+24084,Dublin
+24085,Eagle Rock
+24086,Eggleston
+24087,Elliston
+24088,Ferrum
+24089,Fieldale
+24090,Fincastle
+24091,Floyd
+24092,Glade Hill
+24093,Glen Lyn
+24094,Goldbond
+24095,Goodview
+24101,Hardy
+24102,Henry
+24104,Huddleston
+24105,Indian Valley
+24111,Mc Coy
+24112,Martinsville
+24113,Martinsville
+24114,Martinsville
+24115,Martinsville
+24120,Meadows of Dan
+24121,Moneta
+24122,Montvale
+24124,Narrows
+24126,Newbern
+24127,New Castle
+24128,Newport
+24129,New River
+24130,Oriskany
+24131,Paint Bank
+24132,Parrott
+24133,Patrick Springs
+24134,Pearisburg
+24136,Pembroke
+24137,Penhook
+24138,Pilot
+24139,Pittsville
+24141,Radford
+24142,Radford
+24143,Radford
+24146,Redwood
+24147,Rich Creek
+24148,Ridgeway
+24149,Riner
+24150,Ripplemead
+24151,Rocky Mount
+24153,Salem
+24155,Salem
+24156,Salem
+24157,Salem
+24161,Sandy Level
+24162,Shawsville
+24165,Spencer
+24167,Staffordsville
+24168,Stanleytown
+24171,Stuart
+24174,Thaxton
+24175,Troutville
+24176,Union Hall
+24177,Vesta
+24178,Villamont
+24179,Vinton
+24184,Wirtz
+24185,Woolwine
+24201,Bristol
+24202,Bristol
+24203,Bristol
+24209,Bristol
+24210,Abingdon
+24211,Abingdon
+24212,Abingdon
+24215,Andover
+24216,Appalachia
+24217,Bee
+24218,Ben Hur
+24219,Big Stone Gap
+24220,Birchleaf
+24221,Blackwater
+24224,Castlewood
+24225,Cleveland
+24226,Clinchco
+24228,Clintwood
+24230,Coeburn
+24236,Damascus
+24237,Dante
+24239,Davenport
+24243,Dryden
+24244,Duffield
+24245,Dungannon
+24246,East Stone Gap
+24248,Ewing
+24250,Fort Blackmore
+24251,Gate City
+24256,Haysi
+24258,Hiltons
+24260,Honaker
+24263,Jonesville
+24265,Keokee
+24266,Lebanon
+24269,Mc Clure
+24270,Mendota
+24271,Nickelsville
+24272,Nora
+24273,Norton
+24277,Pennington Gap
+24279,Pound
+24280,Rosedale
+24281,Rose Hill
+24282,Saint Charles
+24283,Saint Paul
+24285,Stonega
+24289,Trammel
+24290,Weber City
+24292,Whitetop
+24293,Wise
+24301,Pulaski
+24311,Atkins
+24312,Austinville
+24313,Barren Springs
+24314,Bastian
+24315,Bland
+24316,Broadford
+24317,Cana
+24318,Ceres
+24319,Chilhowie
+24322,Cripple Creek
+24323,Crockett
+24324,Draper
+24325,Dugspur
+24326,Elk Creek
+24327,Emory
+24328,Fancy Gap
+24330,Fries
+24333,Galax
+24340,Glade Spring
+24343,Hillsville
+24347,Hiwassee
+24348,Independence
+24350,Ivanhoe
+24351,Lambsburg
+24352,Laurel Fork
+24354,Marion
+24360,Max Meadows
+24361,Meadowview
+24363,Mouth of Wilson
+24366,Rocky Gap
+24368,Rural Retreat
+24370,Saltville
+24373,Seven Mile Ford
+24374,Speedwell
+24375,Sugar Grove
+24377,Tannersville
+24378,Trout Dale
+24379,Volney
+24380,Willis
+24381,Woodlawn
+24382,Wytheville
+24401,Staunton
+24402,Staunton
+24407,Staunton
+24411,Augusta Springs
+24412,Bacova
+24413,Blue Grass
+24415,Brownsburg
+24416,Buena Vista
+24421,Churchville
+24422,Clifton Forge
+24426,Covington
+24430,Craigsville
+24431,Crimora
+24432,Deerfield
+24433,Doe Hill
+24435,Fairfield
+24437,Fort Defiance
+24438,Glen Wilton
+24439,Goshen
+24440,Greenville
+24441,Grottoes
+24442,Head Waters
+24445,Hot Springs
+24448,Iron Gate
+24450,Lexington
+24457,Low Moor
+24458,Mc Dowell
+24459,Middlebrook
+24460,Millboro
+24463,Mint Spring
+24464,Montebello
+24465,Monterey
+24467,Mount Sidney
+24468,Mustoe
+24469,New Hope
+24471,Port Republic
+24472,Raphine
+24473,Rockbridge Baths
+24474,Selma
+24475,Spottswood
+24476,Steeles Tavern
+24477,Stuarts Draft
+24479,Swoope
+24482,Verona
+24483,Vesuvius
+24484,Warm Springs
+24485,West Augusta
+24486,Weyers Cave
+24487,Williamsville
+24501,Lynchburg
+24502,Lynchburg
+24503,Lynchburg
+24504,Lynchburg
+24505,Lynchburg
+24506,Lynchburg
+24512,Lynchburg
+24513,Lynchburg
+24514,Lynchburg
+24515,Lynchburg
+24517,Altavista
+24520,Alton
+24521,Amherst
+24522,Appomattox
+24523,Bedford
+24526,Big Island
+24527,Blairs
+24528,Brookneal
+24529,Buffalo Junction
+24530,Callands
+24531,Chatham
+24533,Clifford
+24534,Clover
+24535,Cluster Springs
+24536,Coleman Falls
+24538,Concord
+24539,Crystal Hill
+24540,Danville
+24541,Danville
+24543,Danville
+24544,Danville
+24549,Dry Fork
+24550,Evington
+24551,Forest
+24553,Gladstone
+24554,Gladys
+24555,Glasgow
+24556,Goode
+24557,Gretna
+24558,Halifax
+24562,Howardsville
+24563,Hurt
+24565,Java
+24566,Keeling
+24569,Long Island
+24570,Lowry
+24571,Lynch Station
+24572,Madison Heights
+24574,Monroe
+24576,Naruna
+24577,Nathalie
+24578,Natural Bridge
+24579,Natural Bridge Station
+24580,Nelson
+24581,Norwood
+24585,Republican Grove
+24586,Ringgold
+24588,Rustburg
+24589,Scottsburg
+24590,Scottsville
+24592,South Boston
+24593,Spout Spring
+24594,Sutherlin
+24595,Sweet Briar
+24597,Vernon Hill
+24598,Virgilina
+24599,Wingina
+24601,Amonate
+24602,Bandy
+24603,Big Rock
+24604,Bishop
+24605,Bluefield
+24606,Boissevain
+24607,Breaks
+24608,Burkes Garden
+24609,Cedar Bluff
+24612,Doran
+24613,Falls Mills
+24614,Grundy
+24618,Harman
+24619,Horsepen
+24620,Hurley
+24622,Jewell Ridge
+24624,Keen Mountain
+24627,Mavisdale
+24628,Maxie
+24630,North Tazewell
+24631,Oakwood
+24634,Pilgrims Knob
+24635,Pocahontas
+24637,Pounding Mill
+24639,Raven
+24640,Red Ash
+24641,Richlands
+24646,Rowe
+24647,Shortt Gap
+24649,Swords Creek
+24651,Tazewell
+24656,Vansant
+24657,Whitewood
+24658,Wolford
+84001,Altamont
+84002,Altonah
+84003,American Fork
+84004,Alpine
+84006,Bingham Canyon
+84007,Bluebell
+84008,Bonanza
+84010,Bountiful
+84011,Bountiful
+84013,Cedar Valley
+84014,Centerville
+84015,Clearfield
+84016,Clearfield
+84017,Coalville
+84018,Croydon
+84020,Draper
+84021,Duchesne
+84022,Dugway
+84023,Dutch John
+84024,Echo
+84025,Farmington
+84026,Fort Duchesne
+84027,Fruitland
+84028,Garden City
+84029,Grantsville
+84030,Gusher
+84031,Hanna
+84032,Heber City
+84033,Henefer
+84034,Ibapah
+84035,Jensen
+84036,Kamas
+84037,Kaysville
+84038,Laketown
+84039,Lapoint
+84040,Layton
+84041,Layton
+84042,Lindon
+84043,Lehi
+84044,Magna
+84046,Manila
+84047,Midvale
+84049,Midway
+84050,Morgan
+84051,Mountain Home
+84052,Myton
+84053,Neola
+84054,North Salt Lake
+84055,Oakley
+84056,Hill AFB
+84057,Orem
+84058,Orem
+84059,Orem
+84060,Park City
+84061,Peoa
+84062,Pleasant Grove
+84063,Randlett
+84064,Randolph
+84065,Riverton
+84066,Roosevelt
+84067,Roy
+84068,Park City
+84069,Rush Valley
+84070,Sandy
+84071,Stockton
+84072,Tabiona
+84073,Talmage
+84074,Tooele
+84075,Syracuse
+84076,Tridell
+84078,Vernal
+84079,Vernal
+84080,Vernon
+84082,Wallsburg
+84083,Wendover
+84084,West Jordan
+84085,Whiterocks
+84086,Woodruff
+84087,Woods Cross
+84088,West Jordan
+84089,Clearfield
+84090,Sandy
+84091,Sandy
+84092,Sandy
+84093,Sandy
+84094,Sandy
+84095,South Jordan
+84097,Orem
+84098,Park City
+84101,Salt Lake City
+84102,Salt Lake City
+84103,Salt Lake City
+84104,Salt Lake City
+84105,Salt Lake City
+84106,Salt Lake City
+84107,Salt Lake City
+84108,Salt Lake City
+84109,Salt Lake City
+84110,Salt Lake City
+84111,Salt Lake City
+84112,Salt Lake City
+84113,Salt Lake City
+84114,Salt Lake City
+84115,Salt Lake City
+84116,Salt Lake City
+84117,Salt Lake City
+84118,Salt Lake City
+84119,Salt Lake City
+84120,Salt Lake City
+84121,Salt Lake City
+84122,Salt Lake City
+84123,Salt Lake City
+84124,Salt Lake City
+84125,Salt Lake City
+84126,Salt Lake City
+84127,Salt Lake City
+84128,Salt Lake City
+84130,Salt Lake City
+84131,Salt Lake City
+84132,Salt Lake City
+84133,Salt Lake City
+84134,Salt Lake City
+84135,Salt Lake City
+84136,Salt Lake City
+84137,Salt Lake City
+84138,Salt Lake City
+84139,Salt Lake City
+84140,Salt Lake City
+84141,Salt Lake City
+84142,Salt Lake City
+84143,Salt Lake City
+84144,Salt Lake City
+84145,Salt Lake City
+84147,Salt Lake City
+84148,Salt Lake City
+84150,Salt Lake City
+84151,Salt Lake City
+84152,Salt Lake City
+84153,Salt Lake City
+84157,Salt Lake City
+84158,Salt Lake City
+84165,Salt Lake City
+84170,Salt Lake City
+84171,Salt Lake City
+84180,Salt Lake City
+84184,Salt Lake City
+84185,Salt Lake City
+84189,Salt Lake City
+84190,Salt Lake City
+84199,Salt Lake City
+84201,Ogden
+84244,Ogden
+84301,Bear River City
+84302,Brigham City
+84304,Cache Junction
+84305,Clarkston
+84306,Collinston
+84307,Corinne
+84308,Cornish
+84309,Deweyville
+84310,Eden
+84311,Fielding
+84312,Garland
+84313,Grouse Creek
+84314,Honeyville
+84315,Hooper
+84316,Howell
+84317,Huntsville
+84318,Hyde Park
+84319,Hyrum
+84320,Lewiston
+84321,Logan
+84322,Logan
+84323,Logan
+84324,Mantua
+84325,Mendon
+84326,Millville
+84327,Newton
+84328,Paradise
+84329,Park Valley
+84330,Plymouth
+84331,Portage
+84332,Providence
+84333,Richmond
+84334,Riverside
+84335,Smithfield
+84336,Snowville
+84337,Tremonton
+84338,Trenton
+84339,Wellsville
+84340,Willard
+84341,Logan
+84401,Ogden
+84402,Ogden
+84403,Ogden
+84404,Ogden
+84405,Ogden
+84407,Ogden
+84408,Ogden
+84409,Ogden
+84412,Ogden
+84414,Ogden
+84415,Ogden
+84501,Price
+84510,Aneth
+84511,Blanding
+84512,Bluff
+84513,Castle Dale
+84515,Cisco
+84516,Clawson
+84518,Cleveland
+84520,East Carbon
+84521,Elmo
+84522,Emery
+84523,Ferron
+84525,Green River
+84526,Helper
+84527,Hiawatha
+84528,Huntington
+84529,Kenilworth
+84530,La Sal
+84531,Mexican Hat
+84532,Moab
+84533,Lake Powell
+84534,Montezuma Creek
+84535,Monticello
+84536,Monument Valley
+84537,Orangeville
+84539,Sunnyside
+84540,Thompson
+84542,Wellington
+84601,Provo
+84602,Provo
+84603,Provo
+84604,Provo
+84605,Provo
+84606,Provo
+84620,Aurora
+84621,Axtell
+84622,Centerfield
+84623,Chester
+84624,Delta
+84626,Elberta
+84627,Ephraim
+84628,Eureka
+84629,Fairview
+84630,Fayette
+84631,Fillmore
+84632,Fountain Green
+84633,Goshen
+84634,Gunnison
+84635,Hinckley
+84636,Holden
+84637,Kanosh
+84638,Leamington
+84639,Levan
+84640,Lynndyl
+84642,Manti
+84643,Mayfield
+84644,Meadow
+84645,Mona
+84646,Moroni
+84647,Mount Pleasant
+84648,Nephi
+84649,Oak City
+84650,Oasis
+84651,Payson
+84652,Redmond
+84653,Salem
+84654,Salina
+84655,Santaquin
+84656,Scipio
+84657,Sigurd
+84660,Spanish Fork
+84662,Spring City
+84663,Springville
+84664,Mapleton
+84665,Sterling
+84667,Wales
+84701,Richfield
+84710,Alton
+84711,Annabella
+84712,Antimony
+84713,Beaver
+84714,Beryl
+84715,Bicknell
+84716,Boulder
+84717,Bryce Canyon
+84718,Cannonville
+84719,Brian Head
+84720,Cedar City
+84721,Cedar City
+84722,Central
+84723,Circleville
+84724,Elsinore
+84725,Enterprise
+84726,Escalante
+84728,Garrison
+84729,Glendale
+84730,Glenwood
+84731,Greenville
+84732,Greenwich
+84733,Gunlock
+84734,Hanksville
+84735,Hatch
+84736,Henrieville
+84737,Hurricane
+84738,Ivins
+84739,Joseph
+84740,Junction
+84741,Kanab
+84742,Kanarraville
+84743,Kingston
+84744,Koosharem
+84745,La Verkin
+84746,Leeds
+84747,Loa
+84749,Lyman
+84750,Marysvale
+84751,Milford
+84752,Minersville
+84753,Modena
+84754,Monroe
+84755,Mount Carmel
+84756,Newcastle
+84757,New Harmony
+84758,Orderville
+84759,Panguitch
+84760,Paragonah
+84761,Parowan
+84762,Duck Creek Village
+84763,Rockville
+84764,Bryce
+84765,Santa Clara
+84766,Sevier
+84767,Springdale
+84770,Saint George
+84771,Saint George
+84772,Summit
+84773,Teasdale
+84774,Toquerville
+84775,Torrey
+84776,Tropic
+84779,Virgin
+84780,Washington
+84781,Pine Valley
+84782,Veyo
+84783,Dammeron Valley
+84784,Hildale
+84790,Saint George
+84791,Saint George
+73301,Austin
+73344,Austin
+75001,Addison
+75002,Allen
+75006,Carrollton
+75007,Carrollton
+75008,Carrollton
+75009,Celina
+75010,Carrollton
+75011,Carrollton
+75013,Allen
+75014,Irving
+75015,Irving
+75016,Irving
+75017,Irving
+75019,Coppell
+75020,Denison
+75021,Denison
+75022,Flower Mound
+75023,Plano
+75024,Plano
+75025,Plano
+75026,Plano
+75027,Flower Mound
+75028,Flower Mound
+75029,Lewisville
+75030,Rowlett
+75032,Rockwall
+75034,Frisco
+75035,Frisco
+75037,Irving
+75038,Irving
+75039,Irving
+75040,Garland
+75041,Garland
+75042,Garland
+75043,Garland
+75044,Garland
+75045,Garland
+75046,Garland
+75047,Garland
+75048,Garland
+75049,Garland
+75050,Grand Prairie
+75051,Grand Prairie
+75052,Grand Prairie
+75053,Grand Prairie
+75054,Grand Prairie
+75056,The Colony
+75057,Lewisville
+75058,Gunter
+75060,Irving
+75061,Irving
+75062,Irving
+75063,Irving
+75065,Lake Dallas
+75067,Lewisville
+75068,Little Elm
+75069,Mc Kinney
+75070,Mc Kinney
+75074,Plano
+75075,Plano
+75076,Pottsboro
+75077,Lewisville
+75078,Prosper
+75080,Richardson
+75081,Richardson
+75082,Richardson
+75083,Richardson
+75085,Richardson
+75086,Plano
+75087,Rockwall
+75088,Rowlett
+75089,Rowlett
+75090,Sherman
+75091,Sherman
+75092,Sherman
+75093,Plano
+75094,Plano
+75097,Weston
+75098,Wylie
+75099,Coppell
+75101,Bardwell
+75102,Barry
+75103,Canton
+75104,Cedar Hill
+75105,Chatfield
+75106,Cedar Hill
+75110,Corsicana
+75114,Crandall
+75115,Desoto
+75116,Duncanville
+75117,Edgewood
+75118,Elmo
+75119,Ennis
+75120,Ennis
+75121,Copeville
+75123,Desoto
+75124,Eustace
+75125,Ferris
+75126,Forney
+75127,Fruitvale
+75132,Fate
+75134,Lancaster
+75135,Caddo Mills
+75137,Duncanville
+75138,Duncanville
+75140,Grand Saline
+75141,Hutchins
+75142,Kaufman
+75143,Kemp
+75144,Kerens
+75146,Lancaster
+75147,Mabank
+75148,Malakoff
+75149,Mesquite
+75150,Mesquite
+75151,Corsicana
+75152,Palmer
+75153,Powell
+75154,Red Oak
+75155,Rice
+75157,Rosser
+75158,Scurry
+75159,Seagoville
+75160,Terrell
+75161,Terrell
+75163,Trinidad
+75164,Josephine
+75165,Waxahachie
+75166,Lavon
+75167,Waxahachie
+75168,Waxahachie
+75169,Wills Point
+75172,Wilmer
+75173,Nevada
+75180,Mesquite
+75181,Mesquite
+75182,Sunnyvale
+75185,Mesquite
+75187,Mesquite
+75189,Royse City
+75201,Dallas
+75202,Dallas
+75203,Dallas
+75204,Dallas
+75205,Dallas
+75206,Dallas
+75207,Dallas
+75208,Dallas
+75209,Dallas
+75210,Dallas
+75211,Dallas
+75212,Dallas
+75214,Dallas
+75215,Dallas
+75216,Dallas
+75217,Dallas
+75218,Dallas
+75219,Dallas
+75220,Dallas
+75221,Dallas
+75222,Dallas
+75223,Dallas
+75224,Dallas
+75225,Dallas
+75226,Dallas
+75227,Dallas
+75228,Dallas
+75229,Dallas
+75230,Dallas
+75231,Dallas
+75232,Dallas
+75233,Dallas
+75234,Dallas
+75235,Dallas
+75236,Dallas
+75237,Dallas
+75238,Dallas
+75239,Dallas
+75240,Dallas
+75241,Dallas
+75242,Dallas
+75243,Dallas
+75244,Dallas
+75245,Dallas
+75246,Dallas
+75247,Dallas
+75248,Dallas
+75249,Dallas
+75250,Dallas
+75251,Dallas
+75252,Dallas
+75253,Dallas
+75258,Dallas
+75260,Dallas
+75261,Dallas
+75262,Dallas
+75263,Dallas
+75264,Dallas
+75265,Dallas
+75266,Dallas
+75267,Dallas
+75270,Dallas
+75275,Dallas
+75277,Dallas
+75283,Dallas
+75284,Dallas
+75285,Dallas
+75286,Dallas
+75287,Dallas
+75294,Dallas
+75295,Dallas
+75301,Dallas
+75303,Dallas
+75310,Dallas
+75312,Dallas
+75313,Dallas
+75315,Dallas
+75320,Dallas
+75323,Dallas
+75326,Dallas
+75336,Dallas
+75339,Dallas
+75342,Dallas
+75346,Dallas
+75353,Dallas
+75354,Dallas
+75355,Dallas
+75356,Dallas
+75357,Dallas
+75359,Dallas
+75360,Dallas
+75363,Dallas
+75364,Dallas
+75367,Dallas
+75368,Dallas
+75370,Dallas
+75371,Dallas
+75372,Dallas
+75373,Dallas
+75374,Dallas
+75376,Dallas
+75378,Dallas
+75379,Dallas
+75380,Dallas
+75381,Dallas
+75382,Dallas
+75386,Dallas
+75387,Dallas
+75388,Dallas
+75389,Dallas
+75390,Dallas
+75391,Dallas
+75392,Dallas
+75393,Dallas
+75394,Dallas
+75395,Dallas
+75396,Dallas
+75397,Dallas
+75398,Dallas
+75401,Greenville
+75402,Greenville
+75403,Greenville
+75404,Greenville
+75407,Princeton
+75409,Anna
+75410,Alba
+75411,Arthur City
+75412,Bagwell
+75413,Bailey
+75414,Bells
+75415,Ben Franklin
+75416,Blossom
+75417,Bogata
+75418,Bonham
+75420,Brashear
+75421,Brookston
+75422,Campbell
+75423,Celeste
+75424,Blue Ridge
+75425,Chicota
+75426,Clarksville
+75428,Commerce
+75429,Commerce
+75431,Como
+75432,Cooper
+75433,Cumby
+75434,Cunningham
+75435,Deport
+75436,Detroit
+75437,Dike
+75438,Dodd City
+75439,Ector
+75440,Emory
+75441,Enloe
+75442,Farmersville
+75443,Gober
+75444,Golden
+75446,Honey Grove
+75447,Ivanhoe
+75448,Klondike
+75449,Ladonia
+75450,Lake Creek
+75451,Leesburg
+75452,Leonard
+75453,Lone Oak
+75454,Melissa
+75455,Mount Pleasant
+75456,Mount Pleasant
+75457,Mount Vernon
+75458,Merit
+75459,Howe
+75460,Paris
+75461,Paris
+75462,Paris
+75468,Pattonville
+75469,Pecan Gap
+75470,Petty
+75471,Pickton
+75472,Point
+75473,Powderly
+75474,Quinlan
+75475,Randolph
+75476,Ravenna
+75477,Roxton
+75478,Saltillo
+75479,Savoy
+75480,Scroggins
+75481,Sulphur Bluff
+75482,Sulphur Springs
+75483,Sulphur Springs
+75485,Westminster
+75486,Sumner
+75487,Talco
+75488,Telephone
+75489,Tom Bean
+75490,Trenton
+75491,Whitewright
+75492,Windom
+75493,Winfield
+75494,Winnsboro
+75495,Van Alstyne
+75496,Wolfe City
+75497,Yantis
+75501,Texarkana
+75503,Texarkana
+75504,Texarkana
+75505,Texarkana
+75507,Texarkana
+75550,Annona
+75551,Atlanta
+75554,Avery
+75555,Bivins
+75556,Bloomburg
+75558,Cookville
+75559,De Kalb
+75560,Douglassville
+75561,Hooks
+75562,Kildare
+75563,Linden
+75564,Lodi
+75565,Mc Leod
+75566,Marietta
+75567,Maud
+75568,Naples
+75569,Nash
+75570,New Boston
+75571,Omaha
+75572,Queen City
+75573,Redwater
+75574,Simms
+75599,Texarkana
+75601,Longview
+75602,Longview
+75603,Longview
+75604,Longview
+75605,Longview
+75606,Longview
+75607,Longview
+75608,Longview
+75615,Longview
+75630,Avinger
+75631,Beckville
+75633,Carthage
+75636,Cason
+75637,Clayton
+75638,Daingerfield
+75639,De Berry
+75640,Diana
+75641,Easton
+75642,Elysian Fields
+75643,Gary
+75644,Gilmer
+75647,Gladewater
+75650,Hallsville
+75651,Harleton
+75652,Henderson
+75653,Henderson
+75654,Henderson
+75656,Hughes Springs
+75657,Jefferson
+75658,Joinerville
+75659,Jonesville
+75660,Judson
+75661,Karnack
+75662,Kilgore
+75663,Kilgore
+75666,Laird Hill
+75667,Laneville
+75668,Lone Star
+75669,Long Branch
+75670,Marshall
+75671,Marshall
+75672,Marshall
+75680,Minden
+75681,Mount Enterprise
+75682,New London
+75683,Ore City
+75684,Overton
+75685,Panola
+75686,Pittsburg
+75687,Price
+75688,Scottsville
+75689,Selman City
+75691,Tatum
+75692,Waskom
+75693,White Oak
+75694,Woodlawn
+75701,Tyler
+75702,Tyler
+75703,Tyler
+75704,Tyler
+75705,Tyler
+75706,Tyler
+75707,Tyler
+75708,Tyler
+75709,Tyler
+75710,Tyler
+75711,Tyler
+75712,Tyler
+75713,Tyler
+75750,Arp
+75751,Athens
+75754,Ben Wheeler
+75755,Big Sandy
+75756,Brownsboro
+75757,Bullard
+75758,Chandler
+75759,Cuney
+75760,Cushing
+75762,Flint
+75763,Frankston
+75764,Gallatin
+75765,Hawkins
+75766,Jacksonville
+75770,Larue
+75771,Lindale
+75772,Maydelle
+75773,Mineola
+75778,Murchison
+75779,Neches
+75780,New Summerfield
+75782,Poynor
+75783,Quitman
+75784,Reklaw
+75785,Rusk
+75788,Sacul
+75789,Troup
+75790,Van
+75791,Whitehouse
+75792,Winona
+75798,Tyler
+75799,Tyler
+75801,Palestine
+75802,Palestine
+75831,Buffalo
+75832,Cayuga
+75833,Centerville
+75834,Centralia
+75835,Crockett
+75838,Donie
+75839,Elkhart
+75840,Fairfield
+75844,Grapeland
+75845,Groveton
+75846,Jewett
+75847,Kennard
+75848,Kirvin
+75849,Latexo
+75850,Leona
+75851,Lovelady
+75852,Midway
+75853,Montalba
+75855,Oakwood
+75856,Pennington
+75858,Ratcliff
+75859,Streetman
+75860,Teague
+75861,Tennessee Colony
+75862,Trinity
+75865,Woodlake
+75880,Tennessee Colony
+75882,Palestine
+75884,Tennessee Colony
+75886,Tennessee Colony
+75901,Lufkin
+75902,Lufkin
+75903,Lufkin
+75904,Lufkin
+75915,Lufkin
+75925,Alto
+75926,Apple Springs
+75928,Bon Wier
+75929,Broaddus
+75930,Bronson
+75931,Brookeland
+75932,Burkeville
+75933,Call
+75934,Camden
+75935,Center
+75936,Chester
+75937,Chireno
+75938,Colmesneil
+75939,Corrigan
+75941,Diboll
+75942,Doucette
+75943,Douglass
+75944,Etoile
+75946,Garrison
+75947,Geneva
+75948,Hemphill
+75949,Huntington
+75951,Jasper
+75954,Joaquin
+75956,Kirbyville
+75958,Martinsville
+75959,Milam
+75960,Moscow
+75961,Nacogdoches
+75962,Nacogdoches
+75963,Nacogdoches
+75964,Nacogdoches
+75966,Newton
+75968,Pineland
+75969,Pollok
+75972,San Augustine
+75973,Shelbyville
+75974,Tenaha
+75975,Timpson
+75976,Wells
+75977,Wiergate
+75978,Woden
+75979,Woodville
+75980,Zavalla
+75990,Woodville
+76001,Arlington
+76002,Arlington
+76003,Arlington
+76004,Arlington
+76005,Arlington
+76006,Arlington
+76007,Arlington
+76008,Aledo
+76009,Alvarado
+76010,Arlington
+76011,Arlington
+76012,Arlington
+76013,Arlington
+76014,Arlington
+76015,Arlington
+76016,Arlington
+76017,Arlington
+76018,Arlington
+76019,Arlington
+76020,Azle
+76021,Bedford
+76022,Bedford
+76023,Boyd
+76028,Burleson
+76031,Cleburne
+76033,Cleburne
+76034,Colleyville
+76035,Cresson
+76036,Crowley
+76039,Euless
+76040,Euless
+76041,Forreston
+76043,Glen Rose
+76044,Godley
+76048,Granbury
+76049,Granbury
+76050,Grandview
+76051,Grapevine
+76052,Haslet
+76053,Hurst
+76054,Hurst
+76055,Itasca
+76058,Joshua
+76059,Keene
+76060,Kennedale
+76061,Lillian
+76063,Mansfield
+76064,Maypearl
+76065,Midlothian
+76066,Millsap
+76067,Mineral Wells
+76068,Mineral Wells
+76070,Nemo
+76071,Newark
+76073,Paradise
+76077,Rainbow
+76078,Rhome
+76082,Springtown
+76084,Venus
+76086,Weatherford
+76087,Weatherford
+76088,Weatherford
+76092,Grapevine
+76093,Rio Vista
+76094,Arlington
+76095,Bedford
+76096,Arlington
+76097,Burleson
+76098,Azle
+76099,Grapevine
+76101,Fort Worth
+76102,Fort Worth
+76103,Fort Worth
+76104,Fort Worth
+76105,Fort Worth
+76106,Fort Worth
+76107,Fort Worth
+76108,Fort Worth
+76109,Fort Worth
+76110,Fort Worth
+76111,Fort Worth
+76112,Fort Worth
+76113,Fort Worth
+76114,Fort Worth
+76115,Fort Worth
+76116,Fort Worth
+76117,Haltom City
+76118,Fort Worth
+76119,Fort Worth
+76120,Fort Worth
+76121,Fort Worth
+76122,Fort Worth
+76123,Fort Worth
+76124,Fort Worth
+76126,Fort Worth
+76127,Naval Air Station/ jrb
+76129,Fort Worth
+76130,Fort Worth
+76131,Fort Worth
+76132,Fort Worth
+76133,Fort Worth
+76134,Fort Worth
+76135,Fort Worth
+76136,Fort Worth
+76137,Fort Worth
+76140,Fort Worth
+76147,Fort Worth
+76148,Fort Worth
+76150,Fort Worth
+76155,Fort Worth
+76161,Fort Worth
+76162,Fort Worth
+76163,Fort Worth
+76164,Fort Worth
+76177,Fort Worth
+76178,Fort Worth
+76179,Fort Worth
+76180,North Richland Hills
+76181,Fort Worth
+76182,North Richland Hills
+76185,Fort Worth
+76191,Fort Worth
+76192,Fort Worth
+76193,Fort Worth
+76195,Fort Worth
+76196,Fort Worth
+76197,Fort Worth
+76198,Fort Worth
+76199,Fort Worth
+76201,Denton
+76202,Denton
+76203,Denton
+76204,Denton
+76205,Denton
+76206,Denton
+76207,Denton
+76208,Denton
+76225,Alvord
+76226,Argyle
+76227,Aubrey
+76228,Bellevue
+76230,Bowie
+76233,Collinsville
+76234,Decatur
+76238,Era
+76239,Forestburg
+76240,Gainesville
+76241,Gainesville
+76244,Keller
+76245,Gordonville
+76246,Greenwood
+76247,Justin
+76248,Keller
+76249,Krum
+76250,Lindsay
+76251,Montague
+76252,Muenster
+76253,Myra
+76255,Nocona
+76258,Pilot Point
+76259,Ponder
+76261,Ringgold
+76262,Roanoke
+76263,Rosston
+76264,Sadler
+76265,Saint Jo
+76266,Sanger
+76267,Slidell
+76268,Southmayd
+76270,Sunset
+76271,Tioga
+76272,Valley View
+76273,Whitesboro
+76299,Roanoke
+76301,Wichita Falls
+76302,Wichita Falls
+76305,Wichita Falls
+76306,Wichita Falls
+76307,Wichita Falls
+76308,Wichita Falls
+76309,Wichita Falls
+76310,Wichita Falls
+76311,Sheppard AFB
+76351,Archer City
+76352,Bluegrove
+76354,Burkburnett
+76357,Byers
+76360,Electra
+76363,Goree
+76364,Harrold
+76365,Henrietta
+76366,Holliday
+76367,Iowa Park
+76369,Kamay
+76370,Megargel
+76371,Munday
+76372,Newcastle
+76373,Oklaunion
+76374,Olney
+76377,Petrolia
+76379,Scotland
+76380,Seymour
+76384,Vernon
+76385,Vernon
+76388,Weinert
+76389,Windthorst
+76401,Stephenville
+76402,Stephenville
+76424,Breckenridge
+76426,Bridgeport
+76427,Bryson
+76429,Caddo
+76430,Albany
+76431,Chico
+76432,Blanket
+76433,Bluff Dale
+76435,Carbon
+76436,Carlton
+76437,Cisco
+76439,Dennis
+76442,Comanche
+76443,Cross Plains
+76444,De Leon
+76445,Desdemona
+76446,Dublin
+76448,Eastland
+76449,Graford
+76450,Graham
+76452,Energy
+76453,Gordon
+76454,Gorman
+76455,Gustine
+76457,Hico
+76458,Jacksboro
+76459,Jermyn
+76460,Loving
+76461,Lingleville
+76462,Lipan
+76463,Mingus
+76464,Moran
+76465,Morgan Mill
+76466,Olden
+76467,Paluxy
+76468,Proctor
+76469,Putnam
+76470,Ranger
+76471,Rising Star
+76472,Santo
+76474,Sidney
+76475,Strawn
+76476,Tolar
+76481,South Bend
+76483,Throckmorton
+76484,Palo Pinto
+76485,Peaster
+76486,Perrin
+76487,Poolville
+76490,Whitt
+76491,Woodson
+76501,Temple
+76502,Temple
+76503,Temple
+76504,Temple
+76505,Temple
+76508,Temple
+76511,Bartlett
+76513,Belton
+76518,Buckholts
+76519,Burlington
+76520,Cameron
+76522,Copperas Cove
+76523,Davilla
+76524,Eddy
+76525,Evant
+76526,Flat
+76527,Florence
+76528,Gatesville
+76530,Granger
+76531,Hamilton
+76533,Heidenheimer
+76534,Holland
+76537,Jarrell
+76538,Jonesboro
+76539,Kempner
+76540,Killeen
+76541,Killeen
+76542,Killeen
+76543,Killeen
+76544,Killeen
+76545,Killeen
+76546,Killeen
+76547,Killeen
+76548,Harker Heights
+76549,Killeen
+76550,Lampasas
+76552,Leon Junction
+76554,Little River
+76555,Maysfield
+76556,Milano
+76557,Moody
+76558,Mound
+76559,Nolanville
+76561,Oglesby
+76564,Pendleton
+76565,Pottsville
+76566,Purmela
+76567,Rockdale
+76569,Rogers
+76570,Rosebud
+76571,Salado
+76573,Schwertner
+76574,Taylor
+76577,Thorndale
+76578,Thrall
+76579,Troy
+76596,Gatesville
+76597,Gatesville
+76598,Gatesville
+76599,Gatesville
+76621,Abbott
+76622,Aquilla
+76623,Avalon
+76624,Axtell
+76626,Blooming Grove
+76627,Blum
+76628,Brandon
+76629,Bremond
+76630,Bruceville
+76631,Bynum
+76632,Chilton
+76633,China Spring
+76634,Clifton
+76635,Coolidge
+76636,Covington
+76637,Cranfills Gap
+76638,Crawford
+76639,Dawson
+76640,Elm Mott
+76641,Frost
+76642,Groesbeck
+76643,Hewitt
+76644,Laguna Park
+76645,Hillsboro
+76648,Hubbard
+76649,Iredell
+76650,Irene
+76651,Italy
+76652,Kopperl
+76653,Kosse
+76654,Leroy
+76655,Lorena
+76656,Lott
+76657,Mc Gregor
+76660,Malone
+76661,Marlin
+76664,Mart
+76665,Meridian
+76666,Mertens
+76667,Mexia
+76670,Milford
+76671,Morgan
+76673,Mount Calm
+76675,Otto
+76676,Penelope
+76677,Perry
+76678,Prairie Hill
+76679,Purdon
+76680,Reagan
+76681,Richland
+76682,Riesel
+76684,Ross
+76685,Satin
+76686,Tehuacana
+76687,Thornton
+76689,Valley Mills
+76690,Walnut Springs
+76691,West
+76692,Whitney
+76693,Wortham
+76701,Waco
+76702,Waco
+76703,Waco
+76704,Waco
+76705,Waco
+76706,Waco
+76707,Waco
+76708,Waco
+76710,Waco
+76711,Waco
+76712,Woodway
+76714,Waco
+76715,Waco
+76716,Waco
+76795,Waco
+76797,Waco
+76798,Waco
+76799,Waco
+76801,Brownwood
+76802,Early
+76803,Brownwood
+76804,Brownwood
+76820,Art
+76821,Ballinger
+76823,Bangs
+76824,Bend
+76825,Brady
+76827,Brookesmith
+76828,Burkett
+76831,Castell
+76832,Cherokee
+76834,Coleman
+76836,Doole
+76837,Eden
+76841,Fort Mc Kavett
+76842,Fredonia
+76844,Goldthwaite
+76845,Gouldbusk
+76848,Hext
+76849,Junction
+76852,Lohn
+76853,Lometa
+76854,London
+76855,Lowake
+76856,Mason
+76857,May
+76858,Melvin
+76859,Menard
+76861,Miles
+76862,Millersview
+76864,Mullin
+76865,Norton
+76866,Paint Rock
+76867,Pear Valley
+76869,Pontotoc
+76870,Priddy
+76871,Richland Springs
+76872,Rochelle
+76873,Rockwood
+76874,Roosevelt
+76875,Rowena
+76877,San Saba
+76878,Santa Anna
+76880,Star
+76882,Talpa
+76883,Telegraph
+76884,Valera
+76885,Valley Spring
+76886,Veribest
+76887,Voca
+76888,Voss
+76890,Zephyr
+76901,San Angelo
+76902,San Angelo
+76903,San Angelo
+76904,San Angelo
+76905,San Angelo
+76906,San Angelo
+76908,Goodfellow AFB
+76909,San Angelo
+76930,Barnhart
+76932,Big Lake
+76933,Bronte
+76934,Carlsbad
+76935,Christoval
+76936,Eldorado
+76937,Eola
+76939,Knickerbocker
+76940,Mereta
+76941,Mertzon
+76943,Ozona
+76945,Robert Lee
+76949,Silver
+76950,Sonora
+76951,Sterling City
+76953,Tennyson
+76955,Vancourt
+76957,Wall
+76958,Water Valley
+77001,Houston
+77002,Houston
+77003,Houston
+77004,Houston
+77005,Houston
+77006,Houston
+77007,Houston
+77008,Houston
+77009,Houston
+77010,Houston
+77011,Houston
+77012,Houston
+77013,Houston
+77014,Houston
+77015,Houston
+77016,Houston
+77017,Houston
+77018,Houston
+77019,Houston
+77020,Houston
+77021,Houston
+77022,Houston
+77023,Houston
+77024,Houston
+77025,Houston
+77026,Houston
+77027,Houston
+77028,Houston
+77029,Houston
+77030,Houston
+77031,Houston
+77032,Houston
+77033,Houston
+77034,Houston
+77035,Houston
+77036,Houston
+77037,Houston
+77038,Houston
+77039,Houston
+77040,Houston
+77041,Houston
+77042,Houston
+77043,Houston
+77044,Houston
+77045,Houston
+77046,Houston
+77047,Houston
+77048,Houston
+77049,Houston
+77050,Houston
+77051,Houston
+77052,Houston
+77053,Houston
+77054,Houston
+77055,Houston
+77056,Houston
+77057,Houston
+77058,Houston
+77059,Houston
+77060,Houston
+77061,Houston
+77062,Houston
+77063,Houston
+77064,Houston
+77065,Houston
+77066,Houston
+77067,Houston
+77068,Houston
+77069,Houston
+77070,Houston
+77071,Houston
+77072,Houston
+77073,Houston
+77074,Houston
+77075,Houston
+77076,Houston
+77077,Houston
+77078,Houston
+77079,Houston
+77080,Houston
+77081,Houston
+77082,Houston
+77083,Houston
+77084,Houston
+77085,Houston
+77086,Houston
+77087,Houston
+77088,Houston
+77089,Houston
+77090,Houston
+77091,Houston
+77092,Houston
+77093,Houston
+77094,Houston
+77095,Houston
+77096,Houston
+77097,Houston
+77098,Houston
+77099,Houston
+77201,Houston
+77202,Houston
+77203,Houston
+77204,Houston
+77205,Houston
+77206,Houston
+77207,Houston
+77208,Houston
+77209,Houston
+77210,Houston
+77212,Houston
+77213,Houston
+77215,Houston
+77216,Houston
+77217,Houston
+77218,Houston
+77219,Houston
+77220,Houston
+77221,Houston
+77222,Houston
+77223,Houston
+77224,Houston
+77225,Houston
+77226,Houston
+77227,Houston
+77228,Houston
+77229,Houston
+77230,Houston
+77231,Houston
+77233,Houston
+77234,Houston
+77235,Houston
+77236,Houston
+77237,Houston
+77238,Houston
+77240,Houston
+77241,Houston
+77242,Houston
+77243,Houston
+77244,Houston
+77245,Houston
+77248,Houston
+77249,Houston
+77251,Houston
+77252,Houston
+77253,Houston
+77254,Houston
+77255,Houston
+77256,Houston
+77257,Houston
+77258,Houston
+77259,Houston
+77261,Houston
+77262,Houston
+77263,Houston
+77265,Houston
+77266,Houston
+77267,Houston
+77268,Houston
+77269,Houston
+77270,Houston
+77271,Houston
+77272,Houston
+77273,Houston
+77274,Houston
+77275,Houston
+77277,Houston
+77279,Houston
+77280,Houston
+77281,Houston
+77282,Houston
+77284,Houston
+77287,Houston
+77288,Houston
+77289,Houston
+77290,Houston
+77291,Houston
+77292,Houston
+77293,Houston
+77297,Houston
+77298,Houston
+77299,Houston
+77301,Conroe
+77302,Conroe
+77303,Conroe
+77304,Conroe
+77305,Conroe
+77306,Conroe
+77315,North Houston
+77316,Montgomery
+77318,Willis
+77320,Huntsville
+77325,Humble
+77326,Ace
+77327,Cleveland
+77328,Cleveland
+77331,Coldspring
+77332,Dallardsville
+77333,Dobbin
+77334,Dodge
+77335,Goodrich
+77336,Huffman
+77337,Hufsmith
+77338,Humble
+77339,Humble
+77340,Huntsville
+77341,Huntsville
+77342,Huntsville
+77343,Huntsville
+77344,Huntsville
+77345,Humble
+77346,Humble
+77347,Humble
+77348,Huntsville
+77349,Huntsville
+77350,Leggett
+77351,Livingston
+77353,Magnolia
+77354,Magnolia
+77355,Magnolia
+77356,Montgomery
+77357,New Caney
+77358,New Waverly
+77359,Oakhurst
+77360,Onalaska
+77362,Pinehurst
+77363,Plantersville
+77364,Pointblank
+77365,Porter
+77367,Riverside
+77368,Romayor
+77369,Rye
+77371,Shepherd
+77372,Splendora
+77373,Spring
+77374,Thicket
+77375,Tomball
+77376,Votaw
+77377,Tomball
+77378,Willis
+77379,Spring
+77380,Spring
+77381,Spring
+77382,Spring
+77383,Spring
+77384,Conroe
+77385,Conroe
+77386,Spring
+77387,Spring
+77388,Spring
+77389,Spring
+77391,Spring
+77393,Spring
+77396,Humble
+77399,Livingston
+77401,Bellaire
+77402,Bellaire
+77404,Bay City
+77406,Richmond
+77410,Cypress
+77411,Alief
+77412,Altair
+77413,Barker
+77414,Bay City
+77415,Cedar Lane
+77417,Beasley
+77418,Bellville
+77419,Blessing
+77420,Boling
+77422,Brazoria
+77423,Brookshire
+77426,Chappell Hill
+77428,Collegeport
+77429,Cypress
+77430,Damon
+77431,Danciger
+77432,Danevang
+77433,Cypress
+77434,Eagle Lake
+77435,East Bernard
+77436,Egypt
+77437,El Campo
+77440,Elmaton
+77441,Fulshear
+77442,Garwood
+77443,Glen Flora
+77444,Guy
+77445,Hempstead
+77446,Prairie View
+77447,Hockley
+77448,Hungerford
+77449,Katy
+77450,Katy
+77451,Kendleton
+77452,Kenney
+77453,Lane City
+77454,Lissie
+77455,Louise
+77456,Markham
+77457,Matagorda
+77458,Midfield
+77459,Missouri City
+77460,Nada
+77461,Needville
+77462,Newgulf
+77463,Old Ocean
+77464,Orchard
+77465,Palacios
+77466,Pattison
+77467,Pierce
+77468,Pledger
+77469,Richmond
+77470,Rock Island
+77471,Rosenberg
+77473,San Felipe
+77474,Sealy
+77475,Sheridan
+77476,Simonton
+77477,Stafford
+77478,Sugar Land
+77479,Sugar Land
+77480,Sweeny
+77481,Thompsons
+77482,Van Vleck
+77483,Wadsworth
+77484,Waller
+77485,Wallis
+77486,West Columbia
+77487,Sugar Land
+77488,Wharton
+77489,Missouri City
+77491,Katy
+77492,Katy
+77493,Katy
+77494,Katy
+77496,Sugar Land
+77497,Stafford
+77501,Pasadena
+77502,Pasadena
+77503,Pasadena
+77504,Pasadena
+77505,Pasadena
+77506,Pasadena
+77507,Pasadena
+77508,Pasadena
+77510,Santa FE
+77511,Alvin
+77512,Alvin
+77514,Anahuac
+77515,Angleton
+77516,Angleton
+77517,Santa FE
+77518,Bacliff
+77519,Batson
+77520,Baytown
+77521,Baytown
+77522,Baytown
+77530,Channelview
+77531,Clute
+77532,Crosby
+77533,Daisetta
+77534,Danbury
+77535,Dayton
+77536,Deer Park
+77538,Devers
+77539,Dickinson
+77541,Freeport
+77542,Freeport
+77545,Fresno
+77546,Friendswood
+77547,Galena Park
+77549,Friendswood
+77550,Galveston
+77551,Galveston
+77552,Galveston
+77553,Galveston
+77554,Galveston
+77555,Galveston
+77560,Hankamer
+77561,Hardin
+77562,Highlands
+77563,Hitchcock
+77564,Hull
+77565,Kemah
+77566,Lake Jackson
+77568,La Marque
+77571,La Porte
+77572,La Porte
+77573,League City
+77574,League City
+77575,Liberty
+77577,Liverpool
+77578,Manvel
+77580,Mont Belvieu
+77581,Pearland
+77582,Raywood
+77583,Rosharon
+77584,Pearland
+77585,Saratoga
+77586,Seabrook
+77587,South Houston
+77588,Pearland
+77590,Texas City
+77591,Texas City
+77592,Texas City
+77597,Wallisville
+77598,Webster
+77611,Bridge City
+77612,Buna
+77613,China
+77614,Deweyville
+77615,Evadale
+77616,Fred
+77617,Gilchrist
+77619,Groves
+77622,Hamshire
+77623,High Island
+77624,Hillister
+77625,Kountze
+77626,Mauriceville
+77627,Nederland
+77629,Nome
+77630,Orange
+77631,Orange
+77632,Orange
+77639,Orangefield
+77640,Port Arthur
+77641,Port Arthur
+77642,Port Arthur
+77643,Port Arthur
+77650,Port Bolivar
+77651,Port Neches
+77655,Sabine Pass
+77656,Silsbee
+77657,Lumberton
+77659,Sour Lake
+77660,Spurger
+77661,Stowell
+77662,Vidor
+77663,Village Mills
+77664,Warren
+77665,Winnie
+77670,Vidor
+77701,Beaumont
+77702,Beaumont
+77703,Beaumont
+77704,Beaumont
+77705,Beaumont
+77706,Beaumont
+77707,Beaumont
+77708,Beaumont
+77709,Voth
+77710,Beaumont
+77713,Beaumont
+77720,Beaumont
+77725,Beaumont
+77726,Beaumont
+77801,Bryan
+77802,Bryan
+77803,Bryan
+77805,Bryan
+77806,Bryan
+77807,Bryan
+77808,Bryan
+77830,Anderson
+77831,Bedias
+77833,Brenham
+77834,Brenham
+77835,Burton
+77836,Caldwell
+77837,Calvert
+77838,Chriesman
+77839,Clay
+77840,College Station
+77841,College Station
+77842,College Station
+77843,College Station
+77844,College Station
+77845,College Station
+77850,Concord
+77852,Deanville
+77853,Dime Box
+77855,Flynn
+77856,Franklin
+77857,Gause
+77859,Hearne
+77861,Iola
+77862,Kurten
+77863,Lyons
+77864,Madisonville
+77865,Marquez
+77866,Millican
+77867,Mumford
+77868,Navasota
+77869,Navasota
+77870,New Baden
+77871,Normangee
+77872,North Zulch
+77873,Richards
+77875,Roans Prairie
+77876,Shiro
+77878,Snook
+77879,Somerville
+77880,Washington
+77881,Wellborn
+77882,Wheelock
+77901,Victoria
+77902,Victoria
+77903,Victoria
+77904,Victoria
+77905,Victoria
+77950,Austwell
+77951,Bloomington
+77954,Cuero
+77957,Edna
+77960,Fannin
+77961,Francitas
+77962,Ganado
+77963,Goliad
+77964,Hallettsville
+77967,Hochheim
+77968,Inez
+77969,La Salle
+77970,La Ward
+77971,Lolita
+77972,Long Mott
+77973,McFaddin
+77974,Meyersville
+77975,Moulton
+77976,Nursery
+77977,Placedo
+77978,Point Comfort
+77979,Port Lavaca
+77982,Port O Connor
+77983,Seadrift
+77984,Shiner
+77985,Speaks
+77986,Sublime
+77987,Sweet Home
+77988,Telferner
+77989,Thomaston
+77990,Tivoli
+77991,Vanderbilt
+77993,Weesatche
+77994,Westhoff
+77995,Yoakum
+78001,Artesia Wells
+78002,Atascosa
+78003,Bandera
+78004,Bergheim
+78005,Bigfoot
+78006,Boerne
+78007,Calliham
+78008,Campbellton
+78009,Castroville
+78010,Center Point
+78011,Charlotte
+78012,Christine
+78013,Comfort
+78014,Cotulla
+78015,Boerne
+78016,Devine
+78017,Dilley
+78019,Encinal
+78021,Fowlerton
+78022,George West
+78023,Helotes
+78024,Hunt
+78025,Ingram
+78026,Jourdanton
+78027,Kendalia
+78028,Kerrville
+78029,Kerrville
+78039,La Coste
+78040,Laredo
+78041,Laredo
+78042,Laredo
+78043,Laredo
+78044,Laredo
+78045,Laredo
+78046,Laredo
+78049,Laredo
+78050,Leming
+78052,Lytle
+78053,Mc Coy
+78054,Macdona
+78055,Medina
+78056,Mico
+78057,Moore
+78058,Mountain Home
+78059,Natalia
+78060,Oakville
+78061,Pearsall
+78062,Peggy
+78063,Pipe Creek
+78064,Pleasanton
+78065,Poteet
+78066,Rio Medina
+78067,San Ygnacio
+78069,Somerset
+78070,Spring Branch
+78071,Three Rivers
+78072,Tilden
+78073,Von Ormy
+78074,Waring
+78075,Whitsett
+78076,Zapata
+78101,Adkins
+78102,Beeville
+78104,Beeville
+78107,Berclair
+78108,Cibolo
+78109,Converse
+78111,Ecleto
+78112,Elmendorf
+78113,Falls City
+78114,Floresville
+78115,Geronimo
+78116,Gillett
+78117,Hobson
+78118,Karnes City
+78119,Kenedy
+78121,La Vernia
+78122,Leesville
+78123,Mc Queeney
+78124,Marion
+78125,Mineral
+78130,New Braunfels
+78131,New Braunfels
+78132,New Braunfels
+78133,Canyon Lake
+78135,New Braunfels
+78140,Nixon
+78141,Nordheim
+78142,Normanna
+78143,Pandora
+78144,Panna Maria
+78145,Pawnee
+78146,Pettus
+78147,Poth
+78148,Universal City
+78150,Universal City
+78151,Runge
+78152,Saint Hedwig
+78154,Schertz
+78155,Seguin
+78156,Seguin
+78159,Smiley
+78160,Stockdale
+78161,Sutherland Springs
+78162,Tuleta
+78163,Bulverde
+78164,Yorktown
+78201,San Antonio
+78202,San Antonio
+78203,San Antonio
+78204,San Antonio
+78205,San Antonio
+78206,San Antonio
+78207,San Antonio
+78208,San Antonio
+78209,San Antonio
+78210,San Antonio
+78211,San Antonio
+78212,San Antonio
+78213,San Antonio
+78214,San Antonio
+78215,San Antonio
+78216,San Antonio
+78217,San Antonio
+78218,San Antonio
+78219,San Antonio
+78220,San Antonio
+78221,San Antonio
+78222,San Antonio
+78223,San Antonio
+78224,San Antonio
+78225,San Antonio
+78226,San Antonio
+78227,San Antonio
+78228,San Antonio
+78229,San Antonio
+78230,San Antonio
+78231,San Antonio
+78232,San Antonio
+78233,San Antonio
+78234,San Antonio
+78235,San Antonio
+78236,San Antonio
+78237,San Antonio
+78238,San Antonio
+78239,San Antonio
+78240,San Antonio
+78241,San Antonio
+78242,San Antonio
+78243,San Antonio
+78244,San Antonio
+78245,San Antonio
+78246,San Antonio
+78247,San Antonio
+78248,San Antonio
+78249,San Antonio
+78250,San Antonio
+78251,San Antonio
+78252,San Antonio
+78253,San Antonio
+78254,San Antonio
+78255,San Antonio
+78256,San Antonio
+78257,San Antonio
+78258,San Antonio
+78259,San Antonio
+78260,San Antonio
+78261,San Antonio
+78262,San Antonio
+78263,San Antonio
+78264,San Antonio
+78265,San Antonio
+78266,San Antonio
+78268,San Antonio
+78269,San Antonio
+78270,San Antonio
+78275,San Antonio
+78278,San Antonio
+78279,San Antonio
+78280,San Antonio
+78283,San Antonio
+78284,San Antonio
+78285,San Antonio
+78286,San Antonio
+78287,San Antonio
+78288,San Antonio
+78289,San Antonio
+78291,San Antonio
+78292,San Antonio
+78293,San Antonio
+78294,San Antonio
+78295,San Antonio
+78296,San Antonio
+78297,San Antonio
+78298,San Antonio
+78299,San Antonio
+78330,Agua Dulce
+78332,Alice
+78333,Alice
+78335,Aransas Pass
+78336,Aransas Pass
+78338,Armstrong
+78339,Banquete
+78340,Bayside
+78341,Benavides
+78342,Ben Bolt
+78343,Bishop
+78344,Bruni
+78347,Chapman Ranch
+78349,Concepcion
+78350,Dinero
+78351,Driscoll
+78352,Edroy
+78353,Encino
+78355,Falfurrias
+78357,Freer
+78358,Fulton
+78359,Gregory
+78360,Guerra
+78361,Hebbronville
+78362,Ingleside
+78363,Kingsville
+78364,Kingsville
+78368,Mathis
+78369,Mirando City
+78370,Odem
+78371,Oilton
+78372,Orange Grove
+78373,Port Aransas
+78374,Portland
+78375,Premont
+78376,Realitos
+78377,Refugio
+78379,Riviera
+78380,Robstown
+78381,Rockport
+78382,Rockport
+78383,Sandia
+78384,San Diego
+78385,Sarita
+78387,Sinton
+78389,Skidmore
+78390,Taft
+78391,Tynan
+78393,Woodsboro
+78401,Corpus Christi
+78402,Corpus Christi
+78403,Corpus Christi
+78404,Corpus Christi
+78405,Corpus Christi
+78406,Corpus Christi
+78407,Corpus Christi
+78408,Corpus Christi
+78409,Corpus Christi
+78410,Corpus Christi
+78411,Corpus Christi
+78412,Corpus Christi
+78413,Corpus Christi
+78414,Corpus Christi
+78415,Corpus Christi
+78416,Corpus Christi
+78417,Corpus Christi
+78418,Corpus Christi
+78419,Corpus Christi
+78426,Corpus Christi
+78427,Corpus Christi
+78460,Corpus Christi
+78461,Corpus Christi
+78463,Corpus Christi
+78465,Corpus Christi
+78466,Corpus Christi
+78467,Corpus Christi
+78468,Corpus Christi
+78469,Corpus Christi
+78470,Corpus Christi
+78471,Corpus Christi
+78472,Corpus Christi
+78473,Corpus Christi
+78474,Corpus Christi
+78475,Corpus Christi
+78476,Corpus Christi
+78477,Corpus Christi
+78478,Corpus Christi
+78480,Corpus Christi
+78501,McAllen
+78502,McAllen
+78503,McAllen
+78504,McAllen
+78505,McAllen
+78516,Alamo
+78520,Brownsville
+78521,Brownsville
+78522,Brownsville
+78523,Brownsville
+78526,Brownsville
+78535,Combes
+78536,Delmita
+78537,Donna
+78538,Edcouch
+78539,Edinburg
+78540,Edinburg
+78543,Elsa
+78545,Falcon Heights
+78547,Garciasville
+78548,Grulla
+78549,Hargill
+78550,Harlingen
+78551,Harlingen
+78552,Harlingen
+78553,Harlingen
+78557,Hidalgo
+78558,La Blanca
+78559,La Feria
+78560,La Joya
+78561,Lasara
+78562,La Villa
+78563,Linn
+78564,Lopeno
+78565,Los Ebanos
+78566,Los Fresnos
+78567,Los Indios
+78568,Lozano
+78569,Lyford
+78570,Mercedes
+78572,Mission
+78573,Mission
+78575,Olmito
+78576,Penitas
+78577,Pharr
+78578,Port Isabel
+78579,Progreso
+78580,Raymondville
+78582,Rio Grande City
+78583,Rio Hondo
+78584,Roma
+78585,Salineno
+78586,San Benito
+78588,San Isidro
+78589,San Juan
+78590,San Perlita
+78591,Santa Elena
+78592,Santa Maria
+78593,Santa Rosa
+78594,Sebastian
+78595,Sullivan City
+78596,Weslaco
+78597,South Padre Island
+78598,Port Mansfield
+78599,Weslaco
+78602,Bastrop
+78603,Bebe
+78604,Belmont
+78605,Bertram
+78606,Blanco
+78607,Bluffton
+78608,Briggs
+78609,Buchanan Dam
+78610,Buda
+78611,Burnet
+78612,Cedar Creek
+78613,Cedar Park
+78614,Cost
+78615,Coupland
+78616,Dale
+78617,Del Valle
+78618,Doss
+78619,Driftwood
+78620,Dripping Springs
+78621,Elgin
+78622,Fentress
+78623,Fischer
+78624,Fredericksburg
+78626,Georgetown
+78627,Georgetown
+78628,Georgetown
+78629,Gonzales
+78630,Cedar Park
+78631,Harper
+78632,Harwood
+78634,Hutto
+78635,Hye
+78636,Johnson City
+78638,Kingsbury
+78639,Kingsland
+78640,Kyle
+78641,Leander
+78642,Liberty Hill
+78643,Llano
+78644,Lockhart
+78645,Leander
+78646,Leander
+78648,Luling
+78650,Mc Dade
+78651,Mc Neil
+78652,Manchaca
+78653,Manor
+78654,Marble Falls
+78655,Martindale
+78656,Maxwell
+78657,Marble Falls
+78658,Ottine
+78659,Paige
+78660,Pflugerville
+78661,Prairie Lea
+78662,Red Rock
+78663,Round Mountain
+78664,Round Rock
+78665,Sandy
+78666,San Marcos
+78667,San Marcos
+78669,Spicewood
+78670,Staples
+78671,Stonewall
+78672,Tow
+78673,Walburg
+78674,Weir
+78675,Willow City
+78676,Wimberley
+78677,Wrightsboro
+78680,Round Rock
+78681,Round Rock
+78682,Round Rock
+78683,Round Rock
+78691,Pflugerville
+78701,Austin
+78702,Austin
+78703,Austin
+78704,Austin
+78705,Austin
+78708,Austin
+78709,Austin
+78710,Austin
+78711,Austin
+78712,Austin
+78713,Austin
+78714,Austin
+78715,Austin
+78716,Austin
+78717,Austin
+78718,Austin
+78719,Austin
+78720,Austin
+78721,Austin
+78722,Austin
+78723,Austin
+78724,Austin
+78725,Austin
+78726,Austin
+78727,Austin
+78728,Austin
+78729,Austin
+78730,Austin
+78731,Austin
+78732,Austin
+78733,Austin
+78734,Austin
+78735,Austin
+78736,Austin
+78737,Austin
+78738,Austin
+78739,Austin
+78741,Austin
+78742,Austin
+78744,Austin
+78745,Austin
+78746,Austin
+78747,Austin
+78748,Austin
+78749,Austin
+78750,Austin
+78751,Austin
+78752,Austin
+78753,Austin
+78754,Austin
+78755,Austin
+78756,Austin
+78757,Austin
+78758,Austin
+78759,Austin
+78760,Austin
+78761,Austin
+78762,Austin
+78763,Austin
+78764,Austin
+78765,Austin
+78766,Austin
+78767,Austin
+78768,Austin
+78769,Austin
+78771,Austin
+78772,Austin
+78773,Austin
+78774,Austin
+78778,Austin
+78779,Austin
+78780,Austin
+78781,Austin
+78782,Austin
+78783,Austin
+78785,Austin
+78786,Austin
+78787,Austin
+78788,Austin
+78789,Austin
+78801,Uvalde
+78802,Uvalde
+78827,Asherton
+78828,Barksdale
+78829,Batesville
+78830,Big Wells
+78832,Brackettville
+78833,Camp Wood
+78834,Carrizo Springs
+78836,Catarina
+78837,Comstock
+78838,Concan
+78839,Crystal City
+78840,Del Rio
+78841,Del Rio
+78842,Del Rio
+78843,Laughlin A F B
+78847,Del Rio
+78850,D Hanis
+78851,Dryden
+78852,Eagle Pass
+78853,Eagle Pass
+78860,El Indio
+78861,Hondo
+78870,Knippa
+78871,Langtry
+78872,La Pryor
+78873,Leakey
+78877,Quemado
+78879,Rio Frio
+78880,Rocksprings
+78881,Sabinal
+78883,Tarpley
+78884,Utopia
+78885,Vanderpool
+78886,Yancey
+78931,Bleiblerville
+78932,Carmine
+78933,Cat Spring
+78934,Columbus
+78935,Alleyton
+78938,Ellinger
+78940,Fayetteville
+78941,Flatonia
+78942,Giddings
+78943,Glidden
+78944,Industry
+78945,La Grange
+78946,Ledbetter
+78947,Lexington
+78948,Lincoln
+78949,Muldoon
+78950,New Ulm
+78951,Oakland
+78952,Plum
+78953,Rosanky
+78954,Round Top
+78956,Schulenburg
+78957,Smithville
+78959,Waelder
+78960,Warda
+78961,Round Top
+78962,Weimar
+78963,West Point
+79001,Adrian
+79002,Alanreed
+79003,Allison
+79005,Booker
+79007,Borger
+79008,Borger
+79009,Bovina
+79010,Boys Ranch
+79011,Briscoe
+79012,Bushland
+79013,Cactus
+79014,Canadian
+79015,Canyon
+79016,Canyon
+79018,Channing
+79019,Claude
+79021,Cotton Center
+79022,Dalhart
+79024,Darrouzett
+79025,Dawn
+79027,Dimmitt
+79029,Dumas
+79031,Earth
+79032,Edmonson
+79033,Farnsworth
+79034,Follett
+79035,Friona
+79036,Fritch
+79039,Groom
+79040,Gruver
+79041,Hale Center
+79042,Happy
+79043,Hart
+79044,Hartley
+79045,Hereford
+79046,Higgins
+79051,Kerrick
+79052,Kress
+79053,Lazbuddie
+79054,Lefors
+79056,Lipscomb
+79057,McLean
+79058,Masterson
+79059,Miami
+79061,Mobeetie
+79062,Morse
+79063,Nazareth
+79064,Olton
+79065,Pampa
+79066,Pampa
+79068,Panhandle
+79070,Perryton
+79072,Plainview
+79073,Plainview
+79077,Samnorwood
+79078,Sanford
+79079,Shamrock
+79080,Skellytown
+79081,Spearman
+79082,Springlake
+79083,Stinnett
+79084,Stratford
+79085,Summerfield
+79086,Sunray
+79087,Texline
+79088,Tulia
+79091,Umbarger
+79092,Vega
+79093,Waka
+79094,Wayside
+79095,Wellington
+79096,Wheeler
+79097,White Deer
+79098,Wildorado
+79101,Amarillo
+79102,Amarillo
+79103,Amarillo
+79104,Amarillo
+79105,Amarillo
+79106,Amarillo
+79107,Amarillo
+79108,Amarillo
+79109,Amarillo
+79110,Amarillo
+79111,Amarillo
+79114,Amarillo
+79116,Amarillo
+79117,Amarillo
+79118,Amarillo
+79119,Amarillo
+79120,Amarillo
+79121,Amarillo
+79123,Amarillo
+79124,Amarillo
+79159,Amarillo
+79160,Amarillo
+79163,Amarillo
+79164,Amarillo
+79165,Amarillo
+79166,Amarillo
+79167,Amarillo
+79168,Amarillo
+79170,Amarillo
+79171,Amarillo
+79172,Amarillo
+79174,Amarillo
+79175,Amarillo
+79178,Amarillo
+79180,Amarillo
+79181,Amarillo
+79182,Amarillo
+79184,Amarillo
+79185,Amarillo
+79186,Amarillo
+79187,Amarillo
+79189,Amarillo
+79201,Childress
+79220,Afton
+79221,Aiken
+79222,Carey
+79223,Cee Vee
+79224,Chalk
+79225,Chillicothe
+79226,Clarendon
+79227,Crowell
+79229,Dickens
+79230,Dodson
+79231,Dougherty
+79232,Dumont
+79233,Estelline
+79234,Flomot
+79235,Floydada
+79236,Guthrie
+79237,Hedley
+79238,Kirkland
+79239,Lakeview
+79240,Lelia Lake
+79241,Lockney
+79243,McAdoo
+79244,Matador
+79245,Memphis
+79247,Odell
+79248,Paducah
+79250,Petersburg
+79251,Quail
+79252,Quanah
+79255,Quitaque
+79256,Roaring Springs
+79257,Silverton
+79258,South Plains
+79259,Tell
+79261,Turkey
+79311,Abernathy
+79312,Amherst
+79313,Anton
+79314,Bledsoe
+79316,Brownfield
+79320,Bula
+79322,Crosbyton
+79323,Denver City
+79324,Enochs
+79325,Farwell
+79326,Fieldton
+79329,Idalou
+79330,Justiceburg
+79331,Lamesa
+79336,Levelland
+79338,Levelland
+79339,Littlefield
+79342,Loop
+79343,Lorenzo
+79344,Maple
+79345,Meadow
+79346,Morton
+79347,Muleshoe
+79350,New Deal
+79351,Odonnell
+79353,Pep
+79355,Plains
+79356,Post
+79357,Ralls
+79358,Ropesville
+79359,Seagraves
+79360,Seminole
+79363,Shallowater
+79364,Slaton
+79366,Ransom Canyon
+79367,Smyer
+79369,Spade
+79370,Spur
+79371,Sudan
+79372,Sundown
+79373,Tahoka
+79376,Tokio
+79377,Welch
+79378,Wellman
+79379,Whiteface
+79380,Whitharral
+79381,Wilson
+79382,Wolfforth
+79383,New Home
+79401,Lubbock
+79402,Lubbock
+79403,Lubbock
+79404,Lubbock
+79405,Lubbock
+79406,Lubbock
+79407,Lubbock
+79408,Lubbock
+79409,Lubbock
+79410,Lubbock
+79411,Lubbock
+79412,Lubbock
+79413,Lubbock
+79414,Lubbock
+79415,Lubbock
+79416,Lubbock
+79423,Lubbock
+79424,Lubbock
+79430,Lubbock
+79452,Lubbock
+79453,Lubbock
+79457,Lubbock
+79464,Lubbock
+79490,Lubbock
+79491,Lubbock
+79493,Lubbock
+79499,Lubbock
+79501,Anson
+79502,Aspermont
+79503,Avoca
+79504,Baird
+79505,Benjamin
+79506,Blackwell
+79508,Buffalo Gap
+79510,Clyde
+79511,Coahoma
+79512,Colorado City
+79516,Dunn
+79517,Fluvanna
+79518,Girard
+79519,Goldsboro
+79520,Hamlin
+79521,Haskell
+79525,Hawley
+79526,Hermleigh
+79527,Ira
+79528,Jayton
+79529,Knox City
+79530,Lawn
+79532,Loraine
+79533,Lueders
+79534,Mc Caulley
+79535,Maryneal
+79536,Merkel
+79537,Nolan
+79538,Novice
+79539,O Brien
+79540,Old Glory
+79541,Ovalo
+79543,Roby
+79544,Rochester
+79545,Roscoe
+79546,Rotan
+79547,Rule
+79548,Rule
+79549,Snyder
+79550,Snyder
+79553,Stamford
+79556,Sweetwater
+79560,Sylvester
+79561,Trent
+79562,Tuscola
+79563,Tye
+79565,Westbrook
+79566,Wingate
+79567,Winters
+79601,Abilene
+79602,Abilene
+79603,Abilene
+79604,Abilene
+79605,Abilene
+79606,Abilene
+79607,Dyess AFB
+79608,Abilene
+79697,Abilene
+79698,Abilene
+79699,Abilene
+79701,Midland
+79702,Midland
+79703,Midland
+79704,Midland
+79705,Midland
+79706,Midland
+79707,Midland
+79708,Midland
+79710,Midland
+79711,Midland
+79712,Midland
+79713,Ackerly
+79714,Andrews
+79718,Balmorhea
+79719,Barstow
+79720,Big Spring
+79721,Big Spring
+79730,Coyanosa
+79731,Crane
+79733,Forsan
+79734,Fort Davis
+79735,Fort Stockton
+79738,Gail
+79739,Garden City
+79740,Girvin
+79741,Goldsmith
+79742,Grandfalls
+79743,Imperial
+79744,Iraan
+79745,Kermit
+79748,Knott
+79749,Lenorah
+79752,Mc Camey
+79754,Mentone
+79755,Midkiff
+79756,Monahans
+79758,Gardendale
+79759,Notrees
+79760,Odessa
+79761,Odessa
+79762,Odessa
+79763,Odessa
+79764,Odessa
+79765,Odessa
+79766,Odessa
+79768,Odessa
+79769,Odessa
+79770,Orla
+79772,Pecos
+79776,Penwell
+79777,Pyote
+79778,Rankin
+79779,Royalty
+79780,Saragosa
+79781,Sheffield
+79782,Stanton
+79783,Tarzan
+79785,Toyah
+79786,Toyahvale
+79788,Wickett
+79789,Wink
+79821,Anthony
+79830,Alpine
+79831,Alpine
+79832,Alpine
+79834,Big Bend National Park
+79835,Canutillo
+79836,Clint
+79837,Dell City
+79838,Fabens
+79839,Fort Hancock
+79842,Marathon
+79843,Marfa
+79845,Presidio
+79846,Redford
+79847,Salt Flat
+79848,Sanderson
+79849,San Elizario
+79850,Shafter
+79851,Sierra Blanca
+79852,Terlingua
+79853,Tornillo
+79854,Valentine
+79855,Van Horn
+79901,El Paso
+79902,El Paso
+79903,El Paso
+79904,El Paso
+79905,El Paso
+79906,El Paso
+79907,El Paso
+79908,El Paso
+79910,El Paso
+79911,El Paso
+79912,El Paso
+79913,El Paso
+79914,El Paso
+79915,El Paso
+79916,El Paso
+79917,El Paso
+79918,El Paso
+79920,El Paso
+79922,El Paso
+79923,El Paso
+79924,El Paso
+79925,El Paso
+79926,El Paso
+79927,El Paso
+79929,El Paso
+79930,El Paso
+79931,El Paso
+79932,El Paso
+79934,El Paso
+79935,El Paso
+79936,El Paso
+79937,El Paso
+79938,El Paso
+79940,El Paso
+79941,El Paso
+79942,El Paso
+79943,El Paso
+79944,El Paso
+79945,El Paso
+79946,El Paso
+79947,El Paso
+79948,El Paso
+79949,El Paso
+79950,El Paso
+79951,El Paso
+79952,El Paso
+79953,El Paso
+79954,El Paso
+79955,El Paso
+79958,El Paso
+79960,El Paso
+79961,El Paso
+79966,El Paso
+79968,El Paso
+79973,El Paso
+79974,El Paso
+79975,El Paso
+79976,El Paso
+79977,El Paso
+79978,El Paso
+79980,El Paso
+79982,El Paso
+79983,El Paso
+79984,El Paso
+79985,El Paso
+79986,El Paso
+79987,El Paso
+79988,El Paso
+79989,El Paso
+79990,El Paso
+79991,El Paso
+79992,El Paso
+79993,El Paso
+79994,El Paso
+79995,El Paso
+79996,El Paso
+79997,El Paso
+79998,El Paso
+79999,El Paso
+88510,El Paso
+88511,El Paso
+88512,El Paso
+88513,El Paso
+88514,El Paso
+88515,El Paso
+88516,El Paso
+88517,El Paso
+88518,El Paso
+88519,El Paso
+88520,El Paso
+88521,El Paso
+88523,El Paso
+88524,El Paso
+88525,El Paso
+88526,El Paso
+88527,El Paso
+88528,El Paso
+88529,El Paso
+88530,El Paso
+88531,El Paso
+88532,El Paso
+88533,El Paso
+88534,El Paso
+88535,El Paso
+88536,El Paso
+88538,El Paso
+88539,El Paso
+88540,El Paso
+88541,El Paso
+88542,El Paso
+88543,El Paso
+88544,El Paso
+88545,El Paso
+88546,El Paso
+88547,El Paso
+88548,El Paso
+88549,El Paso
+88550,El Paso
+88553,El Paso
+88554,El Paso
+88555,El Paso
+88556,El Paso
+88557,El Paso
+88558,El Paso
+88559,El Paso
+88560,El Paso
+88561,El Paso
+88562,El Paso
+88563,El Paso
+88565,El Paso
+88566,El Paso
+88567,El Paso
+88568,El Paso
+88569,El Paso
+88570,El Paso
+88571,El Paso
+88572,El Paso
+88573,El Paso
+88574,El Paso
+88575,El Paso
+88576,El Paso
+88577,El Paso
+88578,El Paso
+88579,El Paso
+88580,El Paso
+88581,El Paso
+88582,El Paso
+88583,El Paso
+88584,El Paso
+88585,El Paso
+88586,El Paso
+88587,El Paso
+88588,El Paso
+88589,El Paso
+88590,El Paso
+88595,El Paso
+37010,Adams
+37011,Antioch
+37012,Alexandria
+37013,Antioch
+37014,Arrington
+37015,Ashland City
+37016,Auburntown
+37018,Beechgrove
+37019,Belfast
+37020,Bell Buckle
+37022,Bethpage
+37023,Big Rock
+37024,Brentwood
+37025,Bon Aqua
+37026,Bradyville
+37027,Brentwood
+37028,Bumpus Mills
+37029,Burns
+37030,Carthage
+37031,Castalian Springs
+37032,Cedar Hill
+37033,Centerville
+37034,Chapel Hill
+37035,Chapmansboro
+37036,Charlotte
+37037,Christiana
+37040,Clarksville
+37041,Clarksville
+37042,Clarksville
+37043,Clarksville
+37044,Clarksville
+37046,College Grove
+37047,Cornersville
+37048,Cottontown
+37049,Cross Plains
+37050,Cumberland City
+37051,Cumberland Furnace
+37052,Cunningham
+37055,Dickson
+37056,Dickson
+37057,Dixon Springs
+37058,Dover
+37059,Dowelltown
+37060,Eagleville
+37061,Erin
+37062,Fairview
+37063,Fosterville
+37064,Franklin
+37065,Franklin
+37066,Gallatin
+37067,Franklin
+37068,Franklin
+37069,Franklin
+37070,Goodlettsville
+37071,Gladeville
+37072,Goodlettsville
+37073,Greenbrier
+37074,Hartsville
+37075,Hendersonville
+37076,Hermitage
+37077,Hendersonville
+37078,Hurricane Mills
+37079,Indian Mound
+37080,Joelton
+37082,Kingston Springs
+37083,Lafayette
+37085,Lascassas
+37086,La Vergne
+37087,Lebanon
+37088,Lebanon
+37089,La Vergne
+37090,Lebanon
+37091,Lewisburg
+37095,Liberty
+37096,Linden
+37097,Lobelville
+37098,Lyles
+37101,Mc Ewen
+37110,Mc Minnville
+37111,Mc Minnville
+37115,Madison
+37116,Madison
+37118,Milton
+37119,Mitchellville
+37121,Mount Juliet
+37122,Mount Juliet
+37127,Murfreesboro
+37128,Murfreesboro
+37129,Murfreesboro
+37130,Murfreesboro
+37131,Murfreesboro
+37132,Murfreesboro
+37133,Murfreesboro
+37134,New Johnsonville
+37135,Nolensville
+37136,Norene
+37137,Nunnelly
+37138,Old Hickory
+37140,Only
+37141,Orlinda
+37142,Palmyra
+37143,Pegram
+37144,Petersburg
+37145,Pleasant Shade
+37146,Pleasant View
+37147,Pleasantville
+37148,Portland
+37149,Readyville
+37150,Red Boiling Springs
+37151,Riddleton
+37152,Ridgetop
+37153,Rockvale
+37155,Saint Bethlehem
+37160,Shelbyville
+37161,Shelbyville
+37162,Shelbyville
+37165,Slayden
+37166,Smithville
+37167,Smyrna
+37171,Southside
+37172,Springfield
+37174,Spring Hill
+37175,Stewart
+37178,Tennessee Ridge
+37179,Thompsons Station
+37180,Unionville
+37181,Vanleer
+37183,Wartrace
+37184,Watertown
+37185,Waverly
+37186,Westmoreland
+37187,White Bluff
+37188,White House
+37189,Whites Creek
+37190,Woodbury
+37191,Woodlawn
+37201,Nashville
+37202,Nashville
+37203,Nashville
+37204,Nashville
+37205,Nashville
+37206,Nashville
+37207,Nashville
+37208,Nashville
+37209,Nashville
+37210,Nashville
+37211,Nashville
+37212,Nashville
+37213,Nashville
+37214,Nashville
+37215,Nashville
+37216,Nashville
+37217,Nashville
+37218,Nashville
+37219,Nashville
+37220,Nashville
+37221,Nashville
+37222,Nashville
+37224,Nashville
+37227,Nashville
+37228,Nashville
+37229,Nashville
+37230,Nashville
+37232,Nashville
+37234,Nashville
+37235,Nashville
+37236,Nashville
+37237,Nashville
+37238,Nashville
+37239,Nashville
+37240,Nashville
+37241,Nashville
+37242,Nashville
+37243,Nashville
+37244,Nashville
+37245,Nashville
+37246,Nashville
+37247,Nashville
+37248,Nashville
+37249,Nashville
+37250,Nashville
+37301,Altamont
+37302,Apison
+37303,Athens
+37304,Bakewell
+37305,Beersheba Springs
+37306,Belvidere
+37307,Benton
+37308,Birchwood
+37309,Calhoun
+37310,Charleston
+37311,Cleveland
+37312,Cleveland
+37313,Coalmont
+37314,Cokercreek
+37315,Collegedale
+37316,Conasauga
+37317,Copperhill
+37318,Cowan
+37320,Cleveland
+37321,Dayton
+37322,Decatur
+37323,Cleveland
+37324,Decherd
+37325,Delano
+37326,Ducktown
+37327,Dunlap
+37328,Elora
+37329,Englewood
+37330,Estill Springs
+37331,Etowah
+37332,Evensville
+37333,Farner
+37334,Fayetteville
+37335,Flintville
+37336,Georgetown
+37337,Grandview
+37338,Graysville
+37339,Gruetli Laager
+37340,Guild
+37341,Harrison
+37342,Hillsboro
+37343,Hixson
+37345,Huntland
+37347,Jasper
+37348,Kelso
+37349,Manchester
+37350,Lookout Mountain
+37351,Lupton City
+37352,Lynchburg
+37353,Mc Donald
+37354,Madisonville
+37355,Manchester
+37356,Monteagle
+37357,Morrison
+37359,Mulberry
+37360,Normandy
+37361,Ocoee
+37362,Oldfort
+37363,Ooltewah
+37364,Cleveland
+37365,Palmer
+37366,Pelham
+37367,Pikeville
+37369,Reliance
+37370,Riceville
+37371,Athens
+37372,Saint Andrews
+37373,Sale Creek
+37374,Sequatchie
+37375,Sewanee
+37376,Sherwood
+37377,Signal Mountain
+37378,Smartt
+37379,Soddy Daisy
+37380,South Pittsburg
+37381,Spring City
+37382,Summitville
+37383,Sewanee
+37384,Soddy Daisy
+37385,Tellico Plains
+37387,Tracy City
+37388,Tullahoma
+37389,Arnold AFB
+37391,Turtletown
+37394,Viola
+37395,Watts Bar Dam
+37396,Whiteside
+37397,Whitwell
+37398,Winchester
+37401,Chattanooga
+37402,Chattanooga
+37403,Chattanooga
+37404,Chattanooga
+37405,Chattanooga
+37406,Chattanooga
+37407,Chattanooga
+37408,Chattanooga
+37409,Chattanooga
+37410,Chattanooga
+37411,Chattanooga
+37412,Chattanooga
+37414,Chattanooga
+37415,Chattanooga
+37416,Chattanooga
+37419,Chattanooga
+37421,Chattanooga
+37422,Chattanooga
+37424,Chattanooga
+37450,Chattanooga
+37501,Memphis
+37601,Johnson City
+37602,Johnson City
+37604,Johnson City
+37605,Johnson City
+37614,Johnson City
+37615,Johnson City
+37616,Afton
+37617,Blountville
+37618,Bluff City
+37620,Bristol
+37621,Bristol
+37625,Bristol
+37640,Butler
+37641,Chuckey
+37642,Church Hill
+37643,Elizabethton
+37644,Elizabethton
+37645,Mount Carmel
+37650,Erwin
+37656,Fall Branch
+37657,Flag Pond
+37658,Hampton
+37659,Jonesborough
+37660,Kingsport
+37662,Kingsport
+37663,Kingsport
+37664,Kingsport
+37665,Kingsport
+37669,Kingsport
+37680,Laurel Bloomery
+37681,Limestone
+37682,Milligan College
+37683,Mountain City
+37684,Mountain Home
+37686,Piney Flats
+37687,Roan Mountain
+37688,Shady Valley
+37690,Telford
+37691,Trade
+37692,Unicoi
+37694,Watauga
+37699,Piney Flats
+37701,Alcoa
+37705,Andersonville
+37707,Arthur
+37708,Bean Station
+37709,Blaine
+37710,Briceville
+37711,Bulls Gap
+37713,Bybee
+37714,Caryville
+37715,Clairfield
+37716,Clinton
+37717,Clinton
+37719,Coalfield
+37721,Corryton
+37722,Cosby
+37723,Crab Orchard
+37724,Cumberland Gap
+37725,Dandridge
+37726,Deer Lodge
+37727,Del Rio
+37729,Duff
+37730,Eagan
+37731,Eidson
+37732,Elgin
+37733,Rugby
+37737,Friendsville
+37738,Gatlinburg
+37742,Greenback
+37743,Greeneville
+37744,Greeneville
+37745,Greeneville
+37748,Harriman
+37752,Harrogate
+37753,Hartford
+37754,Heiskell
+37755,Helenwood
+37756,Huntsville
+37757,Jacksboro
+37760,Jefferson City
+37762,Jellico
+37763,Kingston
+37764,Kodak
+37765,Kyles Ford
+37766,La Follette
+37769,Lake City
+37770,Lancing
+37771,Lenoir City
+37772,Lenoir City
+37773,Lone Mountain
+37774,Loudon
+37777,Louisville
+37778,Lowland
+37779,Luttrell
+37801,Maryville
+37802,Maryville
+37803,Maryville
+37804,Maryville
+37806,Mascot
+37807,Maynardville
+37809,Midway
+37810,Mohawk
+37811,Mooresburg
+37813,Morristown
+37814,Morristown
+37815,Morristown
+37816,Morristown
+37818,Mosheim
+37819,Newcomb
+37820,New Market
+37821,Newport
+37822,Newport
+37824,New Tazewell
+37825,New Tazewell
+37826,Niota
+37828,Norris
+37829,Oakdale
+37830,Oak Ridge
+37831,Oak Ridge
+37840,Oliver Springs
+37841,Oneida
+37842,Ozone
+37843,Parrottsville
+37845,Petros
+37846,Philadelphia
+37847,Pioneer
+37848,Powder Springs
+37849,Powell
+37851,Pruden
+37852,Robbins
+37853,Rockford
+37854,Rockwood
+37857,Rogersville
+37860,Russellville
+37861,Rutledge
+37862,Sevierville
+37863,Pigeon Forge
+37864,Sevierville
+37865,Seymour
+37866,Sharps Chapel
+37867,Shawanee
+37868,Pigeon Forge
+37869,Sneedville
+37870,Speedwell
+37871,Strawberry Plains
+37872,Sunbright
+37873,Surgoinsville
+37874,Sweetwater
+37876,Sevierville
+37877,Talbott
+37878,Tallassee
+37879,Tazewell
+37880,Ten Mile
+37881,Thorn Hill
+37882,Townsend
+37885,Vonore
+37886,Walland
+37887,Wartburg
+37888,Washburn
+37890,White Pine
+37891,Whitesburg
+37892,Winfield
+37893,Winona
+37901,Knoxville
+37902,Knoxville
+37909,Knoxville
+37912,Knoxville
+37914,Knoxville
+37915,Knoxville
+37916,Knoxville
+37917,Knoxville
+37918,Knoxville
+37919,Knoxville
+37920,Knoxville
+37921,Knoxville
+37922,Knoxville
+37923,Knoxville
+37924,Knoxville
+37927,Knoxville
+37928,Knoxville
+37929,Knoxville
+37930,Knoxville
+37931,Knoxville
+37932,Knoxville
+37933,Knoxville
+37938,Knoxville
+37939,Knoxville
+37940,Knoxville
+37950,Knoxville
+37990,Knoxville
+37995,Knoxville
+37996,Knoxville
+37997,Knoxville
+37998,Knoxville
+38001,Alamo
+38002,Arlington
+38004,Atoka
+38006,Bells
+38007,Bogota
+38008,Bolivar
+38010,Braden
+38011,Brighton
+38012,Brownsville
+38014,Brunswick
+38015,Burlison
+38017,Collierville
+38018,Cordova
+38019,Covington
+38021,Crockett Mills
+38023,Drummonds
+38024,Dyersburg
+38025,Dyersburg
+38027,Collierville
+38028,Eads
+38029,Ellendale
+38030,Finley
+38034,Friendship
+38036,Gallaway
+38037,Gates
+38039,Grand Junction
+38040,Halls
+38041,Henning
+38042,Hickory Valley
+38043,Hickory Withe
+38044,Hornsby
+38045,Laconia
+38046,La Grange
+38047,Lenox
+38048,Macon
+38049,Mason
+38050,Maury City
+38052,Middleton
+38053,Millington
+38054,Millington
+38055,Millington
+38056,Miston
+38057,Moscow
+38058,Munford
+38059,Newbern
+38060,Oakland
+38061,Pocahontas
+38063,Ripley
+38066,Rossville
+38067,Saulsbury
+38068,Somerville
+38069,Stanton
+38070,Tigrett
+38071,Tipton
+38074,Bolivar
+38075,Whiteville
+38076,Williston
+38077,Wynnburg
+38079,Tiptonville
+38080,Ridgely
+38083,Millington
+38088,Cordova
+38101,Memphis
+38103,Memphis
+38104,Memphis
+38105,Memphis
+38106,Memphis
+38107,Memphis
+38108,Memphis
+38109,Memphis
+38110,Memphis
+38111,Memphis
+38112,Memphis
+38113,Memphis
+38114,Memphis
+38115,Memphis
+38116,Memphis
+38117,Memphis
+38118,Memphis
+38119,Memphis
+38120,Memphis
+38122,Memphis
+38124,Memphis
+38125,Memphis
+38126,Memphis
+38127,Memphis
+38128,Memphis
+38130,Memphis
+38131,Memphis
+38132,Memphis
+38133,Memphis
+38134,Memphis
+38135,Memphis
+38136,Memphis
+38137,Memphis
+38138,Germantown
+38139,Germantown
+38140,Memphis
+38141,Memphis
+38142,Memphis
+38143,Memphis
+38145,Memphis
+38146,Memphis
+38147,Memphis
+38148,Memphis
+38150,Memphis
+38151,Memphis
+38152,Memphis
+38157,Memphis
+38159,Memphis
+38161,Memphis
+38163,Memphis
+38165,Memphis
+38166,Memphis
+38167,Memphis
+38168,Memphis
+38173,Memphis
+38174,Memphis
+38175,Memphis
+38177,Memphis
+38181,Memphis
+38182,Memphis
+38183,Germantown
+38184,Memphis
+38186,Memphis
+38187,Memphis
+38188,Memphis
+38190,Memphis
+38193,Memphis
+38194,Memphis
+38195,Memphis
+38197,Memphis
+38201,Mc Kenzie
+38220,Atwood
+38221,Big Sandy
+38222,Buchanan
+38223,Como
+38224,Cottage Grove
+38225,Dresden
+38226,Dukedom
+38229,Gleason
+38230,Greenfield
+38231,Henry
+38232,Hornbeak
+38233,Kenton
+38235,Mc Lemoresville
+38236,Mansfield
+38237,Martin
+38238,Martin
+38240,Obion
+38241,Palmersville
+38242,Paris
+38251,Puryear
+38253,Rives
+38254,Samburg
+38255,Sharon
+38256,Springville
+38257,South Fulton
+38258,Trezevant
+38259,Trimble
+38260,Troy
+38261,Union City
+38271,Woodland Mills
+38281,Union City
+38301,Jackson
+38302,Jackson
+38303,Jackson
+38305,Jackson
+38308,Jackson
+38310,Adamsville
+38311,Bath Springs
+38313,Beech Bluff
+38314,Jackson
+38315,Bethel Springs
+38316,Bradford
+38317,Bruceton
+38318,Buena Vista
+38320,Camden
+38321,Cedar Grove
+38324,Clarksburg
+38326,Counce
+38327,Crump
+38328,Darden
+38329,Decaturville
+38330,Dyer
+38331,Eaton
+38332,Enville
+38333,Eva
+38334,Finger
+38336,Fruitvale
+38337,Gadsden
+38338,Gibson
+38339,Guys
+38340,Henderson
+38341,Holladay
+38342,Hollow Rock
+38343,Humboldt
+38344,Huntingdon
+38345,Huron
+38346,Idlewild
+38347,Jacks Creek
+38348,Lavinia
+38351,Lexington
+38352,Luray
+38355,Medina
+38356,Medon
+38357,Michie
+38358,Milan
+38359,Milledgeville
+38361,Morris Chapel
+38362,Oakfield
+38363,Parsons
+38365,Pickwick Dam
+38366,Pinson
+38367,Ramer
+38368,Reagan
+38369,Rutherford
+38370,Saltillo
+38371,Sardis
+38372,Savannah
+38374,Scotts Hill
+38375,Selmer
+38376,Shiloh
+38377,Silerton
+38378,Spring Creek
+38379,Stantonville
+38380,Sugar Tree
+38381,Toone
+38382,Trenton
+38387,Westport
+38388,Wildersville
+38389,Yorkville
+38390,Yuma
+38391,Denmark
+38392,Mercer
+38393,Chewalla
+38401,Columbia
+38402,Columbia
+38425,Clifton
+38449,Ardmore
+38450,Collinwood
+38451,Culleoka
+38452,Cypress Inn
+38453,Dellrose
+38454,Duck River
+38455,Elkton
+38456,Ethridge
+38457,Five Points
+38459,Frankewing
+38460,Goodspring
+38461,Hampshire
+38462,Hohenwald
+38463,Iron City
+38464,Lawrenceburg
+38468,Leoma
+38469,Loretto
+38471,Lutts
+38472,Lynnville
+38473,Minor Hill
+38474,Mount Pleasant
+38475,Olivehill
+38476,Primm Springs
+38477,Prospect
+38478,Pulaski
+38481,Saint Joseph
+38482,Santa FE
+38483,Summertown
+38485,Waynesboro
+38486,Westpoint
+38487,Williamsport
+38488,Taft
+38501,Cookeville
+38502,Cookeville
+38503,Cookeville
+38504,Allardt
+38505,Cookeville
+38506,Cookeville
+38541,Allons
+38542,Allred
+38543,Alpine
+38544,Baxter
+38545,Bloomington Springs
+38547,Brush Creek
+38548,Buffalo Valley
+38549,Byrdstown
+38550,Campaign
+38551,Celina
+38552,Chestnut Mound
+38553,Clarkrange
+38554,Crawford
+38555,Crossville
+38556,Jamestown
+38557,Crossville
+38558,Crossville
+38559,Doyle
+38560,Elmwood
+38562,Gainesboro
+38563,Gordonsville
+38564,Granville
+38565,Grimsley
+38567,Hickman
+38568,Hilham
+38569,Lancaster
+38570,Livingston
+38573,Monroe
+38574,Monterey
+38575,Moss
+38577,Pall Mall
+38578,Pleasant Hill
+38579,Quebeck
+38580,Rickman
+38581,Rock Island
+38582,Silver Point
+38583,Sparta
+38585,Spencer
+38587,Walling
+38588,Whitleyville
+38589,Wilder
+57001,Alcester
+57002,Aurora
+57003,Baltic
+57004,Beresford
+57005,Brandon
+57006,Brookings
+57007,Brookings
+57010,Burbank
+57012,Canistota
+57013,Canton
+57014,Centerville
+57015,Chancellor
+57016,Chester
+57017,Colman
+57018,Colton
+57020,Crooks
+57021,Davis
+57022,Dell Rapids
+57024,Egan
+57025,Elk Point
+57026,Elkton
+57027,Fairview
+57028,Flandreau
+57029,Freeman
+57030,Garretson
+57031,Gayville
+57032,Harrisburg
+57033,Hartford
+57034,Hudson
+57035,Humboldt
+57036,Hurley
+57037,Irene
+57038,Jefferson
+57039,Lennox
+57040,Lesterville
+57041,Lyons
+57042,Madison
+57043,Marion
+57044,Meckling
+57045,Menno
+57046,Mission Hill
+57047,Monroe
+57048,Montrose
+57049,North Sioux City
+57050,Nunda
+57051,Oldham
+57052,Olivet
+57053,Parker
+57054,Ramona
+57055,Renner
+57056,Rowena
+57057,Rutland
+57058,Salem
+57059,Scotland
+57061,Sinai
+57062,Springfield
+57063,Tabor
+57064,Tea
+57065,Trent
+57066,Tyndall
+57067,Utica
+57068,Valley Springs
+57069,Vermillion
+57070,Viborg
+57071,Volga
+57072,Volin
+57073,Wakonda
+57074,Ward
+57075,Wentworth
+57076,Winfred
+57077,Worthing
+57078,Yankton
+57079,Yankton
+57101,Sioux Falls
+57103,Sioux Falls
+57104,Sioux Falls
+57105,Sioux Falls
+57106,Sioux Falls
+57107,Sioux Falls
+57108,Sioux Falls
+57109,Sioux Falls
+57110,Sioux Falls
+57115,Buffalo Ridge
+57117,Sioux Falls
+57118,Sioux Falls
+57188,Sioux Falls
+57189,Sioux Falls
+57192,Sioux Falls
+57193,Sioux Falls
+57194,Sioux Falls
+57195,Sioux Falls
+57196,Sioux Falls
+57197,Sioux Falls
+57198,Sioux Falls
+57201,Watertown
+57202,Waverly
+57212,Arlington
+57213,Astoria
+57214,Badger
+57216,Big Stone City
+57217,Bradley
+57218,Brandt
+57219,Bristol
+57220,Bruce
+57221,Bryant
+57223,Castlewood
+57224,Claire City
+57225,Clark
+57226,Clear Lake
+57227,Corona
+57231,De Smet
+57232,Eden
+57233,Erwin
+57234,Estelline
+57235,Florence
+57236,Garden City
+57237,Gary
+57238,Goodwin
+57239,Grenville
+57241,Hayti
+57242,Hazel
+57243,Henry
+57244,Hetland
+57245,Kranzburg
+57246,Labolt
+57247,Lake City
+57248,Lake Norden
+57249,Lake Preston
+57251,Marvin
+57252,Milbank
+57253,Milbank
+57255,New Effington
+57256,Ortley
+57257,Peever
+57258,Raymond
+57259,Revillo
+57260,Rosholt
+57261,Roslyn
+57262,Sisseton
+57263,South Shore
+57264,Stockholm
+57265,Strandburg
+57266,Summit
+57268,Toronto
+57269,Twin Brooks
+57270,Veblen
+57271,Vienna
+57272,Wallace
+57273,Waubay
+57274,Webster
+57276,White
+57278,Willow Lake
+57279,Wilmot
+57301,Mitchell
+57311,Alexandria
+57312,Alpena
+57313,Armour
+57314,Artesian
+57315,Avon
+57317,Bonesteel
+57319,Bridgewater
+57321,Canova
+57322,Carpenter
+57323,Carthage
+57324,Cavour
+57325,Chamberlain
+57326,Chamberlain
+57328,Corsica
+57329,Dante
+57330,Delmont
+57331,Dimock
+57332,Emery
+57334,Ethan
+57335,Fairfax
+57337,Fedora
+57339,Fort Thompson
+57340,Fulton
+57341,Gann Valley
+57342,Geddes
+57344,Harrison
+57345,Highmore
+57346,Stephan
+57348,Hitchcock
+57349,Howard
+57350,Huron
+57353,Iroquois
+57354,Kaylor
+57355,Kimball
+57356,Lake Andes
+57357,Ravinia
+57358,Lane
+57359,Letcher
+57361,Marty
+57362,Miller
+57363,Mount Vernon
+57364,New Holland
+57365,Oacoma
+57366,Parkston
+57367,Pickstown
+57368,Plankinton
+57369,Platte
+57370,Pukwana
+57371,Ree Heights
+57373,Saint Lawrence
+57374,Spencer
+57375,Stickney
+57376,Tripp
+57379,Virgil
+57380,Wagner
+57381,Wessington
+57382,Wessington Springs
+57383,White Lake
+57384,Wolsey
+57385,Woonsocket
+57386,Yale
+57399,Huron
+57401,Aberdeen
+57402,Aberdeen
+57420,Akaska
+57421,Amherst
+57422,Andover
+57424,Ashton
+57426,Barnard
+57427,Bath
+57428,Bowdle
+57429,Brentford
+57430,Britton
+57432,Claremont
+57433,Columbia
+57434,Conde
+57435,Cresbard
+57436,Doland
+57437,Eureka
+57438,Faulkton
+57439,Ferney
+57440,Frankfort
+57441,Frederick
+57442,Gettysburg
+57445,Groton
+57446,Hecla
+57448,Hosmer
+57449,Houghton
+57450,Hoven
+57451,Ipswich
+57452,Java
+57454,Langford
+57455,Lebanon
+57456,Leola
+57457,Longlake
+57460,Mansfield
+57461,Mellette
+57462,Mina
+57465,Northville
+57466,Onaka
+57467,Orient
+57468,Pierpont
+57469,Redfield
+57470,Rockham
+57471,Roscoe
+57472,Selby
+57473,Seneca
+57474,Stratford
+57475,Tolstoy
+57476,Tulare
+57477,Turton
+57479,Warner
+57481,Westport
+57501,Pierre
+57520,Agar
+57521,Belvidere
+57522,Blunt
+57523,Burke
+57526,Carter
+57528,Colome
+57529,Dallas
+57531,Draper
+57532,Fort Pierre
+57533,Gregory
+57534,Hamill
+57536,Harrold
+57537,Hayes
+57538,Herrick
+57540,Holabird
+57541,Ideal
+57542,Iona
+57543,Kadoka
+57544,Kennebec
+57547,Long Valley
+57548,Lower Brule
+57551,Martin
+57552,Midland
+57553,Milesville
+57555,Mission
+57557,Mission Ridge
+57559,Murdo
+57560,Norris
+57562,Okaton
+57563,Okreek
+57564,Onida
+57566,Parmelee
+57567,Philip
+57568,Presho
+57569,Reliance
+57570,Rosebud
+57571,Saint Charles
+57572,Saint Francis
+57574,Tuthill
+57576,Vivian
+57577,Wanblee
+57578,Wewela
+57579,White River
+57580,Winner
+57584,Witten
+57585,Wood
+57601,Mobridge
+57620,Bison
+57621,Bullhead
+57622,Cherry Creek
+57623,Dupree
+57625,Eagle Butte
+57626,Faith
+57628,Firesteel
+57629,Glad Valley
+57630,Glencross
+57631,Glenham
+57632,Herreid
+57633,Isabel
+57634,Keldron
+57636,Lantry
+57638,Lemmon
+57639,Little Eagle
+57640,Lodgepole
+57641,Mc Intosh
+57642,Mc Laughlin
+57643,Mahto
+57644,Meadow
+57645,Morristown
+57646,Mound City
+57647,Parade
+57648,Pollock
+57649,Prairie City
+57650,Ralph
+57651,Reva
+57652,Ridgeview
+57653,Shadehill
+57656,Timber Lake
+57657,Trail City
+57658,Wakpala
+57659,Walker
+57660,Watauga
+57661,Whitehorse
+57701,Rapid City
+57702,Rapid City
+57703,Rapid City
+57706,Ellsworth AFB
+57708,Bethlehem
+57709,Rapid City
+57714,Allen
+57716,Batesland
+57717,Belle Fourche
+57718,Black Hawk
+57719,Box Elder
+57720,Buffalo
+57722,Buffalo Gap
+57724,Camp Crook
+57725,Caputa
+57729,Creighton
+57730,Custer
+57732,Deadwood
+57735,Edgemont
+57736,Elm Springs
+57737,Enning
+57738,Fairburn
+57741,Fort Meade
+57742,Fruitdale
+57744,Hermosa
+57745,Hill City
+57747,Hot Springs
+57748,Howes
+57750,Interior
+57751,Keystone
+57752,Kyle
+57754,Lead
+57755,Ludlow
+57756,Manderson
+57758,Mud Butte
+57759,Nemo
+57760,Newell
+57761,New Underwood
+57762,Nisland
+57763,Oelrichs
+57764,Oglala
+57765,Opal
+57766,Oral
+57767,Owanka
+57769,Piedmont
+57770,Pine Ridge
+57772,Porcupine
+57773,Pringle
+57774,Provo
+57775,Quinn
+57776,Redig
+57777,Red Owl
+57778,Rochford
+57779,Saint Onge
+57780,Scenic
+57782,Smithwick
+57783,Spearfish
+57785,Sturgis
+57787,Union Center
+57788,Vale
+57790,Wall
+57791,Wasta
+57792,White Owl
+57793,Whitewood
+57794,Wounded Knee
+57799,Spearfish
+29001,Alcolu
+29002,Ballentine
+29003,Bamberg
+29006,Batesburg
+29009,Bethune
+29010,Bishopville
+29014,Blackstock
+29015,Blair
+29016,Blythewood
+29018,Bowman
+29020,Camden
+29030,Cameron
+29031,Carlisle
+29032,Cassatt
+29033,Cayce
+29036,Chapin
+29037,Chappells
+29038,Cope
+29039,Cordova
+29040,Dalzell
+29041,Davis Station
+29042,Denmark
+29044,Eastover
+29045,Elgin
+29046,Elliott
+29047,Elloree
+29048,Eutawville
+29051,Gable
+29052,Gadsden
+29053,Gaston
+29054,Gilbert
+29055,Great Falls
+29056,Greeleyville
+29058,Heath Springs
+29059,Holly Hill
+29061,Hopkins
+29062,Horatio
+29063,Irmo
+29065,Jenkinsville
+29067,Kershaw
+29069,Lamar
+29070,Leesville
+29071,Lexington
+29072,Lexington
+29073,Lexington
+29074,Liberty Hill
+29075,Little Mountain
+29078,Lugoff
+29079,Lydia
+29080,Lynchburg
+29081,Ehrhardt
+29082,Lodge
+29101,Mc Bee
+29102,Manning
+29104,Mayesville
+29105,Monetta
+29106,Monticello
+29107,Neeses
+29108,Newberry
+29111,New Zion
+29112,North
+29113,Norway
+29114,Olanta
+29115,Orangeburg
+29116,Orangeburg
+29117,Orangeburg
+29118,Orangeburg
+29122,Peak
+29123,Pelion
+29124,Perry
+29125,Pinewood
+29126,Pomaria
+29127,Prosperity
+29128,Rembert
+29129,Ridge Spring
+29130,Ridgeway
+29132,Rion
+29133,Rowesville
+29135,Saint Matthews
+29137,Salley
+29138,Saluda
+29142,Santee
+29143,Sardinia
+29145,Silverstreet
+29146,Springfield
+29147,State Park
+29148,Summerton
+29150,Sumter
+29151,Sumter
+29152,Shaw A F B
+29153,Sumter
+29154,Sumter
+29160,Swansea
+29161,Timmonsville
+29162,Turbeville
+29163,Vance
+29164,Wagener
+29166,Ward
+29168,Wedgefield
+29169,West Columbia
+29170,West Columbia
+29171,West Columbia
+29172,West Columbia
+29175,Westville
+29176,White Oak
+29177,White Rock
+29178,Whitmire
+29180,Winnsboro
+29201,Columbia
+29202,Columbia
+29203,Columbia
+29204,Columbia
+29205,Columbia
+29206,Columbia
+29207,Columbia
+29208,Columbia
+29209,Columbia
+29210,Columbia
+29211,Columbia
+29212,Columbia
+29214,Columbia
+29215,Columbia
+29216,Columbia
+29217,Columbia
+29218,Columbia
+29219,Columbia
+29220,Columbia
+29221,Columbia
+29222,Columbia
+29223,Columbia
+29224,Columbia
+29225,Columbia
+29226,Columbia
+29227,Columbia
+29228,Columbia
+29229,Columbia
+29230,Columbia
+29240,Columbia
+29250,Columbia
+29260,Columbia
+29290,Columbia
+29292,Columbia
+29301,Spartanburg
+29302,Spartanburg
+29303,Spartanburg
+29304,Spartanburg
+29305,Spartanburg
+29306,Spartanburg
+29307,Spartanburg
+29316,Spartanburg
+29318,Spartanburg
+29319,Spartanburg
+29320,Arcadia
+29321,Buffalo
+29322,Campobello
+29323,Chesnee
+29324,Clifton
+29325,Clinton
+29329,Converse
+29330,Cowpens
+29331,Cross Anchor
+29332,Cross Hill
+29333,Drayton
+29334,Duncan
+29335,Enoree
+29336,Fairforest
+29338,Fingerville
+29340,Gaffney
+29341,Gaffney
+29342,Gaffney
+29346,Glendale
+29348,Gramling
+29349,Inman
+29351,Joanna
+29353,Jonesville
+29355,Kinards
+29356,Landrum
+29360,Laurens
+29364,Lockhart
+29365,Lyman
+29368,Mayo
+29369,Moore
+29370,Mountville
+29372,Pacolet
+29373,Pacolet Mills
+29374,Pauline
+29375,Reidville
+29376,Roebuck
+29377,Startex
+29378,Una
+29379,Union
+29384,Waterloo
+29385,Wellford
+29386,White Stone
+29388,Woodruff
+29390,Duncan
+29391,Duncan
+29395,Jonesville
+29401,Charleston
+29402,Charleston
+29403,Charleston
+29404,Charleston AFB
+29405,North Charleston
+29406,Charleston
+29407,Charleston
+29409,Charleston
+29410,North Charleston
+29412,Charleston
+29413,Charleston
+29414,Charleston
+29415,North Charleston
+29416,Charleston
+29417,Charleston
+29418,North Charleston
+29419,North Charleston
+29420,North Charleston
+29422,Charleston
+29423,Charleston
+29424,Charleston
+29425,Charleston
+29426,Adams Run
+29429,Awendaw
+29430,Bethera
+29431,Bonneau
+29432,Branchville
+29433,Canadys
+29434,Cordesville
+29435,Cottageville
+29436,Cross
+29437,Dorchester
+29438,Edisto Island
+29439,Folly Beach
+29440,Georgetown
+29442,Georgetown
+29445,Goose Creek
+29446,Green Pond
+29447,Grover
+29448,Harleyville
+29449,Hollywood
+29450,Huger
+29451,Isle of Palms
+29452,Jacksonboro
+29453,Jamestown
+29455,Johns Island
+29456,Ladson
+29457,Johns Island
+29458,Mc Clellanville
+29461,Moncks Corner
+29464,Mount Pleasant
+29465,Mount Pleasant
+29466,Mount Pleasant
+29468,Pineville
+29469,Pinopolis
+29470,Ravenel
+29471,Reevesville
+29472,Ridgeville
+29474,Round O
+29475,Ruffin
+29476,Russellville
+29477,Saint George
+29479,Saint Stephen
+29481,Smoaks
+29482,Sullivans Island
+29483,Summerville
+29484,Summerville
+29485,Summerville
+29487,Wadmalaw Island
+29488,Walterboro
+29492,Charleston
+29493,Williams
+29501,Florence
+29502,Florence
+29503,Florence
+29504,Florence
+29505,Florence
+29506,Florence
+29510,Andrews
+29511,Aynor
+29512,Bennettsville
+29516,Blenheim
+29518,Cades
+29519,Centenary
+29520,Cheraw
+29525,Clio
+29526,Conway
+29527,Conway
+29528,Conway
+29530,Coward
+29532,Darlington
+29536,Dillon
+29540,Darlington
+29541,Effingham
+29542,Floyd Dale
+29543,Fork
+29544,Galivants Ferry
+29545,Green Sea
+29546,Gresham
+29547,Hamer
+29550,Hartsville
+29551,Hartsville
+29554,Hemingway
+29555,Johnsonville
+29556,Kingstree
+29560,Lake City
+29563,Lake View
+29564,Lane
+29565,Latta
+29566,Little River
+29567,Little Rock
+29568,Longs
+29569,Loris
+29570,Mc Coll
+29571,Marion
+29572,Myrtle Beach
+29573,Minturn
+29574,Mullins
+29575,Myrtle Beach
+29576,Murrells Inlet
+29577,Myrtle Beach
+29578,Myrtle Beach
+29579,Myrtle Beach
+29580,Nesmith
+29581,Nichols
+29582,North Myrtle Beach
+29583,Pamplico
+29584,Patrick
+29585,Pawleys Island
+29587,Myrtle Beach
+29589,Rains
+29590,Salters
+29591,Scranton
+29592,Sellers
+29593,Society Hill
+29594,Tatum
+29596,Wallace
+29597,North Myrtle Beach
+29598,North Myrtle Beach
+29601,Greenville
+29602,Greenville
+29603,Greenville
+29604,Greenville
+29605,Greenville
+29606,Greenville
+29607,Greenville
+29608,Greenville
+29609,Greenville
+29610,Greenville
+29611,Greenville
+29612,Greenville
+29613,Greenville
+29614,Greenville
+29615,Greenville
+29616,Greenville
+29617,Greenville
+29620,Abbeville
+29621,Anderson
+29622,Anderson
+29623,Anderson
+29624,Anderson
+29625,Anderson
+29626,Anderson
+29627,Belton
+29628,Calhoun Falls
+29630,Central
+29631,Clemson
+29632,Clemson
+29633,Clemson
+29634,Clemson
+29635,Cleveland
+29636,Conestee
+29638,Donalds
+29639,Due West
+29640,Easley
+29641,Easley
+29642,Easley
+29643,Fair Play
+29644,Fountain Inn
+29645,Gray Court
+29646,Greenwood
+29647,Greenwood
+29648,Greenwood
+29649,Greenwood
+29650,Greer
+29651,Greer
+29652,Greer
+29653,Hodges
+29654,Honea Path
+29655,Iva
+29656,La France
+29657,Liberty
+29658,Long Creek
+29659,Lowndesville
+29661,Marietta
+29662,Mauldin
+29664,Mountain Rest
+29665,Newry
+29666,Ninety Six
+29667,Norris
+29669,Pelzer
+29670,Pendleton
+29671,Pickens
+29672,Seneca
+29673,Piedmont
+29675,Richland
+29676,Salem
+29677,Sandy Springs
+29678,Seneca
+29679,Seneca
+29680,Simpsonville
+29681,Simpsonville
+29682,Six Mile
+29683,Slater
+29684,Starr
+29685,Sunset
+29686,Tamassee
+29687,Taylors
+29688,Tigerville
+29689,Townville
+29690,Travelers Rest
+29691,Walhalla
+29692,Ware Shoals
+29693,Westminster
+29695,Hodges
+29696,West Union
+29697,Williamston
+29698,Greenville
+29702,Blacksburg
+29703,Bowling Green
+29704,Catawba
+29706,Chester
+29708,Fort Mill
+29709,Chesterfield
+29710,Clover
+29712,Edgemoor
+29714,Fort Lawn
+29715,Fort Mill
+29716,Fort Mill
+29717,Hickory Grove
+29718,Jefferson
+29720,Lancaster
+29721,Lancaster
+29722,Lancaster
+29724,Lando
+29726,Mc Connells
+29727,Mount Croghan
+29728,Pageland
+29729,Richburg
+29730,Rock Hill
+29731,Rock Hill
+29732,Rock Hill
+29733,Rock Hill
+29734,Rock Hill
+29741,Ruby
+29742,Sharon
+29743,Smyrna
+29744,Van Wyck
+29745,York
+29801,Aiken
+29802,Aiken
+29803,Aiken
+29804,Aiken
+29805,Aiken
+29808,Aiken
+29809,New Ellenton
+29810,Allendale
+29812,Barnwell
+29813,Hilda
+29816,Bath
+29817,Blackville
+29819,Bradley
+29821,Clarks Hill
+29822,Clearwater
+29824,Edgefield
+29826,Elko
+29827,Fairfax
+29828,Gloverville
+29829,Graniteville
+29831,Jackson
+29832,Johnston
+29834,Langley
+29835,Mc Cormick
+29836,Martin
+29838,Modoc
+29839,Montmorenci
+29840,Mount Carmel
+29841,North Augusta
+29842,Beech Island
+29843,Olar
+29844,Parksville
+29845,Plum Branch
+29846,Sycamore
+29847,Trenton
+29848,Troy
+29849,Ulmer
+29850,Vaucluse
+29851,Warrenville
+29853,Williston
+29856,Windsor
+29860,North Augusta
+29861,North Augusta
+29899,Mc Cormick
+29901,Beaufort
+29902,Beaufort
+29903,Beaufort
+29904,Beaufort
+29905,Beaufort
+29906,Beaufort
+29910,Bluffton
+29911,Brunson
+29912,Coosawatchie
+29913,Crocketville
+29914,Dale
+29915,Daufuskie Island
+29916,Early Branch
+29918,Estill
+29920,Saint Helena Island
+29921,Furman
+29922,Garnett
+29923,Gifford
+29924,Hampton
+29925,Hilton Head Island
+29926,Hilton Head Island
+29927,Hardeeville
+29928,Hilton Head Island
+29929,Islandton
+29931,Lobeco
+29932,Luray
+29933,Miley
+29934,Pineland
+29935,Port Royal
+29936,Ridgeland
+29938,Hilton Head Island
+29939,Scotia
+29940,Seabrook
+29941,Sheldon
+29943,Tillman
+29944,Varnville
+29945,Yemassee
+02801,Adamsville
+02802,Albion
+02804,Ashaway
+02806,Barrington
+02807,Block Island
+02808,Bradford
+02809,Bristol
+02812,Carolina
+02813,Charlestown
+02814,Chepachet
+02815,Clayville
+02816,Coventry
+02817,West Greenwich
+02818,East Greenwich
+02822,Exeter
+02823,Fiskeville
+02824,Forestdale
+02825,Foster
+02826,Glendale
+02827,Greene
+02828,Greenville
+02829,Harmony
+02830,Harrisville
+02831,Hope
+02832,Hope Valley
+02833,Hopkinton
+02835,Jamestown
+02836,Kenyon
+02837,Little Compton
+02838,Manville
+02839,Mapleville
+02840,Newport
+02841,Newport
+02842,Middletown
+02852,North Kingstown
+02854,North Kingstown
+02857,North Scituate
+02858,Oakland
+02859,Pascoag
+02860,Pawtucket
+02861,Pawtucket
+02862,Pawtucket
+02863,Central Falls
+02864,Cumberland
+02865,Lincoln
+02871,Portsmouth
+02872,Prudence Island
+02873,Rockville
+02874,Saunderstown
+02875,Shannock
+02876,Slatersville
+02877,Slocum
+02878,Tiverton
+02879,Wakefield
+02880,Wakefield
+02881,Kingston
+02882,Narragansett
+02883,Peace Dale
+02885,Warren
+02886,Warwick
+02887,Warwick
+02888,Warwick
+02889,Warwick
+02891,Westerly
+02892,West Kingston
+02893,West Warwick
+02894,Wood River Junction
+02895,Woonsocket
+02896,North Smithfield
+02898,Wyoming
+02901,Providence
+02902,Providence
+02903,Providence
+02904,Providence
+02905,Providence
+02906,Providence
+02907,Providence
+02908,Providence
+02909,Providence
+02910,Cranston
+02911,North Providence
+02912,Providence
+02914,East Providence
+02915,Riverside
+02916,Rumford
+02917,Smithfield
+02918,Providence
+02919,Johnston
+02920,Cranston
+02921,Cranston
+02940,Providence
+96940,Palau,-2768
+00601,Adjuntas
+00602,Aguada
+00603,Aguadilla
+00604,Aguadilla
+00605,Aguadilla
+00606,Maricao
+00610,Anasco
+00611,Angeles
+00612,Arecibo
+00613,Arecibo
+00614,Arecibo
+00616,Bajadero
+00617,Barceloneta
+00622,Boqueron
+00623,Cabo Rojo
+00624,Penuelas
+00627,Camuy
+00631,Castaner
+00636,Rosario
+00637,Sabana Grande
+00638,Ciales
+00641,Utuado
+00646,Dorado
+00647,Ensenada
+00650,Florida
+00652,Garrochales
+00653,Guanica
+00656,Guayanilla
+00659,Hatillo
+00660,Hormigueros
+00662,Isabela
+00664,Jayuya
+00667,Lajas
+00669,Lares
+00670,Las Marias
+00674,Manati
+00676,Moca
+00677,Rincon
+00678,Quebradillas
+00680,Mayaguez
+00681,Mayaguez
+00682,Mayaguez
+00683,San German
+00685,San Sebastian
+00687,Morovis
+00688,Sabana Hoyos
+00690,San Antonio
+00692,Vega Alta
+00693,Vega Baja
+00694,Vega Baja
+00698,Yauco
+00703,Aguas Buenas
+00704,Aguirre
+00705,Aibonito
+00707,Maunabo
+00714,Arroyo
+00715,Mercedita
+00716,Ponce
+00717,Ponce
+00718,Naguabo
+00719,Naranjito
+00720,Orocovis
+00721,Palmer
+00723,Patillas
+00725,Caguas
+00726,Caguas
+00728,Ponce
+00729,Canovanas
+00730,Ponce
+00731,Ponce
+00732,Ponce
+00733,Ponce
+00734,Ponce
+00735,Ceiba
+00736,Cayey
+00737,Cayey
+00738,Fajardo
+00739,Cidra
+00740,Puerto Real
+00741,Punta Santiago
+00742,Roosevelt Roads
+00744,Rio Blanco
+00745,Rio Grande
+00751,Salinas
+00754,San Lorenzo
+00757,Santa Isabel
+00765,Vieques
+00766,Villalba
+00767,Yabucoa
+00769,Coamo
+00771,Las Piedras
+00772,Loiza
+00773,Luquillo
+00775,Culebra
+00777,Juncos
+00778,Gurabo
+00780,Coto Laurel
+00782,Comerio
+00783,Corozal
+00784,Guayama
+00785,Guayama
+00786,La Plata
+00791,Humacao
+00792,Humacao
+00794,Barranquitas
+00795,Juana Diaz
+00901,San Juan
+00902,San Juan
+00906,San Juan
+00907,San Juan
+00908,San Juan
+00909,San Juan
+00910,San Juan
+00911,San Juan
+00912,San Juan
+00913,San Juan
+00914,San Juan
+00915,San Juan
+00916,San Juan
+00917,San Juan
+00918,San Juan
+00919,San Juan
+00920,San Juan
+00921,San Juan
+00922,San Juan
+00923,San Juan
+00924,San Juan
+00925,San Juan
+00926,San Juan
+00927,San Juan
+00928,San Juan
+00929,San Juan
+00930,San Juan
+00931,San Juan
+00933,San Juan
+00934,Fort Buchanan
+00935,San Juan
+00936,San Juan
+00937,San Juan
+00938,San Juan
+00939,San Juan
+00940,San Juan
+00949,Toa Baja
+00950,Toa Baja
+00951,Toa Baja
+00952,Sabana Seca
+00953,Toa Alta
+00954,Toa Alta
+00955,San Juan
+00956,Bayamon
+00957,Bayamon
+00958,Bayamon
+00959,Bayamon
+00960,Bayamon
+00961,Bayamon
+00962,Catano
+00963,Catano
+00965,Guaynabo
+00966,Guaynabo
+00968,Guaynabo
+00969,Guaynabo
+00970,Guaynabo
+00971,Guaynabo
+00975,San Juan
+00976,Trujillo Alto
+00977,Trujillo Alto
+00978,St Just
+00979,Carolina
+00981,Carolina
+00982,Carolina
+00983,Carolina
+00984,Carolina
+00985,Carolina
+00986,Carolina
+00987,Carolina
+00988,Carolina
+15001,Aliquippa
+15003,Ambridge
+15004,Atlasburg
+15005,Baden
+15006,Bairdford
+15007,Bakerstown
+15009,Beaver
+15010,Beaver Falls
+15012,Belle Vernon
+15014,Brackenridge
+15015,Bradfordwoods
+15017,Bridgeville
+15018,Buena Vista
+15019,Bulger
+15020,Bunola
+15021,Burgettstown
+15022,Charleroi
+15024,Cheswick
+15025,Clairton
+15026,Clinton
+15027,Conway
+15028,Coulters
+15030,Creighton
+15031,Cuddy
+15032,Curtisville
+15033,Donora
+15034,Dravosburg
+15035,East Mc Keesport
+15036,Eldersville
+15037,Elizabeth
+15038,Elrama
+15042,Freedom
+15043,Georgetown
+15044,Gibsonia
+15045,Glassport
+15046,Crescent
+15047,Greenock
+15049,Harwick
+15050,Hookstown
+15051,Indianola
+15052,Industry
+15053,Joffre
+15054,Langeloth
+15055,Lawrence
+15056,Leetsdale
+15057,Mc Donald
+15059,Midland
+15060,Midway
+15061,Monaca
+15062,Monessen
+15063,Monongahela
+15064,Morgan
+15065,Natrona Heights
+15066,New Brighton
+15067,New Eagle
+15068,New Kensington
+15069,New Kensington
+15071,Oakdale
+15072,Pricedale
+15074,Rochester
+15075,Rural Ridge
+15076,Russellton
+15077,Shippingport
+15078,Slovan
+15081,South Heights
+15082,Sturgeon
+15083,Sutersville
+15084,Tarentum
+15085,Trafford
+15086,Warrendale
+15087,Webster
+15088,West Elizabeth
+15089,West Newton
+15090,Wexford
+15091,Wildwood
+15095,Warrendale
+15096,Warrendale
+15101,Allison Park
+15102,Bethel Park
+15104,Braddock
+15106,Carnegie
+15108,Coraopolis
+15110,Duquesne
+15112,East Pittsburgh
+15116,Glenshaw
+15120,Homestead
+15122,West Mifflin
+15123,West Mifflin
+15126,Imperial
+15127,Ingomar
+15129,Library
+15130,Mc Keesport
+15131,Mc Keesport
+15132,Mc Keesport
+15133,Mc Keesport
+15134,Mc Keesport
+15135,Mc Keesport
+15136,Mc Kees Rocks
+15137,North Versailles
+15139,Oakmont
+15140,Pitcairn
+15142,Presto
+15143,Sewickley
+15144,Springdale
+15145,Turtle Creek
+15146,Monroeville
+15147,Verona
+15148,Wilmerding
+15189,Sewickley
+15201,Pittsburgh
+15202,Pittsburgh
+15203,Pittsburgh
+15204,Pittsburgh
+15205,Pittsburgh
+15206,Pittsburgh
+15207,Pittsburgh
+15208,Pittsburgh
+15209,Pittsburgh
+15210,Pittsburgh
+15211,Pittsburgh
+15212,Pittsburgh
+15213,Pittsburgh
+15214,Pittsburgh
+15215,Pittsburgh
+15216,Pittsburgh
+15217,Pittsburgh
+15218,Pittsburgh
+15219,Pittsburgh
+15220,Pittsburgh
+15221,Pittsburgh
+15222,Pittsburgh
+15223,Pittsburgh
+15224,Pittsburgh
+15225,Pittsburgh
+15226,Pittsburgh
+15227,Pittsburgh
+15228,Pittsburgh
+15229,Pittsburgh
+15230,Pittsburgh
+15231,Pittsburgh
+15232,Pittsburgh
+15233,Pittsburgh
+15234,Pittsburgh
+15235,Pittsburgh
+15236,Pittsburgh
+15237,Pittsburgh
+15238,Pittsburgh
+15239,Pittsburgh
+15240,Pittsburgh
+15241,Pittsburgh
+15242,Pittsburgh
+15243,Pittsburgh
+15244,Pittsburgh
+15250,Pittsburgh
+15251,Pittsburgh
+15252,Pittsburgh
+15253,Pittsburgh
+15254,Pittsburgh
+15255,Pittsburgh
+15257,Pittsburgh
+15258,Pittsburgh
+15259,Pittsburgh
+15260,Pittsburgh
+15261,Pittsburgh
+15262,Pittsburgh
+15263,Pittsburgh
+15264,Pittsburgh
+15265,Pittsburgh
+15266,Pittsburgh
+15267,Pittsburgh
+15268,Pittsburgh
+15270,Pittsburgh
+15272,Pittsburgh
+15274,Pittsburgh
+15275,Pittsburgh
+15276,Pittsburgh
+15277,Pittsburgh
+15278,Pittsburgh
+15279,Pittsburgh
+15281,Pittsburgh
+15282,Pittsburgh
+15283,Pittsburgh
+15285,Pittsburgh
+15286,Pittsburgh
+15290,Pittsburgh
+15301,Washington
+15310,Aleppo
+15311,Amity
+15312,Avella
+15313,Beallsville
+15314,Bentleyville
+15315,Bobtown
+15316,Brave
+15317,Canonsburg
+15320,Carmichaels
+15321,Cecil
+15322,Clarksville
+15323,Claysville
+15324,Cokeburg
+15325,Crucible
+15327,Dilliner
+15329,Prosperity
+15330,Eighty Four
+15331,Ellsworth
+15332,Finleyville
+15333,Fredericktown
+15334,Garards Fort
+15336,Gastonville
+15337,Graysville
+15338,Greensboro
+15339,Hendersonville
+15340,Hickory
+15341,Holbrook
+15342,Houston
+15344,Jefferson
+15345,Marianna
+15346,Mather
+15347,Meadow Lands
+15348,Millsboro
+15349,Mount Morris
+15350,Muse
+15351,Nemacolin
+15352,New Freeport
+15353,Nineveh
+15354,Pine Bank
+15357,Rices Landing
+15358,Richeyville
+15359,Rogersville
+15360,Scenery Hill
+15361,Southview
+15362,Spraggs
+15363,Strabane
+15364,Sycamore
+15365,Taylorstown
+15366,Van Voorhis
+15367,Venetia
+15368,Vestaburg
+15370,Waynesburg
+15376,West Alexander
+15377,West Finley
+15378,Westland
+15379,West Middletown
+15380,Wind Ridge
+15401,Uniontown
+15410,Adah
+15411,Addison
+15412,Allenport
+15413,Allison
+15415,Brier Hill
+15416,Brownfield
+15417,Brownsville
+15419,California
+15420,Cardale
+15421,Chalk Hill
+15422,Chestnut Ridge
+15423,Coal Center
+15424,Confluence
+15425,Connellsville
+15427,Daisytown
+15428,Dawson
+15429,Denbo
+15430,Dickerson Run
+15431,Dunbar
+15432,Dunlevy
+15433,East Millsboro
+15434,Elco
+15435,Fairbank
+15436,Fairchance
+15437,Farmington
+15438,Fayette City
+15439,Gans
+15440,Gibbon Glade
+15442,Grindstone
+15443,Hibbs
+15444,Hiller
+15445,Hopwood
+15446,Indian Head
+15447,Isabella
+15448,Jacobs Creek
+15449,Keisterville
+15450,La Belle
+15451,Lake Lynn
+15454,Leckrone
+15455,Leisenring
+15456,Lemont Furnace
+15458,Mc Clellandtown
+15459,Markleysburg
+15460,Martin
+15461,Masontown
+15462,Melcroft
+15463,Merrittstown
+15464,Mill Run
+15465,Mount Braddock
+15466,Newell
+15467,New Geneva
+15468,New Salem
+15469,Normalville
+15470,Ohiopyle
+15472,Oliver
+15473,Perryopolis
+15474,Point Marion
+15475,Republic
+15476,Ronco
+15477,Roscoe
+15478,Smithfield
+15479,Smithton
+15480,Smock
+15482,Star Junction
+15483,Stockdale
+15484,Uledi
+15485,Ursina
+15486,Vanderbilt
+15488,Waltersburg
+15489,West Leisenring
+15490,White
+15492,Wickhaven
+15501,Somerset
+15502,Hidden Valley
+15510,Somerset
+15520,Acosta
+15521,Alum Bank
+15522,Bedford
+15530,Berlin
+15531,Boswell
+15532,Boynton
+15533,Breezewood
+15534,Buffalo Mills
+15535,Clearville
+15536,Crystal Spring
+15537,Everett
+15538,Fairhope
+15539,Fishertown
+15540,Fort Hill
+15541,Friedens
+15542,Garrett
+15544,Gray
+15545,Hyndman
+15546,Jenners
+15547,Jennerstown
+15548,Kantner
+15549,Listie
+15550,Manns Choice
+15551,Markleton
+15552,Meyersdale
+15553,New Baltimore
+15554,New Paris
+15555,Quecreek
+15557,Rockwood
+15558,Salisbury
+15559,Schellsburg
+15560,Shanksville
+15561,Sipesville
+15562,Springs
+15563,Stoystown
+15564,Wellersburg
+15565,West Salisbury
+15601,Greensburg
+15605,Greensburg
+15606,Greensburg
+15610,Acme
+15611,Adamsburg
+15612,Alverton
+15613,Apollo
+15615,Ardara
+15616,Armbrust
+15617,Arona
+15618,Avonmore
+15619,Bovard
+15620,Bradenville
+15621,Calumet
+15622,Champion
+15623,Claridge
+15624,Crabtree
+15625,Darragh
+15626,Delmont
+15627,Derry
+15628,Donegal
+15629,East Vandergrift
+15630,Edmon
+15631,Everson
+15632,Export
+15633,Forbes Road
+15634,Grapeville
+15635,Hannastown
+15636,Harrison City
+15637,Herminie
+15638,Hostetter
+15639,Hunker
+15640,Hutchinson
+15641,Hyde Park
+15642,Irwin
+15644,Jeannette
+15646,Jones Mills
+15647,Larimer
+15650,Latrobe
+15655,Laughlintown
+15656,Leechburg
+15658,Ligonier
+15660,Lowber
+15661,Loyalhanna
+15662,Luxor
+15663,Madison
+15664,Mammoth
+15665,Manor
+15666,Mount Pleasant
+15668,Murrysville
+15670,New Alexandria
+15671,New Derry
+15672,New Stanton
+15673,North Apollo
+15674,Norvelt
+15675,Penn
+15676,Pleasant Unity
+15677,Rector
+15678,Rillton
+15679,Ruffs Dale
+15680,Salina
+15681,Saltsburg
+15682,Schenley
+15683,Scottdale
+15684,Slickville
+15685,Southwest
+15686,Spring Church
+15687,Stahlstown
+15688,Tarrs
+15689,United
+15690,Vandergrift
+15691,Wendel
+15692,Westmoreland City
+15693,Whitney
+15695,Wyano
+15696,Youngstown
+15697,Youngwood
+15698,Yukon
+15701,Indiana
+15705,Indiana
+15710,Alverda
+15711,Anita
+15712,Arcadia
+15713,Aultman
+15714,Northern Cambria
+15715,Big Run
+15716,Black Lick
+15717,Blairsville
+15720,Brush Valley
+15721,Burnside
+15722,Carrolltown
+15723,Chambersville
+15724,Cherry Tree
+15725,Clarksburg
+15727,Clune
+15728,Clymer
+15729,Commodore
+15730,Coolspring
+15731,Coral
+15732,Creekside
+15733,De Lancey
+15734,Dixonville
+15736,Elderton
+15737,Elmora
+15738,Emeigh
+15739,Ernest
+15740,Frostburg
+15741,Gipsy
+15742,Glen Campbell
+15744,Hamilton
+15745,Heilwood
+15746,Hillsdale
+15747,Home
+15748,Homer City
+15750,Josephine
+15751,Juneau
+15752,Kent
+15753,La Jose
+15754,Lucernemines
+15756,Mc Intyre
+15757,Mahaffey
+15758,Marchand
+15759,Marion Center
+15760,Marsteller
+15761,Mentcle
+15762,Nicktown
+15763,Northpoint
+15764,Oliveburg
+15765,Penn Run
+15767,Punxsutawney
+15770,Ringgold
+15771,Rochester Mills
+15772,Rossiter
+15773,Saint Benedict
+15774,Shelocta
+15775,Spangler
+15776,Sprankle Mills
+15777,Starford
+15778,Timblin
+15779,Torrance
+15780,Valier
+15781,Walston
+15783,West Lebanon
+15784,Worthville
+15801,Du Bois
+15821,Benezett
+15822,Brandy Camp
+15823,Brockport
+15824,Brockway
+15825,Brookville
+15827,Byrnedale
+15828,Clarington
+15829,Corsica
+15831,Dagus Mines
+15832,Driftwood
+15834,Emporium
+15840,Falls Creek
+15841,Force
+15845,Johnsonburg
+15846,Kersey
+15847,Knox Dale
+15848,Luthersburg
+15849,Penfield
+15851,Reynoldsville
+15853,Ridgway
+15856,Rockton
+15857,Saint Marys
+15860,Sigel
+15861,Sinnamahoning
+15863,Stump Creek
+15864,Summerville
+15865,Sykesville
+15866,Troutville
+15868,Weedville
+15870,Wilcox
+15901,Johnstown
+15902,Johnstown
+15904,Johnstown
+15905,Johnstown
+15906,Johnstown
+15907,Johnstown
+15909,Johnstown
+15915,Johnstown
+15920,Armagh
+15921,Beaverdale
+15922,Belsano
+15923,Bolivar
+15924,Cairnbrook
+15925,Cassandra
+15926,Central City
+15927,Colver
+15928,Davidsville
+15929,Dilltown
+15930,Dunlo
+15931,Ebensburg
+15934,Elton
+15935,Hollsopple
+15936,Hooversville
+15937,Jerome
+15938,Lilly
+15940,Loretto
+15942,Mineral Point
+15943,Nanty Glo
+15944,New Florence
+15945,Parkhill
+15946,Portage
+15948,Revloc
+15949,Robinson
+15951,Saint Michael
+15952,Salix
+15953,Seanor
+15954,Seward
+15955,Sidman
+15956,South Fork
+15957,Strongstown
+15958,Summerhill
+15959,Tire Hill
+15960,Twin Rocks
+15961,Vintondale
+15962,Wilmore
+15963,Windber
+16001,Butler
+16002,Butler
+16003,Butler
+16016,Boyers
+16017,Boyers
+16018,Boyers
+16020,Boyers
+16021,Branchton
+16022,Bruin
+16023,Cabot
+16024,Callery
+16025,Chicora
+16027,Connoquenessing
+16028,East Brady
+16029,East Butler
+16030,Eau Claire
+16033,Evans City
+16034,Fenelton
+16035,Forestville
+16036,Foxburg
+16037,Harmony
+16038,Harrisville
+16039,Herman
+16040,Hilliards
+16041,Karns City
+16045,Lyndora
+16046,Mars
+16048,North Washington
+16049,Parker
+16050,Petrolia
+16051,Portersville
+16052,Prospect
+16053,Renfrew
+16054,Saint Petersburg
+16055,Sarver
+16056,Saxonburg
+16057,Slippery Rock
+16058,Turkey City
+16059,Valencia
+16061,West Sunbury
+16063,Zelienople
+16066,Cranberry Twp
+16101,New Castle
+16102,New Castle
+16103,New Castle
+16105,New Castle
+16107,New Castle
+16108,New Castle
+16110,Adamsville
+16111,Atlantic
+16112,Bessemer
+16113,Clark
+16114,Clarks Mills
+16115,Darlington
+16116,Edinburg
+16117,Ellwood City
+16120,Enon Valley
+16121,Farrell
+16123,Fombell
+16124,Fredonia
+16125,Greenville
+16127,Grove City
+16130,Hadley
+16131,Hartstown
+16132,Hillsville
+16133,Jackson Center
+16134,Jamestown
+16136,Koppel
+16137,Mercer
+16140,New Bedford
+16141,New Galilee
+16142,New Wilmington
+16143,Pulaski
+16145,Sandy Lake
+16146,Sharon
+16148,Hermitage
+16150,Sharpsville
+16151,Sheakleyville
+16153,Stoneboro
+16154,Transfer
+16155,Villa Maria
+16156,Volant
+16157,Wampum
+16159,West Middlesex
+16160,West Pittsburg
+16161,Wheatland
+16172,New Wilmington
+16201,Kittanning
+16210,Adrian
+16211,Beyer
+16212,Cadogan
+16213,Callensburg
+16214,Clarion
+16215,Kittanning
+16216,Climax
+16217,Cooksburg
+16218,Cowansville
+16220,Crown
+16221,Curllsville
+16222,Dayton
+16223,Distant
+16224,Fairmount City
+16225,Fisher
+16226,Ford City
+16228,Ford Cliff
+16229,Freeport
+16230,Hawthorn
+16232,Knox
+16233,Leeper
+16234,Limestone
+16235,Lucinda
+16236,Mc Grann
+16238,Manorville
+16239,Marienville
+16240,Mayport
+16242,New Bethlehem
+16244,Nu Mine
+16245,Oak Ridge
+16246,Plumville
+16248,Rimersburg
+16249,Rural Valley
+16250,Sagamore
+16253,Seminole
+16254,Shippenville
+16255,Sligo
+16256,Smicksburg
+16257,Snydersburg
+16258,Strattanville
+16259,Templeton
+16260,Vowinckel
+16261,Widnoon
+16262,Worthington
+16263,Yatesboro
+16301,Oil City
+16311,Carlton
+16312,Chandlers Valley
+16313,Clarendon
+16314,Cochranton
+16316,Conneaut Lake
+16317,Cooperstown
+16319,Cranberry
+16321,East Hickory
+16322,Endeavor
+16323,Franklin
+16326,Fryburg
+16327,Guys Mills
+16328,Hydetown
+16329,Irvine
+16331,Kossuth
+16332,Lickingville
+16333,Ludlow
+16334,Marble
+16335,Meadville
+16340,Pittsfield
+16341,Pleasantville
+16342,Polk
+16343,Reno
+16344,Rouseville
+16345,Russell
+16346,Seneca
+16347,Sheffield
+16350,Sugar Grove
+16351,Tidioute
+16352,Tiona
+16353,Tionesta
+16354,Titusville
+16360,Townville
+16361,Tylersburg
+16362,Utica
+16364,Venus
+16365,Warren
+16366,Warren
+16367,Warren
+16368,Irvine
+16369,Irvine
+16370,West Hickory
+16371,Youngsville
+16372,Clintonville
+16373,Emlenton
+16374,Kennerdell
+16375,Lamartine
+16388,Meadville
+16401,Albion
+16402,Bear Lake
+16403,Cambridge Springs
+16404,Centerville
+16405,Columbus
+16406,Conneautville
+16407,Corry
+16410,Cranesville
+16411,East Springfield
+16412,Edinboro
+16413,Elgin
+16415,Fairview
+16416,Garland
+16417,Girard
+16420,Grand Valley
+16421,Harborcreek
+16422,Harmonsburg
+16423,Lake City
+16424,Linesville
+16426,Mc Kean
+16427,Mill Village
+16428,North East
+16430,North Springfield
+16432,Riceville
+16433,Saegertown
+16434,Spartansburg
+16435,Springboro
+16436,Spring Creek
+16438,Union City
+16440,Venango
+16441,Waterford
+16442,Wattsburg
+16443,West Springfield
+16444,Edinboro
+16475,Albion
+16501,Erie
+16502,Erie
+16503,Erie
+16504,Erie
+16505,Erie
+16506,Erie
+16507,Erie
+16508,Erie
+16509,Erie
+16510,Erie
+16511,Erie
+16512,Erie
+16514,Erie
+16515,Erie
+16522,Erie
+16530,Erie
+16531,Erie
+16532,Erie
+16533,Erie
+16534,Erie
+16538,Erie
+16541,Erie
+16544,Erie
+16546,Erie
+16550,Erie
+16553,Erie
+16554,Erie
+16558,Erie
+16563,Erie
+16565,Erie
+16601,Altoona
+16602,Altoona
+16603,Altoona
+16611,Alexandria
+16613,Ashville
+16614,Bakers Summit
+16616,Beccaria
+16617,Bellwood
+16619,Blandburg
+16620,Brisbin
+16621,Broad Top
+16622,Calvin
+16623,Cassville
+16624,Chest Springs
+16625,Claysburg
+16627,Coalport
+16629,Coupon
+16630,Cresson
+16631,Curryville
+16633,Defiance
+16634,Dudley
+16635,Duncansville
+16636,Dysart
+16637,East Freedom
+16638,Entriken
+16639,Fallentimber
+16640,Flinton
+16641,Gallitzin
+16644,Glasgow
+16645,Glen Hope
+16646,Hastings
+16647,Hesston
+16648,Hollidaysburg
+16650,Hopewell
+16651,Houtzdale
+16652,Huntingdon
+16654,Huntingdon
+16655,Imler
+16656,Irvona
+16657,James Creek
+16659,Loysburg
+16660,Mc Connellstown
+16661,Madera
+16662,Martinsburg
+16663,Morann
+16664,New Enterprise
+16665,Newry
+16666,Osceola Mills
+16667,Osterburg
+16668,Patton
+16669,Petersburg
+16670,Queen
+16671,Ramey
+16672,Riddlesburg
+16673,Roaring Spring
+16674,Robertsdale
+16675,Saint Boniface
+16677,Sandy Ridge
+16678,Saxton
+16679,Six Mile Run
+16680,Smithmill
+16681,Smokerun
+16682,Sproul
+16683,Spruce Creek
+16684,Tipton
+16685,Todd
+16686,Tyrone
+16689,Waterfall
+16691,Wells Tannery
+16692,Westover
+16693,Williamsburg
+16694,Wood
+16695,Woodbury
+16698,Houtzdale
+16699,Cresson
+16701,Bradford
+16720,Austin
+16724,Crosby
+16725,Custer City
+16726,Cyclone
+16727,Derrick City
+16728,De Young
+16729,Duke Center
+16730,East Smethport
+16731,Eldred
+16732,Gifford
+16733,Hazel Hurst
+16734,James City
+16735,Kane
+16738,Lewis Run
+16740,Mount Jewett
+16743,Port Allegany
+16744,Rew
+16745,Rixford
+16746,Roulette
+16748,Shinglehouse
+16749,Smethport
+16750,Turtlepoint
+16751,Westline
+16801,State College
+16802,University Park
+16803,State College
+16804,State College
+16805,State College
+16820,Aaronsburg
+16821,Allport
+16822,Beech Creek
+16823,Bellefonte
+16825,Bigler
+16826,Blanchard
+16827,Boalsburg
+16828,Centre Hall
+16829,Clarence
+16830,Clearfield
+16832,Coburn
+16833,Curwensville
+16834,Drifting
+16835,Fleming
+16836,Frenchville
+16837,Glen Richey
+16838,Grampian
+16839,Grassflat
+16840,Hawk Run
+16841,Howard
+16843,Hyde
+16844,Julian
+16845,Karthaus
+16847,Kylertown
+16848,Lamar
+16849,Lanse
+16850,Lecontes Mills
+16851,Lemont
+16852,Madisonburg
+16853,Milesburg
+16854,Millheim
+16855,Mineral Springs
+16856,Mingoville
+16858,Morrisdale
+16859,Moshannon
+16860,Munson
+16861,New Millport
+16863,Olanta
+16864,Orviston
+16865,Pennsylvania Furnace
+16866,Philipsburg
+16868,Pine Grove Mills
+16870,Port Matilda
+16871,Pottersdale
+16872,Rebersburg
+16873,Shawville
+16874,Snow Shoe
+16875,Spring Mills
+16876,Wallaceton
+16877,Warriors Mark
+16878,West Decatur
+16879,Winburne
+16881,Woodland
+16882,Woodward
+16901,Wellsboro
+16910,Alba
+16911,Arnot
+16912,Blossburg
+16914,Columbia Cross Roads
+16915,Coudersport
+16917,Covington
+16918,Cowanesque
+16920,Elkland
+16921,Gaines
+16922,Galeton
+16923,Genesee
+16925,Gillett
+16926,Granville Summit
+16927,Harrison Valley
+16928,Knoxville
+16929,Lawrenceville
+16930,Liberty
+16932,Mainesburg
+16933,Mansfield
+16935,Middlebury Center
+16936,Millerton
+16937,Mills
+16938,Morris
+16939,Morris Run
+16940,Nelson
+16941,Genesee
+16942,Osceola
+16943,Sabinsville
+16945,Sylvania
+16946,Tioga
+16947,Troy
+16948,Ulysses
+16950,Westfield
+17001,Camp Hill
+17002,Allensville
+17003,Annville
+17004,Belleville
+17005,Berrysburg
+17006,Blain
+17007,Boiling Springs
+17008,Bowmansdale
+17009,Burnham
+17010,Campbelltown
+17011,Camp Hill
+17012,Camp Hill
+17013,Carlisle
+17014,Cocolamus
+17016,Cornwall
+17017,Dalmatia
+17018,Dauphin
+17019,Dillsburg
+17020,Duncannon
+17021,East Waterford
+17022,Elizabethtown
+17023,Elizabethville
+17024,Elliottsburg
+17025,Enola
+17026,Fredericksburg
+17027,Grantham
+17028,Grantville
+17029,Granville
+17030,Gratz
+17031,Green Park
+17032,Halifax
+17033,Hershey
+17034,Highspire
+17035,Honey Grove
+17036,Hummelstown
+17037,Ickesburg
+17038,Jonestown
+17039,Kleinfeltersville
+17040,Landisburg
+17041,Lawn
+17042,Lebanon
+17043,Lemoyne
+17044,Lewistown
+17045,Liverpool
+17046,Lebanon
+17047,Loysville
+17048,Lykens
+17049,Mc Alisterville
+17051,Mc Veytown
+17052,Mapleton Depot
+17053,Marysville
+17054,Mattawana
+17055,Mechanicsburg
+17056,Mexico
+17057,Middletown
+17058,Mifflin
+17059,Mifflintown
+17060,Mill Creek
+17061,Millersburg
+17062,Millerstown
+17063,Milroy
+17064,Mount Gretna
+17065,Mount Holly Springs
+17066,Mount Union
+17067,Myerstown
+17068,New Bloomfield
+17069,New Buffalo
+17070,New Cumberland
+17071,New Germantown
+17072,New Kingstown
+17073,Newmanstown
+17074,Newport
+17075,Newton Hamilton
+17076,Oakland Mills
+17077,Ono
+17078,Palmyra
+17080,Pillow
+17081,Plainfield
+17082,Port Royal
+17083,Quentin
+17084,Reedsville
+17085,Rexmont
+17086,Richfield
+17087,Richland
+17088,Schaefferstown
+17089,Camp Hill
+17090,Shermans Dale
+17091,Camp Hill
+17093,Summerdale
+17094,Thompsontown
+17097,Wiconisco
+17098,Williamstown
+17099,Yeagertown
+17101,Harrisburg
+17102,Harrisburg
+17103,Harrisburg
+17104,Harrisburg
+17105,Harrisburg
+17106,Harrisburg
+17107,Harrisburg
+17108,Harrisburg
+17109,Harrisburg
+17110,Harrisburg
+17111,Harrisburg
+17112,Harrisburg
+17113,Harrisburg
+17120,Harrisburg
+17121,Harrisburg
+17122,Harrisburg
+17123,Harrisburg
+17124,Harrisburg
+17125,Harrisburg
+17126,Harrisburg
+17127,Harrisburg
+17128,Harrisburg
+17129,Harrisburg
+17130,Harrisburg
+17140,Harrisburg
+17177,Harrisburg
+17201,Chambersburg
+17210,Amberson
+17211,Artemas
+17212,Big Cove Tannery
+17213,Blairs Mills
+17214,Blue Ridge Summit
+17215,Burnt Cabins
+17217,Concord
+17219,Doylesburg
+17220,Dry Run
+17221,Fannettsburg
+17222,Fayetteville
+17223,Fort Littleton
+17224,Fort Loudon
+17225,Greencastle
+17228,Harrisonville
+17229,Hustontown
+17231,Lemasters
+17232,Lurgan
+17233,Mc Connellsburg
+17235,Marion
+17236,Mercersburg
+17237,Mont Alto
+17238,Needmore
+17239,Neelyton
+17240,Newburg
+17241,Newville
+17243,Orbisonia
+17244,Orrstown
+17246,Pleasant Hall
+17247,Quincy
+17249,Rockhill Furnace
+17250,Rouzerville
+17251,Roxbury
+17252,Saint Thomas
+17253,Saltillo
+17254,Scotland
+17255,Shade Gap
+17256,Shady Grove
+17257,Shippensburg
+17260,Shirleysburg
+17261,South Mountain
+17262,Spring Run
+17263,State Line
+17264,Three Springs
+17265,Upperstrasburg
+17266,Walnut Bottom
+17267,Warfordsburg
+17268,Waynesboro
+17270,Williamson
+17271,Willow Hill
+17272,Zullinger
+17294,Blue Ridge Summit
+17301,Abbottstown
+17302,Airville
+17303,Arendtsville
+17304,Aspers
+17306,Bendersville
+17307,Biglerville
+17309,Brogue
+17310,Cashtown
+17311,Codorus
+17312,Craley
+17313,Dallastown
+17314,Delta
+17315,Dover
+17316,East Berlin
+17317,East Prospect
+17318,Emigsville
+17319,Etters
+17320,Fairfield
+17321,Fawn Grove
+17322,Felton
+17323,Franklintown
+17324,Gardners
+17325,Gettysburg
+17326,Gettysburg
+17327,Glen Rock
+17329,Glenville
+17331,Hanover
+17332,Hanover
+17333,Hanover
+17337,Idaville
+17339,Lewisberry
+17340,Littlestown
+17342,Loganville
+17343,Mc Knightstown
+17344,Mc Sherrystown
+17345,Manchester
+17346,Menges Mills
+17347,Mount Wolf
+17349,New Freedom
+17350,New Oxford
+17352,New Park
+17353,Orrtanna
+17354,Porters Sideling
+17355,Railroad
+17356,Red Lion
+17358,Rossville
+17360,Seven Valleys
+17361,Shrewsbury
+17362,Spring Grove
+17363,Stewartstown
+17364,Thomasville
+17365,Wellsville
+17366,Windsor
+17368,Wrightsville
+17370,York Haven
+17371,York New Salem
+17372,York Springs
+17375,Peach Glen
+17401,York
+17402,York
+17403,York
+17404,York
+17405,York
+17406,York
+17407,York
+17415,York
+17501,Akron
+17502,Bainbridge
+17503,Bart
+17504,Bausman
+17505,Bird in Hand
+17506,Blue Ball
+17507,Bowmansville
+17508,Brownstown
+17509,Christiana
+17512,Columbia
+17516,Conestoga
+17517,Denver
+17518,Drumore
+17519,East Earl
+17520,East Petersburg
+17521,Elm
+17522,Ephrata
+17527,Gap
+17528,Goodville
+17529,Gordonville
+17532,Holtwood
+17533,Hopeland
+17534,Intercourse
+17535,Kinzers
+17536,Kirkwood
+17537,Lampeter
+17538,Landisville
+17540,Leola
+17543,Lititz
+17545,Manheim
+17547,Marietta
+17549,Martindale
+17550,Maytown
+17551,Millersville
+17552,Mount Joy
+17554,Mountville
+17555,Narvon
+17557,New Holland
+17560,New Providence
+17562,Paradise
+17563,Peach Bottom
+17564,Penryn
+17565,Pequea
+17566,Quarryville
+17567,Reamstown
+17568,Refton
+17569,Reinholds
+17570,Rheems
+17572,Ronks
+17573,Ronks
+17575,Silver Spring
+17576,Smoketown
+17577,Soudersburg
+17578,Stevens
+17579,Strasburg
+17580,Talmage
+17581,Terre Hill
+17582,Washington Boro
+17583,West Willow
+17584,Willow Street
+17585,Witmer
+17601,Lancaster
+17602,Lancaster
+17603,Lancaster
+17604,Lancaster
+17605,Lancaster
+17606,Lancaster
+17607,Lancaster
+17608,Lancaster
+17699,Lancaster
+17701,Williamsport
+17702,Williamsport
+17703,Williamsport
+17705,Williamsport
+17720,Antes Fort
+17721,Avis
+17722,Bodines
+17723,Jersey Shore
+17724,Canton
+17726,Castanea
+17727,Cedar Run
+17728,Cogan Station
+17729,Cross Fork
+17730,Dewart
+17731,Eagles Mere
+17735,Grover
+17737,Hughesville
+17738,Hyner
+17739,Jersey Mills
+17740,Jersey Shore
+17742,Lairdsville
+17743,Leroy
+17744,Linden
+17745,Lock Haven
+17747,Loganton
+17748,Mc Elhattan
+17749,Mc Ewensville
+17750,Mackeyville
+17751,Mill Hall
+17752,Montgomery
+17754,Montoursville
+17756,Muncy
+17758,Muncy Valley
+17759,Nisbet
+17760,North Bend
+17762,Picture Rocks
+17763,Ralston
+17764,Renovo
+17765,Roaring Branch
+17767,Salona
+17768,Shunk
+17769,Slate Run
+17771,Trout Run
+17772,Turbotville
+17773,Tylersville
+17774,Unityville
+17776,Waterville
+17777,Watsontown
+17778,Westport
+17779,Woolrich
+17801,Sunbury
+17810,Allenwood
+17812,Beaver Springs
+17813,Beavertown
+17814,Benton
+17815,Bloomsburg
+17820,Catawissa
+17821,Danville
+17822,Danville
+17823,Dornsife
+17824,Elysburg
+17825,Excelsior
+17827,Freeburg
+17828,Gowen City
+17829,Hartleton
+17830,Herndon
+17831,Hummels Wharf
+17832,Marion Heights
+17833,Kreamer
+17834,Kulpmont
+17835,Laurelton
+17836,Leck Kill
+17837,Lewisburg
+17839,Light Street
+17840,Locust Gap
+17841,Mc Clure
+17842,Middleburg
+17843,Beaver Springs
+17844,Mifflinburg
+17845,Millmont
+17846,Millville
+17847,Milton
+17850,Montandon
+17851,Mount Carmel
+17853,Mount Pleasant Mills
+17855,New Berlin
+17856,New Columbia
+17857,Northumberland
+17858,Numidia
+17859,Orangeville
+17860,Paxinos
+17861,Paxtonville
+17862,Penns Creek
+17864,Port Trevorton
+17865,Potts Grove
+17866,Coal Township
+17867,Rebuck
+17868,Riverside
+17870,Selinsgrove
+17872,Shamokin
+17876,Shamokin Dam
+17877,Snydertown
+17878,Stillwater
+17880,Swengel
+17881,Trevorton
+17882,Troxelville
+17883,Vicksburg
+17884,Washingtonville
+17885,Weikert
+17886,West Milton
+17887,White Deer
+17888,Wilburton
+17889,Winfield
+17901,Pottsville
+17920,Aristes
+17921,Ashland
+17922,Auburn
+17923,Branchdale
+17925,Brockton
+17927,Centralia
+17929,Cressona
+17930,Cumbola
+17931,Frackville
+17932,Frackville
+17933,Friedensburg
+17934,Gilberton
+17935,Girardville
+17936,Gordon
+17938,Hegins
+17941,Klingerstown
+17942,Landingville
+17943,Lavelle
+17944,Llewellyn
+17945,Locustdale
+17946,Lost Creek
+17948,Mahanoy City
+17949,Mahanoy Plane
+17951,Mar Lin
+17952,Mary D
+17953,Middleport
+17954,Minersville
+17957,Muir
+17959,New Philadelphia
+17960,New Ringgold
+17961,Orwigsburg
+17963,Pine Grove
+17964,Pitman
+17965,Port Carbon
+17966,Ravine
+17967,Ringtown
+17968,Sacramento
+17970,Saint Clair
+17972,Schuylkill Haven
+17974,Seltzer
+17976,Shenandoah
+17978,Spring Glen
+17979,Summit Station
+17980,Tower City
+17981,Tremont
+17982,Tuscarora
+17983,Valley View
+17985,Zion Grove
+18001,Lehigh Valley
+18002,Lehigh Valley
+18003,Lehigh Valley
+18010,Ackermanville
+18011,Alburtis
+18012,Aquashicola
+18013,Bangor
+18014,Bath
+18015,Bethlehem
+18016,Bethlehem
+18017,Bethlehem
+18018,Bethlehem
+18020,Bethlehem
+18025,Bethlehem
+18030,Bowmanstown
+18031,Breinigsville
+18032,Catasauqua
+18034,Center Valley
+18035,Cherryville
+18036,Coopersburg
+18037,Coplay
+18038,Danielsville
+18039,Durham
+18040,Easton
+18041,East Greenville
+18042,Easton
+18043,Easton
+18044,Easton
+18045,Easton
+18046,East Texas
+18049,Emmaus
+18050,Flicksville
+18051,Fogelsville
+18052,Whitehall
+18053,Germansville
+18054,Green Lane
+18055,Hellertown
+18056,Hereford
+18058,Kunkletown
+18059,Laurys Station
+18060,Limeport
+18062,Macungie
+18063,Martins Creek
+18064,Nazareth
+18065,Neffs
+18066,New Tripoli
+18067,Northampton
+18068,Old Zionsville
+18069,Orefield
+18070,Palm
+18071,Palmerton
+18072,Pen Argyl
+18073,Pennsburg
+18074,Perkiomenville
+18076,Red Hill
+18077,Riegelsville
+18078,Schnecksville
+18079,Slatedale
+18080,Slatington
+18081,Springtown
+18083,Stockertown
+18084,Sumneytown
+18085,Tatamy
+18086,Treichlers
+18087,Trexlertown
+18088,Walnutport
+18091,Wind Gap
+18092,Zionsville
+18098,Emmaus
+18099,Emmaus
+18101,Allentown
+18102,Allentown
+18103,Allentown
+18104,Allentown
+18105,Allentown
+18106,Allentown
+18109,Allentown
+18175,Allentown
+18195,Allentown
+18201,Hazleton
+18210,Albrightsville
+18211,Andreas
+18212,Ashfield
+18214,Barnesville
+18216,Beaver Meadows
+18218,Coaldale
+18219,Conyngham
+18220,Delano
+18221,Drifton
+18222,Drums
+18223,Ebervale
+18224,Freeland
+18225,Harleigh
+18229,Jim Thorpe
+18230,Junedale
+18231,Kelayres
+18232,Lansford
+18234,Lattimer Mines
+18235,Lehighton
+18237,McAdoo
+18239,Milnesville
+18240,Nesquehoning
+18241,Nuremberg
+18242,Oneida
+18243,Pardeesville
+18244,Parryville
+18245,Quakake
+18246,Rock Glen
+18247,Saint Johns
+18248,Sheppton
+18249,Sugarloaf
+18250,Summit Hill
+18251,Sybertsville
+18252,Tamaqua
+18254,Tresckow
+18255,Weatherly
+18256,Weston
+18301,East Stroudsburg
+18320,Analomink
+18321,Bartonsville
+18322,Brodheadsville
+18323,Buck Hill Falls
+18324,Bushkill
+18325,Canadensis
+18326,Cresco
+18327,Delaware Water Gap
+18328,Dingmans Ferry
+18330,Effort
+18331,Gilbert
+18332,Henryville
+18333,Kresgeville
+18334,Long Pond
+18335,Marshalls Creek
+18336,Matamoras
+18337,Milford
+18340,Millrift
+18341,Minisink Hills
+18342,Mountainhome
+18343,Mount Bethel
+18344,Mount Pocono
+18346,Pocono Summit
+18347,Pocono Lake
+18348,Pocono Lake Preserve
+18349,Pocono Manor
+18350,Pocono Pines
+18351,Portland
+18352,Reeders
+18353,Saylorsburg
+18354,Sciota
+18355,Scotrun
+18356,Shawnee on Delaware
+18357,Skytop
+18360,Stroudsburg
+18370,Swiftwater
+18371,Tamiment
+18372,Tannersville
+18373,Unity House
+18401,Aldenville
+18403,Archbald
+18405,Beach Lake
+18407,Carbondale
+18410,Chinchilla
+18411,Clarks Summit
+18413,Clifford
+18414,Dalton
+18415,Damascus
+18416,Elmhurst
+18417,Equinunk
+18419,Factoryville
+18420,Fleetville
+18421,Forest City
+18424,Gouldsboro
+18425,Greeley
+18426,Greentown
+18427,Hamlin
+18428,Hawley
+18430,Herrick Center
+18431,Honesdale
+18433,Jermyn
+18434,Jessup
+18435,Lackawaxen
+18436,Lake Ariel
+18437,Lake Como
+18438,Lakeville
+18439,Lakewood
+18440,La Plume
+18441,Lenoxville
+18443,Milanville
+18444,Moscow
+18445,Newfoundland
+18446,Nicholson
+18447,Olyphant
+18448,Olyphant
+18449,Orson
+18451,Paupack
+18452,Peckville
+18453,Pleasant Mount
+18454,Poyntelle
+18455,Preston Park
+18456,Prompton
+18457,Rowland
+18458,Shohola
+18459,South Canaan
+18460,South Sterling
+18461,Starlight
+18462,Starrucca
+18463,Sterling
+18464,Tafton
+18465,Thompson
+18466,Tobyhanna
+18469,Tyler Hill
+18470,Union Dale
+18471,Waverly
+18472,Waymart
+18473,White Mills
+18501,Scranton
+18502,Scranton
+18503,Scranton
+18504,Scranton
+18505,Scranton
+18507,Moosic
+18508,Scranton
+18509,Scranton
+18510,Scranton
+18512,Scranton
+18514,Scranton
+18515,Scranton
+18517,Taylor
+18518,Old Forge
+18519,Dickson City
+18522,Scranton
+18540,Scranton
+18577,Scranton
+18601,Beach Haven
+18602,Bear Creek
+18603,Berwick
+18610,Blakeslee
+18611,Cambra
+18612,Dallas
+18614,Dushore
+18615,Falls
+18616,Forksville
+18617,Glen Lyon
+18618,Harveys Lake
+18619,Hillsgrove
+18621,Hunlock Creek
+18622,Huntington Mills
+18623,Laceyville
+18624,Lake Harmony
+18625,Lake Winola
+18626,Laporte
+18627,Lehman
+18628,Lopez
+18629,Mehoopany
+18630,Meshoppen
+18631,Mifflinville
+18632,Mildred
+18634,Nanticoke
+18635,Nescopeck
+18636,Noxen
+18640,Pittston
+18641,Pittston
+18642,Duryea
+18643,Pittston
+18644,Wyoming
+18651,Plymouth
+18653,Ransom
+18654,Shawanese
+18655,Shickshinny
+18656,Sweet Valley
+18657,Tunkhannock
+18660,Wapwallopen
+18661,White Haven
+18690,Dallas
+18701,Wilkes Barre
+18702,Wilkes Barre
+18703,Wilkes Barre
+18704,Kingston
+18705,Wilkes Barre
+18706,Wilkes Barre
+18707,Mountain Top
+18708,Shavertown
+18709,Luzerne
+18710,Wilkes Barre
+18711,Wilkes Barre
+18761,Wilkes Barre
+18762,Wilkes Barre
+18763,Wilkes Barre
+18764,Wilkes Barre
+18765,Wilkes Barre
+18766,Wilkes Barre
+18767,Wilkes Barre
+18768,Wilkes Barre
+18769,Wilkes Barre
+18773,Wilkes Barre
+18774,Wilkes Barre
+18801,Montrose
+18810,Athens
+18812,Brackney
+18813,Brooklyn
+18814,Burlington
+18815,Camptown
+18816,Dimock
+18817,East Smithfield
+18818,Friendsville
+18820,Gibson
+18821,Great Bend
+18822,Hallstead
+18823,Harford
+18824,Hop Bottom
+18825,Jackson
+18826,Kingsley
+18827,Lanesboro
+18828,Lawton
+18829,Le Raysville
+18830,Little Meadows
+18831,Milan
+18832,Monroeton
+18833,New Albany
+18834,New Milford
+18837,Rome
+18839,Rushville
+18840,Sayre
+18842,South Gibson
+18843,South Montrose
+18844,Springville
+18845,Stevensville
+18846,Sugar Run
+18847,Susquehanna
+18848,Towanda
+18850,Ulster
+18851,Warren Center
+18853,Wyalusing
+18854,Wysox
+18901,Doylestown
+18910,Bedminster
+18911,Blooming Glen
+18912,Buckingham
+18913,Carversville
+18914,Chalfont
+18915,Colmar
+18916,Danboro
+18917,Dublin
+18918,Earlington
+18920,Erwinna
+18921,Ferndale
+18922,Forest Grove
+18923,Fountainville
+18924,Franconia
+18925,Furlong
+18926,Gardenville
+18927,Hilltown
+18928,Holicong
+18929,Jamison
+18930,Kintnersville
+18931,Lahaska
+18932,Line Lexington
+18933,Lumberville
+18934,Mechanicsville
+18935,Milford Square
+18936,Montgomeryville
+18938,New Hope
+18940,Newtown
+18942,Ottsville
+18943,Penns Park
+18944,Perkasie
+18946,Pineville
+18947,Pipersville
+18949,Plumsteadville
+18950,Point Pleasant
+18951,Quakertown
+18953,Revere
+18954,Richboro
+18955,Richlandtown
+18956,Rushland
+18957,Salford
+18958,Salfordville
+18960,Sellersville
+18962,Silverdale
+18963,Solebury
+18964,Souderton
+18966,Southampton
+18968,Spinnerstown
+18969,Telford
+18970,Trumbauersville
+18971,Tylersport
+18972,Upper Black Eddy
+18974,Warminster
+18976,Warrington
+18977,Washington Crossing
+18979,Woxall
+18980,Wycombe
+18981,Zionhill
+18991,Warminster
+19001,Abington
+19002,Ambler
+19003,Ardmore
+19004,Bala Cynwyd
+19006,Huntingdon Valley
+19007,Bristol
+19008,Broomall
+19009,Bryn Athyn
+19010,Bryn Mawr
+19012,Cheltenham
+19013,Chester
+19014,Aston
+19015,Brookhaven
+19016,Chester
+19017,Chester Heights
+19018,Primos
+19019,Philadelphia
+19020,Bensalem
+19021,Croydon
+19022,Crum Lynne
+19023,Darby
+19025,Dresher
+19026,Drexel Hill
+19027,Elkins Park
+19028,Edgemont
+19029,Essington
+19030,Fairless Hills
+19031,Flourtown
+19032,Folcroft
+19033,Folsom
+19034,Fort Washington
+19035,Gladwyne
+19036,Glenolden
+19037,Glen Riddle Lima
+19038,Glenside
+19039,Gradyville
+19040,Hatboro
+19041,Haverford
+19043,Holmes
+19044,Horsham
+19046,Jenkintown
+19047,Langhorne
+19048,Langhorne
+19049,Langhorne
+19050,Lansdowne
+19052,Lenni
+19053,Feasterville Trevose
+19054,Levittown
+19055,Levittown
+19056,Levittown
+19057,Levittown
+19058,Levittown
+19059,Levittown
+19061,Marcus Hook
+19063,Media
+19064,Springfield
+19065,Media
+19066,Merion Station
+19067,Morrisville
+19070,Morton
+19072,Narberth
+19073,Newtown Square
+19074,Norwood
+19075,Oreland
+19076,Prospect Park
+19078,Ridley Park
+19079,Sharon Hill
+19080,Wayne
+19081,Swarthmore
+19082,Upper Darby
+19083,Havertown
+19085,Villanova
+19086,Wallingford
+19087,Wayne
+19088,Wayne
+19089,Wayne
+19090,Willow Grove
+19091,Media
+19092,Philadelphia
+19093,Philadelphia
+19094,Woodlyn
+19095,Wyncote
+19096,Wynnewood
+19098,Holmes
+19099,Philadelphia
+19101,Philadelphia
+19102,Philadelphia
+19103,Philadelphia
+19104,Philadelphia
+19105,Philadelphia
+19106,Philadelphia
+19107,Philadelphia
+19108,Philadelphia
+19109,Philadelphia
+19110,Philadelphia
+19111,Philadelphia
+19112,Philadelphia
+19113,Philadelphia
+19114,Philadelphia
+19115,Philadelphia
+19116,Philadelphia
+19118,Philadelphia
+19119,Philadelphia
+19120,Philadelphia
+19121,Philadelphia
+19122,Philadelphia
+19123,Philadelphia
+19124,Philadelphia
+19125,Philadelphia
+19126,Philadelphia
+19127,Philadelphia
+19128,Philadelphia
+19129,Philadelphia
+19130,Philadelphia
+19131,Philadelphia
+19132,Philadelphia
+19133,Philadelphia
+19134,Philadelphia
+19135,Philadelphia
+19136,Philadelphia
+19137,Philadelphia
+19138,Philadelphia
+19139,Philadelphia
+19140,Philadelphia
+19141,Philadelphia
+19142,Philadelphia
+19143,Philadelphia
+19144,Philadelphia
+19145,Philadelphia
+19146,Philadelphia
+19147,Philadelphia
+19148,Philadelphia
+19149,Philadelphia
+19150,Philadelphia
+19151,Philadelphia
+19152,Philadelphia
+19153,Philadelphia
+19154,Philadelphia
+19155,Philadelphia
+19160,Philadelphia
+19161,Philadelphia
+19162,Philadelphia
+19170,Philadelphia
+19171,Philadelphia
+19172,Philadelphia
+19173,Philadelphia
+19175,Philadelphia
+19177,Philadelphia
+19178,Philadelphia
+19179,Philadelphia
+19181,Philadelphia
+19182,Philadelphia
+19183,Philadelphia
+19184,Philadelphia
+19185,Philadelphia
+19187,Philadelphia
+19188,Philadelphia
+19191,Philadelphia
+19192,Philadelphia
+19193,Philadelphia
+19194,Philadelphia
+19196,Philadelphia
+19197,Philadelphia
+19244,Philadelphia
+19255,Philadelphia
+19301,Paoli
+19310,Atglen
+19311,Avondale
+19312,Berwyn
+19316,Brandamore
+19317,Chadds Ford
+19318,Chatham
+19319,Cheyney
+19320,Coatesville
+19330,Cochranville
+19331,Concordville
+19333,Devon
+19335,Downingtown
+19339,Concordville
+19340,Concordville
+19341,Exton
+19342,Glen Mills
+19343,Glenmoore
+19344,Honey Brook
+19345,Immaculata
+19346,Kelton
+19347,Kemblesville
+19348,Kennett Square
+19350,Landenberg
+19351,Lewisville
+19352,Lincoln University
+19353,Lionville
+19354,Lyndell
+19355,Malvern
+19357,Mendenhall
+19358,Modena
+19360,New London
+19362,Nottingham
+19363,Oxford
+19365,Parkesburg
+19366,Pocopson
+19367,Pomeroy
+19369,Sadsburyville
+19370,Steelville
+19371,Suplee
+19372,Thorndale
+19373,Thornton
+19374,Toughkenamon
+19375,Unionville
+19376,Wagontown
+19380,West Chester
+19381,West Chester
+19382,West Chester
+19383,West Chester
+19390,West Grove
+19395,Westtown
+19397,Southeastern
+19398,Southeastern
+19399,Southeastern
+19401,Norristown
+19403,Norristown
+19404,Norristown
+19405,Bridgeport
+19406,King of Prussia
+19407,Audubon
+19408,Eagleville
+19409,Fairview Village
+19420,Arcola
+19421,Birchrunville
+19422,Blue Bell
+19423,Cedars
+19424,Blue Bell
+19425,Chester Springs
+19426,Collegeville
+19428,Conshohocken
+19429,Conshohocken
+19430,Creamery
+19432,Devault
+19435,Frederick
+19436,Gwynedd
+19437,Gwynedd Valley
+19438,Harleysville
+19440,Hatfield
+19441,Harleysville
+19442,Kimberton
+19443,Kulpsville
+19444,Lafayette Hill
+19446,Lansdale
+19450,Lederach
+19451,Mainland
+19452,Miquon
+19453,Mont Clare
+19454,North Wales
+19455,North Wales
+19456,Oaks
+19457,Parker Ford
+19460,Phoenixville
+19462,Plymouth Meeting
+19464,Pottstown
+19465,Pottstown
+19468,Royersford
+19470,Saint Peters
+19472,Sassamansville
+19473,Schwenksville
+19474,Skippack
+19475,Spring City
+19477,Spring House
+19478,Spring Mount
+19480,Uwchland
+19481,Valley Forge
+19482,Valley Forge
+19483,Valley Forge
+19484,Valley Forge
+19485,Valley Forge
+19486,West Point
+19487,King of Prussia
+19488,Norristown
+19489,Norristown
+19490,Worcester
+19492,Zieglerville
+19493,Valley Forge
+19494,Valley Forge
+19495,Valley Forge
+19496,Valley Forge
+19501,Adamstown
+19503,Bally
+19504,Barto
+19505,Bechtelsville
+19506,Bernville
+19507,Bethel
+19508,Birdsboro
+19510,Blandon
+19511,Bowers
+19512,Boyertown
+19516,Centerport
+19517,Dauberville
+19518,Douglassville
+19519,Earlville
+19520,Elverson
+19522,Fleetwood
+19523,Geigertown
+19525,Gilbertsville
+19526,Hamburg
+19529,Kempton
+19530,Kutztown
+19533,Leesport
+19534,Lenhartsville
+19535,Limekiln
+19536,Lyon Station
+19538,Maxatawny
+19539,Mertztown
+19540,Mohnton
+19541,Mohrsville
+19542,Monocacy Station
+19543,Morgantown
+19544,Mount Aetna
+19545,New Berlinville
+19547,Oley
+19548,Pine Forge
+19549,Port Clinton
+19550,Rehrersburg
+19551,Robesonia
+19554,Shartlesville
+19555,Shoemakersville
+19557,Stony Run
+19559,Strausstown
+19560,Temple
+19562,Topton
+19564,Virginville
+19565,Wernersville
+19567,Womelsdorf
+19601,Reading
+19602,Reading
+19603,Reading
+19604,Reading
+19605,Reading
+19606,Reading
+19607,Reading
+19608,Reading
+19609,Reading
+19610,Reading
+19611,Reading
+19612,Reading
+19640,Reading
+97001,Antelope
+97002,Aurora
+97004,Beavercreek
+97005,Beaverton
+97006,Beaverton
+97007,Beaverton
+97008,Beaverton
+97009,Boring
+97010,Bridal Veil
+97011,Brightwood
+97013,Canby
+97014,Cascade Locks
+97015,Clackamas
+97016,Clatskanie
+97017,Colton
+97018,Columbia City
+97019,Corbett
+97020,Donald
+97021,Dufur
+97022,Eagle Creek
+97023,Estacada
+97024,Fairview
+97026,Gervais
+97027,Gladstone
+97028,Government Camp
+97029,Grass Valley
+97030,Gresham
+97031,Hood River
+97032,Hubbard
+97033,Kent
+97034,Lake Oswego
+97035,Lake Oswego
+97036,Marylhurst
+97037,Maupin
+97038,Molalla
+97039,Moro
+97040,Mosier
+97041,Mount Hood Parkdale
+97042,Mulino
+97044,Odell
+97045,Oregon City
+97048,Rainier
+97049,Rhododendron
+97050,Rufus
+97051,Saint Helens
+97053,Warren
+97054,Deer Island
+97055,Sandy
+97056,Scappoose
+97057,Shaniko
+97058,The Dalles
+97060,Troutdale
+97062,Tualatin
+97063,Tygh Valley
+97064,Vernonia
+97065,Wasco
+97067,Welches
+97068,West Linn
+97070,Wilsonville
+97071,Woodburn
+97075,Beaverton
+97076,Beaverton
+97077,Beaverton
+97078,Beaverton
+97080,Gresham
+97101,Amity
+97102,Arch Cape
+97103,Astoria
+97106,Banks
+97107,Bay City
+97108,Beaver
+97109,Buxton
+97110,Cannon Beach
+97111,Carlton
+97112,Cloverdale
+97113,Cornelius
+97114,Dayton
+97115,Dundee
+97116,Forest Grove
+97117,Gales Creek
+97118,Garibaldi
+97119,Gaston
+97121,Hammond
+97122,Hebo
+97123,Hillsboro
+97124,Hillsboro
+97125,Manning
+97127,Lafayette
+97128,McMinnville
+97130,Manzanita
+97131,Nehalem
+97132,Newberg
+97133,North Plains
+97134,Oceanside
+97135,Pacific City
+97136,Rockaway Beach
+97137,Saint Paul
+97138,Seaside
+97140,Sherwood
+97141,Tillamook
+97143,Netarts
+97144,Timber
+97145,Tolovana Park
+97146,Warrenton
+97147,Wheeler
+97148,Yamhill
+97149,Neskowin
+97201,Portland
+97202,Portland
+97203,Portland
+97204,Portland
+97205,Portland
+97206,Portland
+97207,Portland
+97208,Portland
+97209,Portland
+97210,Portland
+97211,Portland
+97212,Portland
+97213,Portland
+97214,Portland
+97215,Portland
+97216,Portland
+97217,Portland
+97218,Portland
+97219,Portland
+97220,Portland
+97221,Portland
+97222,Portland
+97223,Portland
+97224,Portland
+97225,Portland
+97227,Portland
+97228,Portland
+97229,Portland
+97230,Portland
+97231,Portland
+97232,Portland
+97233,Portland
+97236,Portland
+97238,Portland
+97240,Portland
+97242,Portland
+97251,Portland
+97253,Portland
+97254,Portland
+97255,Portland
+97256,Portland
+97258,Portland
+97259,Portland
+97266,Portland
+97267,Portland
+97268,Portland
+97269,Portland
+97271,Portland
+97272,Portland
+97280,Portland
+97281,Portland
+97282,Portland
+97283,Portland
+97286,Portland
+97290,Portland
+97291,Portland
+97292,Portland
+97293,Portland
+97294,Portland
+97296,Portland
+97298,Portland
+97299,Portland
+97301,Salem
+97302,Salem
+97303,Salem
+97304,Salem
+97305,Salem
+97306,Salem
+97307,Keizer
+97308,Salem
+97309,Salem
+97310,Salem
+97311,Salem
+97312,Salem
+97313,Salem
+97314,Salem
+97321,Albany
+97324,Alsea
+97325,Aumsville
+97326,Blodgett
+97327,Brownsville
+97329,Cascadia
+97330,Corvallis
+97331,Corvallis
+97333,Corvallis
+97335,Crabtree
+97336,Crawfordsville
+97338,Dallas
+97339,Corvallis
+97341,Depoe Bay
+97342,Detroit
+97343,Eddyville
+97344,Falls City
+97345,Foster
+97346,Gates
+97347,Grand Ronde
+97348,Halsey
+97350,Idanha
+97351,Independence
+97352,Jefferson
+97355,Lebanon
+97357,Logsden
+97358,Lyons
+97359,Marion
+97360,Mill City
+97361,Monmouth
+97362,Mount Angel
+97364,Neotsu
+97365,Newport
+97366,South Beach
+97367,Lincoln City
+97368,Otis
+97369,Otter Rock
+97370,Philomath
+97371,Rickreall
+97372,Rose Lodge
+97373,Saint Benedict
+97374,Scio
+97375,Scotts Mills
+97376,Seal Rock
+97377,Shedd
+97378,Sheridan
+97380,Siletz
+97381,Silverton
+97383,Stayton
+97384,Mehama
+97385,Sublimity
+97386,Sweet Home
+97388,Gleneden Beach
+97389,Tangent
+97390,Tidewater
+97391,Toledo
+97392,Turner
+97394,Waldport
+97396,Willamina
+97401,Eugene
+97402,Eugene
+97403,Eugene
+97404,Eugene
+97405,Eugene
+97406,Agness
+97407,Allegany
+97408,Eugene
+97409,Alvadore
+97410,Azalea
+97411,Bandon
+97412,Blachly
+97413,Blue River
+97414,Broadbent
+97415,Brookings
+97416,Camas Valley
+97417,Canyonville
+97419,Cheshire
+97420,Coos Bay
+97423,Coquille
+97424,Cottage Grove
+97425,Crescent Lake
+97426,Creswell
+97427,Culp Creek
+97428,Curtin
+97429,Days Creek
+97430,Deadwood
+97431,Dexter
+97432,Dillard
+97434,Dorena
+97435,Drain
+97436,Elkton
+97437,Elmira
+97438,Fall Creek
+97439,Florence
+97440,Eugene
+97441,Gardiner
+97442,Glendale
+97443,Glide
+97444,Gold Beach
+97446,Harrisburg
+97447,Idleyld Park
+97448,Junction City
+97449,Lakeside
+97450,Langlois
+97451,Lorane
+97452,Lowell
+97453,Mapleton
+97454,Marcola
+97455,Pleasant Hill
+97456,Monroe
+97457,Myrtle Creek
+97458,Myrtle Point
+97459,North Bend
+97460,Norway
+97461,Noti
+97462,Oakland
+97463,Oakridge
+97464,Ophir
+97465,Port Orford
+97466,Powers
+97467,Reedsport
+97468,Remote
+97469,Riddle
+97470,Roseburg
+97472,Saginaw
+97473,Scottsburg
+97476,Sixes
+97477,Springfield
+97478,Springfield
+97479,Sutherlin
+97480,Swisshome
+97481,Tenmile
+97482,Thurston
+97484,Tiller
+97486,Umpqua
+97487,Veneta
+97488,Vida
+97489,Walterville
+97490,Walton
+97491,Wedderburn
+97492,Westfir
+97493,Westlake
+97494,Wilbur
+97495,Winchester
+97496,Winston
+97497,Wolf Creek
+97498,Yachats
+97499,Yoncalla
+97501,Medford
+97502,Central Point
+97503,White City
+97504,Medford
+97520,Ashland
+97522,Butte Falls
+97523,Cave Junction
+97524,Eagle Point
+97525,Gold Hill
+97526,Grants Pass
+97527,Grants Pass
+97528,Grants Pass
+97530,Jacksonville
+97531,Kerby
+97532,Merlin
+97533,Murphy
+97534,O Brien
+97535,Phoenix
+97536,Prospect
+97537,Rogue River
+97538,Selma
+97539,Shady Cove
+97540,Talent
+97541,Trail
+97543,Wilderville
+97544,Williams
+97601,Klamath Falls
+97602,Klamath Falls
+97603,Klamath Falls
+97604,Crater Lake
+97620,Adel
+97621,Beatty
+97622,Bly
+97623,Bonanza
+97624,Chiloquin
+97625,Dairy
+97626,Fort Klamath
+97627,Keno
+97630,Lakeview
+97632,Malin
+97633,Merrill
+97634,Midland
+97635,New Pine Creek
+97636,Paisley
+97637,Plush
+97638,Silver Lake
+97639,Sprague River
+97640,Summer Lake
+97641,Christmas Valley
+97701,Bend
+97702,Bend
+97707,Bend
+97708,Bend
+97709,Bend
+97710,Fields
+97711,Ashwood
+97712,Brothers
+97720,Burns
+97721,Princeton
+97722,Diamond
+97730,Camp Sherman
+97731,Chemult
+97732,Crane
+97733,Crescent
+97734,Culver
+97735,Fort Rock
+97736,Frenchglen
+97737,Gilchrist
+97738,Hines
+97739,La Pine
+97740,Lawen
+97741,Madras
+97750,Mitchell
+97751,Paulina
+97752,Post
+97753,Powell Butte
+97754,Prineville
+97756,Redmond
+97758,Riley
+97759,Sisters
+97760,Terrebonne
+97761,Warm Springs
+97801,Pendleton
+97810,Adams
+97812,Arlington
+97813,Athena
+97814,Baker City
+97817,Bates
+97818,Boardman
+97819,Bridgeport
+97820,Canyon City
+97821,Cayuse
+97823,Condon
+97824,Cove
+97825,Dayville
+97826,Echo
+97827,Elgin
+97828,Enterprise
+97830,Fossil
+97831,Fox
+97833,Haines
+97834,Halfway
+97835,Helix
+97836,Heppner
+97837,Hereford
+97838,Hermiston
+97839,Lexington
+97840,Oxbow
+97841,Imbler
+97842,Imnaha
+97843,Ione
+97844,Irrigon
+97845,John Day
+97846,Joseph
+97848,Kimberly
+97850,La Grande
+97856,Long Creek
+97857,Lostine
+97859,Meacham
+97861,Mikkalo
+97862,Milton Freewater
+97864,Monument
+97865,Mount Vernon
+97867,North Powder
+97868,Pilot Rock
+97869,Prairie City
+97870,Richland
+97872,Ritter
+97873,Seneca
+97874,Spray
+97875,Stanfield
+97876,Summerville
+97877,Sumpter
+97880,Ukiah
+97882,Umatilla
+97883,Union
+97884,Unity
+97885,Wallowa
+97886,Weston
+97901,Adrian
+97902,Arock
+97903,Brogan
+97904,Drewsey
+97905,Durkee
+97906,Harper
+97907,Huntington
+97908,Ironside
+97909,Jamieson
+97910,Jordan Valley
+97911,Juntura
+97913,Nyssa
+97914,Ontario
+97917,Riverside
+97918,Vale
+97920,Westfall
+73001,Albert
+73002,Alex
+73003,Edmond
+73004,Amber
+73005,Anadarko
+73006,Apache
+73007,Arcadia
+73008,Bethany
+73009,Binger
+73010,Blanchard
+73011,Bradley
+73012,Bray
+73013,Edmond
+73014,Calumet
+73015,Carnegie
+73016,Cashion
+73017,Cement
+73018,Chickasha
+73019,Norman
+73020,Choctaw
+73021,Colony
+73022,Concho
+73023,Chickasha
+73024,Corn
+73026,Norman
+73027,Coyle
+73028,Crescent
+73029,Cyril
+73030,Davis
+73031,Dibble
+73032,Dougherty
+73033,Eakly
+73034,Edmond
+73036,El Reno
+73038,Fort Cobb
+73040,Geary
+73041,Gotebo
+73042,Gracemont
+73043,Greenfield
+73044,Guthrie
+73045,Harrah
+73047,Hinton
+73048,Hydro
+73049,Jones
+73050,Langston
+73051,Lexington
+73052,Lindsay
+73053,Lookeba
+73054,Luther
+73055,Marlow
+73056,Marshall
+73057,Maysville
+73058,Meridian
+73059,Minco
+73061,Morrison
+73062,Mountain View
+73063,Mulhall
+73064,Mustang
+73065,Newcastle
+73066,Nicoma Park
+73067,Ninnekah
+73068,Noble
+73069,Norman
+73070,Norman
+73071,Norman
+73072,Norman
+73073,Orlando
+73074,Paoli
+73075,Pauls Valley
+73077,Perry
+73078,Piedmont
+73079,Pocasset
+73080,Purcell
+73082,Rush Springs
+73083,Edmond
+73084,Spencer
+73085,Yukon
+73086,Sulphur
+73089,Tuttle
+73090,Union City
+73092,Verden
+73093,Washington
+73094,Washita
+73095,Wayne
+73096,Weatherford
+73097,Wheatland
+73098,Wynnewood
+73099,Yukon
+73101,Oklahoma City
+73102,Oklahoma City
+73103,Oklahoma City
+73104,Oklahoma City
+73105,Oklahoma City
+73106,Oklahoma City
+73107,Oklahoma City
+73108,Oklahoma City
+73109,Oklahoma City
+73110,Oklahoma City
+73111,Oklahoma City
+73112,Oklahoma City
+73113,Oklahoma City
+73114,Oklahoma City
+73115,Oklahoma City
+73116,Oklahoma City
+73117,Oklahoma City
+73118,Oklahoma City
+73119,Oklahoma City
+73120,Oklahoma City
+73121,Oklahoma City
+73122,Oklahoma City
+73123,Oklahoma City
+73124,Oklahoma City
+73125,Oklahoma City
+73126,Oklahoma City
+73127,Oklahoma City
+73128,Oklahoma City
+73129,Oklahoma City
+73130,Oklahoma City
+73131,Oklahoma City
+73132,Oklahoma City
+73134,Oklahoma City
+73135,Oklahoma City
+73136,Oklahoma City
+73137,Oklahoma City
+73139,Oklahoma City
+73140,Oklahoma City
+73141,Oklahoma City
+73142,Oklahoma City
+73143,Oklahoma City
+73144,Oklahoma City
+73145,Oklahoma City
+73146,Oklahoma City
+73147,Oklahoma City
+73148,Oklahoma City
+73149,Oklahoma City
+73150,Oklahoma City
+73151,Oklahoma City
+73152,Oklahoma City
+73153,Oklahoma City
+73154,Oklahoma City
+73155,Oklahoma City
+73156,Oklahoma City
+73157,Oklahoma City
+73159,Oklahoma City
+73160,Oklahoma City
+73162,Oklahoma City
+73163,Oklahoma City
+73164,Oklahoma City
+73165,Oklahoma City
+73167,Oklahoma City
+73169,Oklahoma City
+73170,Oklahoma City
+73172,Oklahoma City
+73173,Oklahoma City
+73177,Oklahoma City
+73178,Oklahoma City
+73179,Oklahoma City
+73180,Oklahoma City
+73184,Oklahoma City
+73185,Oklahoma City
+73189,Oklahoma City
+73190,Oklahoma City
+73193,Oklahoma City
+73194,Oklahoma City
+73195,Oklahoma City
+73196,Oklahoma City
+73197,Oklahoma City
+73198,Oklahoma City
+73199,Oklahoma City
+73401,Ardmore
+73402,Ardmore
+73403,Ardmore
+73425,Countyline
+73430,Burneyville
+73432,Coleman
+73433,Elmore City
+73434,Foster
+73435,Fox
+73436,Gene Autry
+73437,Graham
+73438,Healdton
+73439,Kingston
+73440,Lebanon
+73441,Leon
+73442,Loco
+73443,Lone Grove
+73444,Hennepin
+73446,Madill
+73447,Mannsville
+73448,Marietta
+73449,Mead
+73450,Milburn
+73453,Overbrook
+73455,Ravia
+73456,Ringling
+73458,Springer
+73459,Thackerville
+73460,Tishomingo
+73461,Wapanucka
+73463,Wilson
+73476,Pernell
+73481,Ratliff City
+73487,Tatums
+73488,Tussy
+73491,Velma
+73501,Lawton
+73502,Lawton
+73503,Fort Sill
+73505,Lawton
+73506,Lawton
+73507,Lawton
+73520,Addington
+73521,Altus
+73522,Altus
+73523,Altus AFB
+73526,Blair
+73527,Cache
+73528,Chattanooga
+73529,Comanche
+73530,Davidson
+73531,Devol
+73532,Duke
+73533,Duncan
+73534,Duncan
+73536,Duncan
+73537,Eldorado
+73538,Elgin
+73539,Elmer
+73540,Faxon
+73541,Fletcher
+73542,Frederick
+73543,Geronimo
+73544,Gould
+73546,Grandfield
+73547,Granite
+73548,Hastings
+73549,Headrick
+73550,Hollis
+73551,Hollister
+73552,Indiahoma
+73553,Loveland
+73554,Mangum
+73555,Manitou
+73556,Martha
+73557,Medicine Park
+73558,Meers
+73559,Mountain Park
+73560,Olustee
+73561,Oscar
+73562,Randlett
+73564,Roosevelt
+73565,Ryan
+73566,Snyder
+73567,Sterling
+73568,Temple
+73569,Terral
+73570,Tipton
+73571,Vinson
+73572,Walters
+73573,Waurika
+73575,Duncan
+73601,Clinton
+73620,Arapaho
+73622,Bessie
+73624,Burns Flat
+73625,Butler
+73626,Canute
+73627,Carter
+73628,Cheyenne
+73632,Cordell
+73638,Crawford
+73639,Custer City
+73641,Dill City
+73642,Durham
+73644,Elk City
+73645,Erick
+73646,Fay
+73647,Foss
+73648,Elk City
+73650,Hammon
+73651,Hobart
+73654,Leedey
+73655,Lone Wolf
+73656,Mayfield
+73658,Oakwood
+73659,Putnam
+73660,Reydon
+73661,Rocky
+73662,Sayre
+73663,Seiling
+73664,Sentinel
+73666,Sweetwater
+73667,Taloga
+73668,Texola
+73669,Thomas
+73673,Willow
+73701,Enid
+73702,Enid
+73703,Enid
+73705,Enid
+73706,Enid
+73716,Aline
+73717,Alva
+73718,Ames
+73719,Amorita
+73720,Bison
+73722,Burlington
+73724,Canton
+73726,Carmen
+73727,Carrier
+73728,Cherokee
+73729,Cleo Springs
+73730,Covington
+73731,Dacoma
+73733,Douglas
+73734,Dover
+73735,Drummond
+73736,Fairmont
+73737,Fairview
+73738,Garber
+73739,Goltry
+73741,Helena
+73742,Hennessey
+73743,Hillsdale
+73744,Hitchcock
+73746,Hopeton
+73747,Isabella
+73749,Jet
+73750,Kingfisher
+73753,Kremlin
+73754,Lahoma
+73755,Longdale
+73756,Loyal
+73757,Lucien
+73758,Manchester
+73759,Medford
+73760,Meno
+73761,Nash
+73762,Okarche
+73763,Okeene
+73764,Omega
+73766,Pond Creek
+73768,Ringwood
+73770,Southard
+73771,Wakita
+73772,Watonga
+73773,Waukomis
+73801,Woodward
+73802,Woodward
+73832,Arnett
+73834,Buffalo
+73835,Camargo
+73838,Chester
+73840,Fargo
+73841,Fort Supply
+73842,Freedom
+73843,Gage
+73844,Gate
+73847,Knowles
+73848,Laverne
+73851,May
+73852,Mooreland
+73853,Mutual
+73855,Rosston
+73857,Sharon
+73858,Shattuck
+73859,Vici
+73860,Waynoka
+73901,Adams
+73931,Balko
+73932,Beaver
+73933,Boise City
+73937,Felt
+73938,Forgan
+73939,Goodwell
+73942,Guymon
+73944,Hardesty
+73945,Hooker
+73946,Kenton
+73947,Keyes
+73949,Texhoma
+73950,Turpin
+73951,Tyrone
+74001,Avant
+74002,Barnsdall
+74003,Bartlesville
+74004,Bartlesville
+74005,Bartlesville
+74006,Bartlesville
+74008,Bixby
+74009,Bowring
+74010,Bristow
+74011,Broken Arrow
+74012,Broken Arrow
+74013,Broken Arrow
+74014,Broken Arrow
+74015,Catoosa
+74016,Chelsea
+74017,Claremore
+74018,Claremore
+74020,Cleveland
+74021,Collinsville
+74022,Copan
+74023,Cushing
+74026,Davenport
+74027,Delaware
+74028,Depew
+74029,Dewey
+74030,Drumright
+74031,Foyil
+74032,Glencoe
+74033,Glenpool
+74034,Hallett
+74035,Hominy
+74036,Inola
+74037,Jenks
+74038,Jennings
+74039,Kellyville
+74041,Kiefer
+74042,Lenapah
+74043,Leonard
+74044,Mannford
+74045,Maramec
+74046,Milfay
+74047,Mounds
+74048,Nowata
+74050,Oakhurst
+74051,Ochelata
+74052,Oilton
+74053,Oologah
+74054,Osage
+74055,Owasso
+74056,Pawhuska
+74058,Pawnee
+74059,Perkins
+74060,Prue
+74061,Ramona
+74062,Ripley
+74063,Sand Springs
+74066,Sapulpa
+74067,Sapulpa
+74068,Shamrock
+74070,Skiatook
+74071,Slick
+74072,S Coffeyville
+74073,Sperry
+74074,Stillwater
+74075,Stillwater
+74076,Stillwater
+74077,Stillwater
+74078,Stillwater
+74079,Stroud
+74080,Talala
+74081,Terlton
+74082,Vera
+74083,Wann
+74084,Wynona
+74085,Yale
+74101,Tulsa
+74102,Tulsa
+74103,Tulsa
+74104,Tulsa
+74105,Tulsa
+74106,Tulsa
+74107,Tulsa
+74108,Tulsa
+74110,Tulsa
+74112,Tulsa
+74114,Tulsa
+74115,Tulsa
+74116,Tulsa
+74117,Tulsa
+74119,Tulsa
+74120,Tulsa
+74121,Tulsa
+74126,Tulsa
+74127,Tulsa
+74128,Tulsa
+74129,Tulsa
+74130,Tulsa
+74131,Tulsa
+74132,Tulsa
+74133,Tulsa
+74134,Tulsa
+74135,Tulsa
+74136,Tulsa
+74137,Tulsa
+74141,Tulsa
+74145,Tulsa
+74146,Tulsa
+74147,Tulsa
+74148,Tulsa
+74149,Tulsa
+74150,Tulsa
+74152,Tulsa
+74153,Tulsa
+74155,Tulsa
+74156,Tulsa
+74157,Tulsa
+74158,Tulsa
+74159,Tulsa
+74169,Tulsa
+74170,Tulsa
+74171,Tulsa
+74172,Tulsa
+74182,Tulsa
+74183,Tulsa
+74184,Tulsa
+74186,Tulsa
+74187,Tulsa
+74189,Tulsa
+74192,Tulsa
+74193,Tulsa
+74194,Tulsa
+74301,Vinita
+74330,Adair
+74331,Afton
+74332,Big Cabin
+74333,Bluejacket
+74335,Cardin
+74337,Chouteau
+74338,Colcord
+74339,Commerce
+74340,Disney
+74342,Eucha
+74343,Fairland
+74344,Grove
+74345,Grove
+74346,Jay
+74347,Kansas
+74349,Ketchum
+74350,Langley
+74352,Locust Grove
+74353,Mazie
+74354,Miami
+74355,Miami
+74358,North Miami
+74359,Oaks
+74360,Picher
+74361,Pryor
+74362,Pryor
+74363,Quapaw
+74364,Rose
+74365,Salina
+74366,Spavinaw
+74367,Strang
+74368,Twin Oaks
+74369,Welch
+74370,Wyandotte
+74401,Muskogee
+74402,Muskogee
+74403,Muskogee
+74421,Beggs
+74422,Boynton
+74423,Braggs
+74425,Canadian
+74426,Checotah
+74427,Cookson
+74428,Council Hill
+74429,Coweta
+74430,Crowder
+74431,Dewar
+74432,Eufaula
+74434,Fort Gibson
+74435,Gore
+74436,Haskell
+74437,Henryetta
+74438,Hitchita
+74440,Hoyt
+74441,Hulbert
+74442,Indianola
+74444,Moodys
+74445,Morris
+74446,Okay
+74447,Okmulgee
+74450,Oktaha
+74451,Park Hill
+74452,Peggs
+74454,Porter
+74455,Porum
+74456,Preston
+74457,Proctor
+74458,Redbird
+74459,Rentiesville
+74460,Schulter
+74461,Stidham
+74462,Stigler
+74463,Taft
+74464,Tahlequah
+74465,Tahlequah
+74466,Tullahassee
+74467,Wagoner
+74468,Wainwright
+74469,Warner
+74470,Webbers Falls
+74471,Welling
+74472,Whitefield
+74477,Wagoner
+74501,McAlester
+74502,McAlester
+74521,Albion
+74522,Alderson
+74523,Antlers
+74525,Atoka
+74528,Blanco
+74529,Blocker
+74530,Bromide
+74531,Calvin
+74533,Caney
+74534,Centrahoma
+74535,Clarita
+74536,Clayton
+74538,Coalgate
+74540,Daisy
+74542,Atoka
+74543,Finley
+74545,Gowen
+74546,Haileyville
+74547,Hartshorne
+74549,Honobia
+74552,Kinta
+74553,Kiowa
+74554,Krebs
+74555,Lane
+74556,Lehigh
+74557,Moyers
+74558,Nashoba
+74559,Panola
+74560,Pittsburg
+74561,Quinton
+74562,Rattan
+74563,Red Oak
+74565,Savanna
+74567,Snow
+74569,Stringtown
+74570,Stuart
+74571,Talihina
+74572,Tupelo
+74574,Tuskahoma
+74576,Wardville
+74577,Whitesboro
+74578,Wilburton
+74601,Ponca City
+74602,Ponca City
+74603,Ponca City
+74604,Ponca City
+74630,Billings
+74631,Blackwell
+74632,Braman
+74633,Burbank
+74636,Deer Creek
+74637,Fairfax
+74640,Hunter
+74641,Kaw City
+74643,Lamont
+74644,Marland
+74646,Nardin
+74647,Newkirk
+74650,Ralston
+74651,Red Rock
+74652,Shidler
+74653,Tonkawa
+74701,Durant
+74702,Durant
+74720,Achille
+74721,Albany
+74722,Battiest
+74723,Bennington
+74724,Bethel
+74726,Bokchito
+74727,Boswell
+74728,Broken Bow
+74729,Caddo
+74730,Calera
+74731,Cartwright
+74733,Colbert
+74734,Eagletown
+74735,Fort Towson
+74736,Garvin
+74737,Golden
+74738,Grant
+74740,Haworth
+74741,Hendrix
+74743,Hugo
+74745,Idabel
+74747,Kemp
+74748,Kenefic
+74750,Millerton
+74752,Pickens
+74753,Platter
+74754,Ringold
+74755,Rufe
+74756,Sawyer
+74759,Soper
+74760,Spencerville
+74761,Swink
+74764,Valliant
+74766,Wright City
+74801,Shawnee
+74802,Shawnee
+74804,Shawnee
+74818,Seminole
+74820,Ada
+74821,Ada
+74824,Agra
+74825,Allen
+74826,Asher
+74827,Atwood
+74829,Boley
+74830,Bowlegs
+74831,Byars
+74832,Carney
+74833,Castle
+74834,Chandler
+74836,Connerville
+74837,Cromwell
+74839,Dustin
+74840,Earlsboro
+74842,Fittstown
+74843,Fitzhugh
+74844,Francis
+74845,Hanna
+74848,Holdenville
+74849,Konawa
+74850,Lamar
+74851,McLoud
+74852,Macomb
+74854,Maud
+74855,Meeker
+74856,Mill Creek
+74857,Newalla
+74859,Okemah
+74860,Paden
+74864,Prague
+74865,Roff
+74866,Saint Louis
+74867,Sasakwa
+74868,Seminole
+74869,Sparks
+74871,Stonewall
+74872,Stratford
+74873,Tecumseh
+74875,Tryon
+74878,Wanette
+74880,Weleetka
+74881,Wellston
+74883,Wetumka
+74884,Wewoka
+74901,Arkoma
+74902,Pocola
+74930,Bokoshe
+74931,Bunch
+74932,Cameron
+74935,Fanshawe
+74936,Gans
+74937,Heavener
+74939,Hodgen
+74940,Howe
+74941,Keota
+74942,Leflore
+74943,Lequire
+74944,McCurtain
+74945,Marble City
+74946,Moffett
+74947,Monroe
+74948,Muldrow
+74949,Muse
+74951,Panama
+74953,Poteau
+74954,Roland
+74955,Sallisaw
+74956,Shady Point
+74957,Smithville
+74959,Spiro
+74960,Stilwell
+74962,Vian
+74963,Watson
+74964,Watts
+74965,Westville
+74966,Wister
+43001,Alexandria
+43002,Amlin
+43003,Ashley
+43004,Blacklick
+43005,Bladensburg
+43006,Brinkhaven
+43007,Broadway
+43008,Buckeye Lake
+43009,Cable
+43010,Catawba
+43011,Centerburg
+43013,Croton
+43014,Danville
+43015,Delaware
+43016,Dublin
+43017,Dublin
+43018,Etna
+43019,Fredericktown
+43021,Galena
+43022,Gambier
+43023,Granville
+43025,Hebron
+43026,Hilliard
+43027,Homer
+43028,Howard
+43029,Irwin
+43030,Jacksontown
+43031,Johnstown
+43032,Kilbourne
+43033,Kirkersville
+43035,Lewis Center
+43036,Magnetic Springs
+43037,Martinsburg
+43040,Marysville
+43041,Marysville
+43044,Mechanicsburg
+43045,Milford Center
+43046,Millersport
+43047,Mingo
+43048,Mount Liberty
+43050,Mount Vernon
+43054,New Albany
+43055,Newark
+43056,Heath
+43058,Newark
+43060,North Lewisburg
+43061,Ostrander
+43062,Pataskala
+43064,Plain City
+43065,Powell
+43066,Radnor
+43067,Raymond
+43068,Reynoldsburg
+43070,Rosewood
+43071,Saint Louisville
+43072,Saint Paris
+43073,Summit Station
+43074,Sunbury
+43076,Thornville
+43077,Unionville Center
+43078,Urbana
+43080,Utica
+43081,Westerville
+43082,Westerville
+43083,Westville
+43084,Woodstock
+43085,Columbus
+43086,Westerville
+43093,Newark
+43098,Hebron
+43101,Adelphi
+43102,Amanda
+43103,Ashville
+43105,Baltimore
+43106,Bloomingburg
+43107,Bremen
+43109,Brice
+43110,Canal Winchester
+43111,Carbon Hill
+43112,Carroll
+43113,Circleville
+43115,Clarksburg
+43116,Commercial Point
+43117,Derby
+43119,Galloway
+43123,Grove City
+43125,Groveport
+43126,Harrisburg
+43127,Haydenville
+43128,Jeffersonville
+43130,Lancaster
+43135,Laurelville
+43136,Lithopolis
+43137,Lockbourne
+43138,Logan
+43140,London
+43142,Milledgeville
+43143,Mount Sterling
+43144,Murray City
+43145,New Holland
+43146,Orient
+43147,Pickerington
+43148,Pleasantville
+43149,Rockbridge
+43150,Rushville
+43151,Sedalia
+43152,South Bloomingville
+43153,South Solon
+43154,Stoutsville
+43155,Sugar Grove
+43156,Tarlton
+43157,Thurston
+43158,Union Furnace
+43160,Washington Court House
+43162,West Jefferson
+43163,West Rushville
+43164,Williamsport
+43199,Groveport
+43201,Columbus
+43202,Columbus
+43203,Columbus
+43204,Columbus
+43205,Columbus
+43206,Columbus
+43207,Columbus
+43209,Columbus
+43210,Columbus
+43211,Columbus
+43212,Columbus
+43213,Columbus
+43214,Columbus
+43215,Columbus
+43216,Columbus
+43217,Columbus
+43218,Columbus
+43219,Columbus
+43220,Columbus
+43221,Columbus
+43222,Columbus
+43223,Columbus
+43224,Columbus
+43226,Columbus
+43227,Columbus
+43228,Columbus
+43229,Columbus
+43230,Columbus
+43231,Columbus
+43232,Columbus
+43234,Columbus
+43235,Columbus
+43236,Columbus
+43240,Columbus
+43251,Columbus
+43260,Columbus
+43265,Columbus
+43266,Columbus
+43268,Columbus
+43270,Columbus
+43271,Columbus
+43272,Columbus
+43279,Columbus
+43284,Columbus
+43287,Columbus
+43291,Columbus
+43299,Columbus
+43301,Marion
+43302,Marion
+43306,Marion
+43307,Marion
+43310,Belle Center
+43311,Bellefontaine
+43314,Caledonia
+43315,Cardington
+43316,Carey
+43317,Chesterville
+43318,De Graff
+43319,East Liberty
+43320,Edison
+43321,Fulton
+43322,Green Camp
+43323,Harpster
+43324,Huntsville
+43325,Iberia
+43326,Kenton
+43330,Kirby
+43331,Lakeview
+43332,La Rue
+43333,Lewistown
+43334,Marengo
+43335,Martel
+43336,Middleburg
+43337,Morral
+43338,Mount Gilead
+43340,Mount Victory
+43341,New Bloomington
+43342,Prospect
+43343,Quincy
+43344,Richwood
+43345,Ridgeway
+43346,Roundhead
+43347,Rushsylvania
+43348,Russells Point
+43349,Shauck
+43350,Sparta
+43351,Upper Sandusky
+43356,Waldo
+43357,West Liberty
+43358,West Mansfield
+43359,Wharton
+43360,Zanesfield
+43402,Bowling Green
+43403,Bowling Green
+43406,Bradner
+43407,Burgoon
+43408,Clay Center
+43410,Clyde
+43412,Curtice
+43413,Cygnet
+43414,Dunbridge
+43416,Elmore
+43420,Fremont
+43430,Genoa
+43431,Gibsonburg
+43432,Graytown
+43433,Gypsum
+43434,Harbor View
+43435,Helena
+43436,Isle Saint George
+43437,Jerry City
+43438,Kelleys Island
+43439,Lacarne
+43440,Lakeside Marblehead
+43441,Lemoyne
+43442,Lindsey
+43443,Luckey
+43445,Martin
+43446,Middle Bass
+43447,Millbury
+43449,Oak Harbor
+43450,Pemberville
+43451,Portage
+43452,Port Clinton
+43456,Put in Bay
+43457,Risingsun
+43458,Rocky Ridge
+43460,Rossford
+43462,Rudolph
+43463,Stony Ridge
+43464,Vickery
+43465,Walbridge
+43466,Wayne
+43467,West Millgrove
+43468,Williston
+43469,Woodville
+43501,Alvordton
+43502,Archbold
+43504,Berkey
+43505,Blakeslee
+43506,Bryan
+43510,Colton
+43511,Custar
+43512,Defiance
+43515,Delta
+43516,Deshler
+43517,Edgerton
+43518,Edon
+43519,Evansport
+43520,Farmer
+43521,Fayette
+43522,Grand Rapids
+43523,Grelton
+43524,Hamler
+43525,Haskins
+43526,Hicksville
+43527,Holgate
+43528,Holland
+43529,Hoytville
+43530,Jewell
+43531,Kunkle
+43532,Liberty Center
+43533,Lyons
+43534,Mc Clure
+43535,Malinta
+43536,Mark Center
+43537,Maumee
+43540,Metamora
+43541,Milton Center
+43542,Monclova
+43543,Montpelier
+43545,Napoleon
+43547,Neapolis
+43548,New Bavaria
+43549,Ney
+43550,Okolona
+43551,Perrysburg
+43552,Perrysburg
+43553,Pettisville
+43554,Pioneer
+43555,Ridgeville Corners
+43556,Sherwood
+43557,Stryker
+43558,Swanton
+43560,Sylvania
+43565,Tontogany
+43566,Waterville
+43567,Wauseon
+43569,Weston
+43570,West Unity
+43571,Whitehouse
+43601,Toledo
+43602,Toledo
+43603,Toledo
+43604,Toledo
+43605,Toledo
+43606,Toledo
+43607,Toledo
+43608,Toledo
+43609,Toledo
+43610,Toledo
+43611,Toledo
+43612,Toledo
+43613,Toledo
+43614,Toledo
+43615,Toledo
+43616,Oregon
+43617,Toledo
+43618,Oregon
+43619,Northwood
+43620,Toledo
+43623,Toledo
+43624,Toledo
+43635,Toledo
+43652,Toledo
+43653,Toledo
+43654,Toledo
+43655,Toledo
+43656,Toledo
+43657,Toledo
+43659,Toledo
+43660,Toledo
+43661,Toledo
+43666,Toledo
+43667,Toledo
+43681,Toledo
+43682,Toledo
+43697,Toledo
+43699,Toledo
+43701,Zanesville
+43702,Zanesville
+43711,Ava
+43713,Barnesville
+43716,Beallsville
+43717,Belle Valley
+43718,Belmont
+43719,Bethesda
+43720,Blue Rock
+43721,Brownsville
+43722,Buffalo
+43723,Byesville
+43724,Caldwell
+43725,Cambridge
+43727,Chandlersville
+43728,Chesterhill
+43730,Corning
+43731,Crooksville
+43732,Cumberland
+43733,Derwent
+43734,Duncan Falls
+43735,East Fultonham
+43736,Fairview
+43738,Fultonham
+43739,Glenford
+43740,Gratiot
+43746,Hopewell
+43747,Jerusalem
+43748,Junction City
+43749,Kimbolton
+43750,Kipling
+43752,Laings
+43754,Lewisville
+43755,Lore City
+43756,Mc Connelsville
+43757,Malaga
+43758,Malta
+43759,Morristown
+43760,Mount Perry
+43761,Moxahala
+43762,New Concord
+43764,New Lexington
+43766,New Straitsville
+43767,Norwich
+43768,Old Washington
+43771,Philo
+43772,Pleasant City
+43773,Quaker City
+43777,Roseville
+43778,Salesville
+43779,Sarahsville
+43780,Senecaville
+43782,Shawnee
+43783,Somerset
+43786,Stafford
+43787,Stockport
+43788,Summerfield
+43789,Sycamore Valley
+43791,White Cottage
+43793,Woodsfield
+43802,Adamsville
+43803,Bakersville
+43804,Baltic
+43805,Blissfield
+43811,Conesville
+43812,Coshocton
+43821,Dresden
+43822,Frazeysburg
+43824,Fresno
+43828,Keene
+43830,Nashport
+43832,Newcomerstown
+43836,Plainfield
+43837,Port Washington
+43840,Stone Creek
+43842,Trinway
+43843,Walhonding
+43844,Warsaw
+43845,West Lafayette
+43901,Adena
+43902,Alledonia
+43903,Amsterdam
+43905,Barton
+43906,Bellaire
+43907,Cadiz
+43908,Bergholz
+43909,Blaine
+43910,Bloomingdale
+43912,Bridgeport
+43913,Brilliant
+43914,Cameron
+43915,Clarington
+43916,Colerain
+43917,Dillonvale
+43920,East Liverpool
+43925,East Springfield
+43926,Empire
+43927,Fairpoint
+43928,Glencoe
+43930,Hammondsville
+43931,Hannibal
+43932,Irondale
+43933,Jacobsburg
+43934,Lansing
+43935,Martins Ferry
+43937,Maynard
+43938,Mingo Junction
+43939,Mount Pleasant
+43940,Neffs
+43941,Piney Fork
+43942,Powhatan Point
+43943,Rayland
+43944,Richmond
+43945,Salineville
+43946,Sardis
+43947,Shadyside
+43948,Smithfield
+43950,Saint Clairsville
+43951,Lafferty
+43952,Steubenville
+43953,Steubenville
+43961,Stratton
+43962,Summitville
+43963,Tiltonsville
+43964,Toronto
+43966,Unionport
+43967,Warnock
+43968,Wellsville
+43970,Wolf Run
+43971,Yorkville
+43972,Bannock
+43973,Freeport
+43974,Harrisville
+43976,Hopedale
+43977,Flushing
+43981,New Athens
+43983,Piedmont
+43984,New Rumley
+43985,Holloway
+43986,Jewett
+43988,Scio
+43989,Short Creek
+44001,Amherst
+44003,Andover
+44004,Ashtabula
+44005,Ashtabula
+44010,Austinburg
+44011,Avon
+44012,Avon Lake
+44017,Berea
+44021,Burton
+44022,Chagrin Falls
+44023,Chagrin Falls
+44024,Chardon
+44026,Chesterland
+44028,Columbia Station
+44030,Conneaut
+44032,Dorset
+44033,East Claridon
+44035,Elyria
+44036,Elyria
+44039,North Ridgeville
+44040,Gates Mills
+44041,Geneva
+44044,Grafton
+44045,Grand River
+44046,Huntsburg
+44047,Jefferson
+44048,Kingsville
+44049,Kipton
+44050,Lagrange
+44052,Lorain
+44053,Lorain
+44054,Sheffield Lake
+44055,Lorain
+44056,Macedonia
+44057,Madison
+44060,Mentor
+44061,Mentor
+44062,Middlefield
+44064,Montville
+44065,Newbury
+44067,Northfield
+44068,North Kingsville
+44070,North Olmsted
+44072,Novelty
+44073,Novelty
+44074,Oberlin
+44076,Orwell
+44077,Painesville
+44080,Parkman
+44081,Perry
+44082,Pierpont
+44084,Rock Creek
+44085,Rome
+44086,Thompson
+44087,Twinsburg
+44088,Unionville
+44089,Vermilion
+44090,Wellington
+44092,Wickliffe
+44093,Williamsfield
+44094,Willoughby
+44095,Eastlake
+44096,Willoughby
+44097,Eastlake
+44099,Windsor
+44101,Cleveland
+44102,Cleveland
+44103,Cleveland
+44104,Cleveland
+44105,Cleveland
+44106,Cleveland
+44107,Lakewood
+44108,Cleveland
+44109,Cleveland
+44110,Cleveland
+44111,Cleveland
+44112,Cleveland
+44113,Cleveland
+44114,Cleveland
+44115,Cleveland
+44116,Rocky River
+44117,Euclid
+44118,Cleveland
+44119,Cleveland
+44120,Cleveland
+44121,Cleveland
+44122,Beachwood
+44123,Euclid
+44124,Cleveland
+44125,Cleveland
+44126,Cleveland
+44127,Cleveland
+44128,Cleveland
+44129,Cleveland
+44130,Cleveland
+44131,Independence
+44132,Euclid
+44133,North Royalton
+44134,Cleveland
+44135,Cleveland
+44136,Strongsville
+44137,Maple Heights
+44138,Olmsted Falls
+44139,Solon
+44140,Bay Village
+44141,Brecksville
+44142,Brookpark
+44143,Cleveland
+44144,Cleveland
+44145,Westlake
+44146,Bedford
+44147,Broadview Heights
+44177,Cleveland
+44178,Cleveland
+44179,Cleveland
+44181,Cleveland
+44184,Cleveland
+44185,Cleveland
+44186,Cleveland
+44188,Cleveland
+44189,Cleveland
+44190,Cleveland
+44191,Cleveland
+44192,Cleveland
+44193,Cleveland
+44194,Cleveland
+44195,Cleveland
+44197,Cleveland
+44198,Cleveland
+44199,Cleveland
+44201,Atwater
+44202,Aurora
+44203,Barberton
+44210,Bath
+44211,Brady Lake
+44212,Brunswick
+44214,Burbank
+44215,Chippewa Lake
+44216,Clinton
+44217,Creston
+44221,Cuyahoga Falls
+44222,Cuyahoga Falls
+44223,Cuyahoga Falls
+44224,Stow
+44230,Doylestown
+44231,Garrettsville
+44232,Green
+44233,Hinckley
+44234,Hiram
+44235,Homerville
+44236,Hudson
+44237,Hudson
+44238,Hudson
+44240,Kent
+44241,Streetsboro
+44242,Kent
+44243,Kent
+44250,Lakemore
+44251,Westfield Center
+44253,Litchfield
+44254,Lodi
+44255,Mantua
+44256,Medina
+44258,Medina
+44260,Mogadore
+44262,Munroe Falls
+44264,Peninsula
+44265,Randolph
+44266,Ravenna
+44270,Rittman
+44272,Rootstown
+44273,Seville
+44274,Sharon Center
+44275,Spencer
+44276,Sterling
+44278,Tallmadge
+44280,Valley City
+44281,Wadsworth
+44282,Wadsworth
+44285,Wayland
+44286,Richfield
+44287,West Salem
+44288,Windham
+44301,Akron
+44302,Akron
+44303,Akron
+44304,Akron
+44305,Akron
+44306,Akron
+44307,Akron
+44308,Akron
+44309,Akron
+44310,Akron
+44311,Akron
+44312,Akron
+44313,Akron
+44314,Akron
+44315,Akron
+44316,Akron
+44317,Akron
+44319,Akron
+44320,Akron
+44321,Akron
+44322,Akron
+44325,Akron
+44326,Akron
+44328,Akron
+44333,Akron
+44334,Akron
+44372,Akron
+44393,Akron
+44396,Akron
+44397,Akron
+44398,Akron
+44399,Akron
+44401,Berlin Center
+44402,Bristolville
+44403,Brookfield
+44404,Burghill
+44405,Campbell
+44406,Canfield
+44408,Columbiana
+44410,Cortland
+44411,Deerfield
+44412,Diamond
+44413,East Palestine
+44415,Elkton
+44416,Ellsworth
+44417,Farmdale
+44418,Fowler
+44420,Girard
+44422,Greenford
+44423,Hanoverton
+44424,Hartford
+44425,Hubbard
+44427,Kensington
+44428,Kinsman
+44429,Lake Milton
+44430,Leavittsburg
+44431,Leetonia
+44432,Lisbon
+44436,Lowellville
+44437,Mc Donald
+44438,Masury
+44439,Mesopotamia
+44440,Mineral Ridge
+44441,Negley
+44442,New Middletown
+44443,New Springfield
+44444,Newton Falls
+44445,New Waterford
+44446,Niles
+44449,North Benton
+44450,North Bloomfield
+44451,North Jackson
+44452,North Lima
+44453,Orangeville
+44454,Petersburg
+44455,Rogers
+44460,Salem
+44470,Southington
+44471,Struthers
+44473,Vienna
+44481,Warren
+44482,Warren
+44483,Warren
+44484,Warren
+44485,Warren
+44486,Warren
+44487,Warren
+44488,Warren
+44490,Washingtonville
+44491,West Farmington
+44492,West Point
+44493,Winona
+44501,Youngstown
+44502,Youngstown
+44503,Youngstown
+44504,Youngstown
+44505,Youngstown
+44506,Youngstown
+44507,Youngstown
+44509,Youngstown
+44510,Youngstown
+44511,Youngstown
+44512,Youngstown
+44513,Youngstown
+44514,Youngstown
+44515,Youngstown
+44555,Youngstown
+44598,Youngstown
+44599,Youngstown
+44601,Alliance
+44606,Apple Creek
+44607,Augusta
+44608,Beach City
+44609,Beloit
+44610,Berlin
+44611,Big Prairie
+44612,Bolivar
+44613,Brewster
+44614,Canal Fulton
+44615,Carrollton
+44617,Charm
+44618,Dalton
+44619,Damascus
+44620,Dellroy
+44621,Dennison
+44622,Dover
+44624,Dundee
+44625,East Rochester
+44626,East Sparta
+44627,Fredericksburg
+44628,Glenmont
+44629,Gnadenhutten
+44630,Greentown
+44631,Harlem Springs
+44632,Hartville
+44633,Holmesville
+44634,Homeworth
+44636,Kidron
+44637,Killbuck
+44638,Lakeville
+44639,Leesville
+44640,Limaville
+44641,Louisville
+44643,Magnolia
+44644,Malvern
+44645,Marshallville96
+44646,Massillon36
+44647,Massillon70
+44648,Massillon33
+44650,Maximo62
+44651,Mechanicstown43
+44652,Middlebranch92
+44653,Midvale46
+44654,Millersburg29
+44656,Mineral City53
+44657,Minerva79
+44659,Mount Eaton55
+44660,Mount Hope87
+44661,Nashville45
+44662,Navarre01
+44663,New Philadelphia47
+44665,North Georgetown63
+44666,North Lawrence94
+44667,Orrville57
+44669,Paris93
+44670,Robertsville68
+44671,Sandyville17
+44672,Sebring03
+44675,Sherrodsville83
+44676,Shreve01
+44677,Smithville54
+44678,Somerdale38
+44679,Stillwater30
+44680,Strasburg35
+44681,Sugarcreek19
+44682,Tuscarawas22
+44683,Uhrichsville53
+44685,Uniontown84
+44687,Walnut Creek67
+44688,Waynesburg19
+44689,Wilmot92
+44690,Winesburg76
+44691,Wooster34
+44693,Deersville71
+44695,Bowerston53
+44697,Zoar52
+44699,Tippecanoe67
+44701,Canton66
+44702,Canton66
+44703,Canton54
+44704,Canton05
+44705,Canton00
+44706,Canton33
+44707,Canton01
+44708,Canton04
+44709,Canton40
+44710,Canton18
+44711,Canton66
+44712,Canton66
+44714,Canton71
+44718,Canton80
+44720,Canton83
+44721,Canton92
+44730,Canton98
+44735,Canton66
+44750,Canton66
+44760,Canton66
+44767,Canton66
+44798,Canton66
+44799,Canton66
+44801,Adrian07
+44802,Alvada93
+44803,Amsden68
+44804,Arcadia65
+44805,Ashland70
+44807,Attica62
+44809,Bascom29
+44811,Bellevue57
+44813,Bellville11
+44814,Berlin Heights31
+44815,Bettsville52
+44816,Birmingham38
+44817,Bloomdale42
+44818,Bloomville43
+44820,Bucyrus08
+44822,Butler55
+44824,Castalia77
+44825,Chatfield18
+44826,Collins20
+44827,Crestline35
+44828,Flat Rock48
+44830,Fostoria95
+44833,Galion10
+44836,Green Springs11
+44837,Greenwich71
+44838,Hayesville53
+44839,Huron35
+44840,Jeromesville39
+44841,Kansas83
+44842,Loudonville19
+44843,Lucas17
+44844,Mc Cutchenville78
+44845,Melmore03
+44846,Milan94
+44847,Monroeville07
+44848,Nankin02
+44849,Nevada42
+44850,New Haven63
+44851,New London30
+44853,New Riegel19
+44854,New Washington08
+44855,North Fairfield42
+44856,North Robinson30
+44857,Norwalk37
+44859,Nova39
+44860,Oceola76
+44861,Old Fort41
+44862,Ontario10
+44864,Perrysville19
+44865,Plymouth79
+44866,Polk82
+44867,Republic24
+44870,Sandusky53
+44871,Sandusky57
+44874,Savannah08
+44875,Shelby24
+44878,Shiloh53
+44880,Sullivan46
+44881,Sulphur Springs99
+44882,Sycamore12
+44883,Tiffin85
+44887,Tiro71
+44888,Willard12
+44889,Wakeman31
+44890,Willard18
+44901,Mansfield99
+44902,Mansfield98
+44903,Mansfield86
+44904,Mansfield14
+44905,Mansfield36
+44906,Mansfield29
+44907,Mansfield93
+44999,Mansfield99
+45001,Addyston84
+45002,Cleves24
+45003,College Corner11
+45004,Collinsville20
+45005,Franklin65
+45011,Hamilton51
+45012,Hamilton96
+45013,Hamilton05
+45014,Fairfield25
+45015,Hamilton21
+45018,Fairfield96
+45020,Hamilton96
+45023,Hamilton96
+45025,Hamilton96
+45026,Hamilton96
+45030,Harrison16
+45032,Harveysburg74
+45033,Hooven29
+45034,Kings Mills38
+45036,Lebanon55
+45039,Maineville63
+45040,Mason79
+45041,Miamitown73
+45042,Middletown83
+45043,Middletown48
+45044,Middletown77
+45050,Monroe97
+45051,Mount Saint Joseph59
+45052,North Bend33
+45053,Okeana76
+45054,Oregonia34
+45055,Overpeck38
+45056,Oxford86
+45061,Ross19
+45062,Seven Mile94
+45063,Shandon48
+45064,Somerville33
+45065,South Lebanon64
+45066,Springboro39
+45067,Trenton84
+45068,Waynesville90
+45069,West Chester72
+45070,West Elkton79
+45071,West Chester73
+45073,Monroe07
+45099,Monroe07
+45101,Aberdeen92
+45102,Amelia34
+45103,Batavia22
+45105,Bentonville23
+45106,Bethel02
+45107,Blanchester42
+45110,Buford11
+45111,Camp Dennison20
+45112,Chilo54
+45113,Clarksville27
+45114,Cuba52
+45115,Decatur15
+45118,Fayetteville09
+45119,Feesburg80
+45120,Felicity99
+45121,Georgetown80
+45122,Goshen94
+45123,Greenfield57
+45130,Hamersville77
+45131,Higginsport32
+45132,Highland20
+45133,Hillsboro96
+45135,Leesburg89
+45138,Lees Creek51
+45140,Loveland65
+45142,Lynchburg33
+45144,Manchester39
+45145,Marathon24
+45146,Martinsville27
+45147,Miamiville08
+45148,Midland32
+45150,Milford93
+45152,Morrow87
+45153,Moscow85
+45154,Mount Orab34
+45155,Mowrystown15
+45156,Neville63
+45157,New Richmond23
+45158,Newtonsville30
+45159,New Vienna43
+45160,Owensville99
+45162,Pleasant Plain27
+45164,Port William94
+45165,Greenfield56
+45166,Reesville12
+45167,Ripley12
+45168,Russellville31
+45169,Sabina49
+45171,Sardinia79
+45172,Sinking Spring05
+45174,Terrace Park06
+45176,Williamsburg22
+45177,Wilmington58
+45201,Cincinnati47
+45202,Cincinnati06
+45203,Cincinnati76
+45204,Cincinnati38
+45205,Cincinnati36
+45206,Cincinnati22
+45207,Cincinnati30
+45208,Cincinnati71
+45209,Cincinnati88
+45210,Cincinnati95
+45211,Cincinnati90
+45212,Cincinnati57
+45213,Cincinnati84
+45214,Cincinnati74
+45215,Cincinnati37
+45216,Cincinnati13
+45217,Cincinnati07
+45218,Cincinnati64
+45219,Cincinnati93
+45220,Cincinnati80
+45221,Cincinnati47
+45222,Cincinnati47
+45223,Cincinnati48
+45224,Cincinnati63
+45225,Cincinnati41
+45226,Cincinnati94
+45227,Cincinnati28
+45228,Cincinnati00
+45229,Cincinnati19
+45230,Cincinnati38
+45231,Cincinnati47
+45232,Cincinnati85
+45233,Cincinnati36
+45234,Cincinnati47
+45235,Cincinnati22
+45236,Cincinnati00
+45237,Cincinnati53
+45238,Cincinnati97
+45239,Cincinnati03
+45240,Cincinnati51
+45241,Cincinnati80
+45242,Cincinnati45
+45243,Cincinnati63
+45244,Cincinnati91
+45245,Cincinnati58
+45246,Cincinnati10
+45247,Cincinnati22
+45248,Cincinnati39
+45249,Cincinnati69
+45250,Cincinnati47
+45251,Cincinnati84
+45252,Cincinnati44
+45253,Cincinnati47
+45254,Cincinnati21
+45255,Cincinnati00
+45258,Cincinnati47
+45262,Cincinnati47
+45263,Cincinnati47
+45264,Cincinnati47
+45267,Cincinnati47
+45268,Cincinnati47
+45269,Cincinnati47
+45270,Cincinnati47
+45271,Cincinnati47
+45273,Cincinnati47
+45274,Cincinnati47
+45275,Cincinnati54
+45277,Cincinnati47
+45296,Cincinnati47
+45298,Cincinnati47
+45299,Cincinnati47
+45301,Alpha15
+45302,Anna92
+45303,Ansonia54
+45304,Arcanum24
+45305,Bellbrook77
+45306,Botkins13
+45307,Bowersville59
+45308,Bradford73
+45309,Brookville63
+45310,Burkettsville55
+45311,Camden56
+45312,Casstown99
+45314,Cedarville77
+45315,Clayton50
+45316,Clifton01
+45317,Conover32
+45318,Covington81
+45319,Donnelsville56
+45320,Eaton90
+45321,Eldorado02
+45322,Englewood54
+45323,Enon84
+45324,Fairborn10
+45325,Farmersville95
+45326,Fletcher67
+45327,Germantown29
+45328,Gettysburg46
+45329,Gordon61
+45330,Gratis91
+45331,Greenville80
+45332,Hollansburg56
+45333,Houston84
+45334,Jackson Center53
+45335,Jamestown33
+45336,Kettlersville38
+45337,Laura45
+45338,Lewisburg33
+45339,Ludlow Falls00
+45340,Maplewood74
+45341,Medway82
+45342,Miamisburg81
+45343,Miamisburg48
+45344,New Carlisle81
+45345,New Lebanon96
+45346,New Madison37
+45347,New Paris01
+45348,New Weston48
+45349,North Hampton56
+45350,North Star37
+45351,Osgood05
+45352,Palestine98
+45353,Pemberton95
+45354,Phillipsburg74
+45356,Piqua65
+45358,Pitsburg74
+45359,Pleasant Hill74
+45360,Port Jefferson31
+45361,Potsdam56
+45362,Rossburg63
+45363,Russia26
+45365,Sidney78
+45367,Sidney78
+45368,South Charleston72
+45369,South Vienna16
+45370,Spring Valley20
+45371,Tipp City76
+45372,Tremont City51
+45373,Troy11
+45374,Troy62
+45377,Vandalia62
+45378,Verona85
+45380,Versailles08
+45381,West Alexandria70
+45382,West Manchester56
+45383,West Milton17
+45384,Wilberforce63
+45385,Xenia51
+45387,Yellow Springs22
+45388,Yorkshire26
+45389,Christiansburg48
+45390,Union City34
+45401,Dayton21
+45402,Dayton31
+45403,Dayton60
+45404,Dayton56
+45405,Dayton97
+45406,Dayton68
+45407,Dayton91
+45408,Dayton94
+45409,Dayton36
+45410,Dayton62
+45412,Dayton21
+45413,Dayton21
+45414,Dayton90
+45415,Dayton41
+45416,Dayton45
+45417,Dayton63
+45418,Dayton30
+45419,Dayton57
+45420,Dayton96
+45422,Dayton21
+45423,Dayton21
+45424,Dayton98
+45426,Dayton87
+45427,Dayton03
+45428,Dayton21
+45429,Dayton71
+45430,Dayton46
+45431,Dayton39
+45432,Dayton32
+45433,Dayton61
+45434,Dayton05
+45435,Dayton21
+45437,Dayton22
+45439,Dayton00
+45440,Dayton42
+45441,Dayton21
+45448,Dayton21
+45449,Dayton85
+45454,Dayton21
+45458,Dayton83
+45459,Dayton76
+45463,Dayton21
+45469,Dayton21
+45470,Dayton21
+45475,Dayton21
+45479,Dayton21
+45481,Dayton21
+45482,Dayton21
+45490,Dayton21
+45501,Springfield03
+45502,Springfield03
+45503,Springfield37
+45504,Springfield31
+45505,Springfield64
+45506,Springfield57
+45601,Chillicothe37
+45612,Bainbridge94
+45613,Beaver82
+45614,Bidwell36
+45616,Blue Creek17
+45617,Bourneville03
+45618,Cherry Fork94
+45619,Chesapeake12
+45620,Cheshire41
+45621,Coalton18
+45622,Creola83
+45623,Crown City71
+45624,Cynthiana20
+45628,Frankfort48
+45629,Franklin Furnace15
+45630,Friendship83
+45631,Gallipolis66
+45633,Hallsville26
+45634,Hamden58
+45636,Haverhill84
+45638,Ironton77
+45640,Jackson00
+45642,Jasper53
+45643,Kerr46
+45644,Kingston05
+45645,Kitts Hill11
+45646,Latham65
+45647,Londonderry27
+45648,Lucasville90
+45650,Lynx24
+45651,Mc Arthur44
+45652,Mc Dermott63
+45653,Minford22
+45654,New Plymouth98
+45656,Oak Hill70
+45657,Otway14
+45658,Patriot72
+45659,Pedro81
+45660,Peebles56
+45661,Piketon65
+45662,Portsmouth67
+45663,West Portsmouth66
+45669,Proctorville10
+45671,Rarden79
+45672,Ray26
+45673,Richmond Dale99
+45674,Rio Grande06
+45675,Rock Camp98
+45677,Scioto Furnace14
+45678,Scottown44
+45679,Seaman31
+45680,South Point28
+45681,South Salem01
+45682,South Webster60
+45683,Stockdale85
+45684,Stout07
+45685,Thurman52
+45686,Vinton02
+45687,Wakefield14
+45688,Waterloo70
+45690,Waverly49
+45692,Wellston86
+45693,West Union93
+45694,Wheelersburg01
+45695,Wilkesville73
+45696,Willow Wood87
+45697,Winchester28
+45698,Zaleski12
+45699,Lucasville56
+45701,Athens43
+45710,Albany10
+45711,Amesville76
+45712,Barlow64
+45713,Bartlett93
+45714,Belpre44
+45715,Beverly44
+45716,Buchtel03
+45717,Carbondale26
+45719,Chauncey71
+45720,Chester51
+45721,Coal Run19
+45723,Coolville84
+45724,Cutler55
+45727,Dexter City98
+45729,Fleming83
+45732,Glouster14
+45734,Graysville28
+45735,Guysville27
+45739,Hockingport14
+45740,Jacksonville09
+45741,Langsville19
+45742,Little Hocking10
+45743,Long Bottom21
+45744,Lowell89
+45745,Lower Salem50
+45746,Macksburg51
+45750,Marietta09
+45760,Middleport86
+45761,Millfield97
+45764,Nelsonville36
+45766,New Marshfield53
+45767,New Matamoras97
+45768,Newport87
+45769,Pomeroy20
+45770,Portland93
+45771,Racine95
+45772,Reedsville62
+45773,Reno90
+45775,Rutland17
+45776,Shade37
+45777,Sharpsburg58
+45778,Stewart35
+45779,Syracuse24
+45780,The Plains65
+45781,Torch07
+45782,Trimble18
+45783,Tuppers Plains27
+45784,Vincent63
+45786,Waterford59
+45787,Watertown84
+45788,Whipple77
+45789,Wingett Run38
+45801,Lima18
+45802,Lima44
+45804,Lima97
+45805,Lima86
+45806,Lima24
+45807,Lima66
+45808,Beaverdam69
+45809,Gomer41
+45810,Ada45
+45812,Alger67
+45813,Antwerp96
+45814,Arlington26
+45815,Belmore45
+45816,Benton Ridge13
+45817,Bluffton42
+45819,Buckland02
+45820,Cairo51
+45821,Cecil27
+45822,Celina56
+45826,Chickasaw97
+45827,Cloverdale90
+45828,Coldwater86
+45830,Columbus Grove21
+45831,Continental34
+45832,Convoy44
+45833,Delphos95
+45835,Dola76
+45836,Dunkirk20
+45837,Dupont83
+45838,Elgin58
+45839,Findlay66
+45840,Findlay68
+45841,Jenera10
+45843,Forest51
+45844,Fort Jennings42
+45845,Fort Loramie24
+45846,Fort Recovery38
+45848,Glandorf24
+45849,Grover Hill69
+45850,Harrod46
+45851,Haviland40
+45853,Kalida91
+45854,Lafayette09
+45855,Latty82
+45856,Leipsic91
+45858,Mc Comb99
+45859,Mc Guffey86
+45860,Maria Stein77
+45861,Melrose59
+45862,Mendon39
+45863,Middle Point87
+45864,Miller City54
+45865,Minster25
+45866,Montezuma25
+45867,Mount Blanchard04
+45868,Mount Cory79
+45869,New Bremen87
+45870,New Hampshire37
+45871,New Knoxville88
+45872,North Baltimore96
+45873,Oakwood74
+45874,Ohio City23
+45875,Ottawa34
+45876,Ottoville61
+45877,Pandora82
+45879,Paulding03
+45880,Payne25
+45881,Rawson40
+45882,Rockford60
+45883,Saint Henry57
+45884,Saint Johns99
+45885,Saint Marys81
+45886,Scott06
+45887,Spencerville93
+45888,Uniopolis83
+45889,Van Buren51
+45890,Vanlue44
+45891,Van Wert00
+45893,Vaughnsville75
+45894,Venedocia65
+45895,Wapakoneta49
+45896,Waynesfield40
+45897,Williamstown05
+45898,Willshire63
+45899,Wren34
+45944,Cincinnati47
+45999,Cincinnati47
+00501,Holtsville
+00544,Holtsville
+06390,Fishers Island
+10001,New York
+10002,New York
+10003,New York
+10004,New York
+10005,New York
+10006,New York
+10007,New York
+10008,New York
+10009,New York
+10010,New York
+10011,New York
+10012,New York
+10013,New York
+10014,New York
+10015,New York
+10016,New York
+10017,New York
+10018,New York
+10019,New York
+10020,New York
+10021,New York
+10022,New York
+10023,New York
+10024,New York
+10025,New York
+10026,New York
+10027,New York
+10028,New York
+10029,New York
+10030,New York
+10031,New York
+10032,New York
+10033,New York
+10034,New York
+10035,New York
+10036,New York
+10037,New York
+10038,New York
+10039,New York
+10040,New York
+10041,New York
+10043,New York
+10044,New York
+10045,New York
+10046,New York
+10047,New York
+10048,New York
+10055,New York
+10060,New York
+10069,New York
+10072,New York
+10079,New York
+10080,New York
+10081,New York
+10082,New York
+10087,New York
+10090,New York
+10094,New York
+10095,New York
+10096,New York
+10098,New York
+10099,New York
+10101,New York
+10102,New York
+10103,New York
+10104,New York
+10105,New York
+10106,New York
+10107,New York
+10108,New York
+10109,New York
+10110,New York
+10111,New York
+10112,New York
+10113,New York
+10114,New York
+10115,New York
+10116,New York
+10117,New York
+10118,New York
+10119,New York
+10120,New York
+10121,New York
+10122,New York
+10123,New York
+10124,New York
+10125,New York
+10126,New York
+10128,New York
+10129,New York
+10130,New York
+10131,New York
+10132,New York
+10133,New York
+10138,New York
+10149,New York
+10150,New York
+10151,New York
+10152,New York
+10153,New York
+10154,New York
+10155,New York
+10156,New York
+10157,New York
+10158,New York
+10159,New York
+10160,New York
+10161,New York
+10162,New York
+10163,New York
+10164,New York
+10165,New York
+10166,New York
+10167,New York
+10168,New York
+10169,New York
+10170,New York
+10171,New York
+10172,New York
+10173,New York
+10174,New York
+10175,New York
+10176,New York
+10177,New York
+10178,New York
+10179,New York
+10184,New York
+10185,New York
+10196,New York
+10197,New York
+10199,New York
+10203,New York
+10211,New York
+10212,New York
+10213,New York
+10242,New York
+10249,New York
+10256,New York
+10257,New York
+10258,New York
+10259,New York
+10260,New York
+10261,New York
+10265,New York
+10268,New York
+10269,New York
+10270,New York
+10271,New York
+10272,New York
+10273,New York
+10274,New York
+10275,New York
+10276,New York
+10277,New York
+10278,New York
+10279,New York
+10280,New York
+10281,New York
+10282,New York
+10285,New York
+10286,New York
+10292,New York
+10301,Staten Island
+10302,Staten Island
+10303,Staten Island
+10304,Staten Island
+10305,Staten Island
+10306,Staten Island
+10307,Staten Island
+10308,Staten Island
+10309,Staten Island
+10310,Staten Island
+10311,Staten Island
+10312,Staten Island
+10313,Staten Island
+10314,Staten Island
+10451,Bronx
+10452,Bronx
+10453,Bronx
+10454,Bronx
+10455,Bronx
+10456,Bronx
+10457,Bronx
+10458,Bronx
+10459,Bronx
+10460,Bronx
+10461,Bronx
+10462,Bronx
+10463,Bronx
+10464,Bronx
+10465,Bronx
+10466,Bronx
+10467,Bronx
+10468,Bronx
+10469,Bronx
+10470,Bronx
+10471,Bronx
+10472,Bronx
+10473,Bronx
+10474,Bronx
+10475,Bronx
+10499,Bronx
+10501,Amawalk
+10502,Ardsley
+10503,Ardsley on Hudson
+10504,Armonk
+10505,Baldwin Place
+10506,Bedford
+10507,Bedford Hills
+10509,Brewster
+10510,Briarcliff Manor
+10511,Buchanan
+10512,Carmel
+10514,Chappaqua
+10516,Cold Spring
+10517,Crompond
+10518,Cross River
+10519,Croton Falls
+10520,Croton on Hudson
+10521,Croton on Hudson
+10522,Dobbs Ferry
+10523,Elmsford
+10524,Garrison
+10526,Goldens Bridge
+10527,Granite Springs
+10528,Harrison
+10530,Hartsdale
+10532,Hawthorne
+10533,Irvington
+10535,Jefferson Valley
+10536,Katonah
+10537,Lake Peekskill
+10538,Larchmont
+10540,Lincolndale
+10541,Mahopac
+10542,Mahopac Falls
+10543,Mamaroneck
+10545,Maryknoll
+10546,Millwood
+10547,Mohegan Lake
+10548,Montrose
+10549,Mount Kisco
+10550,Mount Vernon
+10551,Mount Vernon
+10552,Mount Vernon
+10553,Mount Vernon
+10557,Mount Vernon
+10558,Mount Vernon
+10559,Mount Vernon
+10560,North Salem
+10562,Ossining
+10566,Peekskill
+10567,Cortlandt Manor
+10570,Pleasantville
+10571,Pleasantville
+10572,Pleasantville
+10573,Port Chester
+10576,Pound Ridge
+10577,Purchase
+10578,Purdys
+10579,Putnam Valley
+10580,Rye
+10581,Rye
+10583,Scarsdale
+10587,Shenorock
+10588,Shrub Oak
+10589,Somers
+10590,South Salem
+10591,Tarrytown
+10592,Tarrytown
+10594,Thornwood
+10595,Valhalla
+10596,Verplanck
+10597,Waccabuc
+10598,Yorktown Heights
+10601,White Plains
+10602,White Plains
+10603,White Plains
+10604,West Harrison
+10605,White Plains
+10606,White Plains
+10607,White Plains
+10610,White Plains
+10625,White Plains
+10629,White Plains
+10633,White Plains
+10650,White Plains
+10701,Yonkers
+10702,Yonkers
+10703,Yonkers
+10704,Yonkers
+10705,Yonkers
+10706,Hastings on Hudson
+10707,Tuckahoe
+10708,Bronxville
+10709,Eastchester
+10710,Yonkers
+10801,New Rochelle
+10802,New Rochelle
+10803,Pelham
+10804,New Rochelle
+10805,New Rochelle
+10901,Suffern
+10910,Arden
+10911,Bear Mountain
+10912,Bellvale
+10913,Blauvelt
+10914,Blooming Grove
+10915,Bullville
+10916,Campbell Hall
+10917,Central Valley
+10918,Chester
+10919,Circleville
+10920,Congers
+10921,Florida
+10922,Fort Montgomery
+10923,Garnerville
+10924,Goshen
+10925,Greenwood Lake
+10926,Harriman
+10927,Haverstraw
+10928,Highland Falls
+10930,Highland Mills
+10931,Hillburn
+10932,Howells
+10933,Johnson
+10940,Middletown
+10941,Middletown
+10943,Middletown
+10950,Monroe
+10952,Monsey
+10953,Mountainville
+10954,Nanuet
+10956,New City
+10958,New Hampton
+10959,New Milford
+10960,Nyack
+10962,Orangeburg
+10963,Otisville
+10964,Palisades
+10965,Pearl River
+10968,Piermont
+10969,Pine Island
+10970,Pomona
+10973,Slate Hill
+10974,Sloatsburg
+10975,Southfields
+10976,Sparkill
+10977,Spring Valley
+10979,Sterling Forest
+10980,Stony Point
+10981,Sugar Loaf
+10982,Tallman
+10983,Tappan
+10984,Thiells
+10985,Thompson Ridge
+10986,Tomkins Cove
+10987,Tuxedo Park
+10988,Unionville
+10989,Valley Cottage
+10990,Warwick
+10992,Washingtonville
+10993,West Haverstraw
+10994,West Nyack
+10995,West Nyack
+10996,West Point
+10997,West Point
+10998,Westtown
+11001,Floral Park
+11002,Floral Park
+11003,Elmont
+11004,Glen Oaks
+11005,Floral Park
+11010,Franklin Square
+11020,Great Neck
+11021,Great Neck
+11022,Great Neck
+11023,Great Neck
+11024,Great Neck
+11025,Great Neck
+11026,Great Neck
+11027,Great Neck
+11030,Manhasset
+11040,New Hyde Park
+11041,New Hyde Park
+11042,New Hyde Park
+11043,New Hyde Park
+11044,New Hyde Park
+11050,Port Washington
+11051,Port Washington
+11052,Port Washington
+11053,Port Washington
+11054,Port Washington
+11055,Port Washington
+11096,Inwood
+11099,New Hyde Park
+11101,Long Island City
+11102,Astoria
+11103,Astoria
+11104,Sunnyside
+11105,Astoria
+11106,Astoria
+11109,Long Island City
+11120,Long Island City
+11201,Brooklyn
+11202,Brooklyn
+11203,Brooklyn
+11204,Brooklyn
+11205,Brooklyn
+11206,Brooklyn
+11207,Brooklyn
+11208,Brooklyn
+11209,Brooklyn
+11210,Brooklyn
+11211,Brooklyn
+11212,Brooklyn
+11213,Brooklyn
+11214,Brooklyn
+11215,Brooklyn
+11216,Brooklyn
+11217,Brooklyn
+11218,Brooklyn
+11219,Brooklyn
+11220,Brooklyn
+11221,Brooklyn
+11222,Brooklyn
+11223,Brooklyn
+11224,Brooklyn
+11225,Brooklyn
+11226,Brooklyn
+11228,Brooklyn
+11229,Brooklyn
+11230,Brooklyn
+11231,Brooklyn
+11232,Brooklyn
+11233,Brooklyn
+11234,Brooklyn
+11235,Brooklyn
+11236,Brooklyn
+11237,Brooklyn
+11238,Brooklyn
+11239,Brooklyn
+11240,Brooklyn
+11241,Brooklyn
+11242,Brooklyn
+11243,Brooklyn
+11244,Brooklyn
+11245,Brooklyn
+11247,Brooklyn
+11248,Brooklyn
+11249,Brooklyn
+11251,Brooklyn
+11252,Brooklyn
+11254,Brooklyn
+11255,Brooklyn
+11256,Brooklyn
+11351,Flushing
+11352,Flushing
+11353,Flushing
+11354,Flushing
+11355,Flushing
+11356,College Point
+11357,Whitestone
+11358,Flushing
+11359,Bayside
+11360,Bayside
+11361,Bayside
+11362,Little Neck
+11363,Little Neck
+11364,Oakland Gardens
+11365,Fresh Meadows
+11366,Fresh Meadows
+11367,Flushing
+11368,Corona
+11369,East Elmhurst
+11370,East Elmhurst
+11371,Flushing
+11372,Jackson Heights
+11373,Elmhurst
+11374,Rego Park
+11375,Forest Hills
+11377,Woodside
+11378,Maspeth
+11379,Middle Village
+11380,Elmhurst
+11381,Flushing
+11385,Ridgewood
+11386,Ridgewood
+11388,Flushing
+11390,Flushing
+11405,Jamaica
+11411,Cambria Heights
+11412,Saint Albans
+11413,Springfield Gardens
+11414,Howard Beach
+11415,Kew Gardens
+11416,Ozone Park
+11417,Ozone Park
+11418,Richmond Hill
+11419,South Richmond Hill
+11420,South Ozone Park
+11421,Woodhaven
+11422,Rosedale
+11423,Hollis
+11424,Jamaica
+11425,Jamaica
+11426,Bellerose
+11427,Queens Village
+11428,Queens Village
+11429,Queens Village
+11430,Jamaica
+11431,Jamaica
+11432,Jamaica
+11433,Jamaica
+11434,Jamaica
+11435,Jamaica
+11436,Jamaica
+11439,Jamaica
+11451,Jamaica
+11484,Jamaica
+11499,Jamaica
+11501,Mineola
+11507,Albertson
+11509,Atlantic Beach
+11510,Baldwin
+11514,Carle Place
+11516,Cedarhurst
+11518,East Rockaway
+11520,Freeport
+11530,Garden City
+11531,Garden City
+11535,Garden City
+11536,Garden City
+11542,Glen Cove
+11545,Glen Head
+11547,Glenwood Landing
+11548,Greenvale
+11549,Hempstead
+11550,Hempstead
+11551,Hempstead
+11552,West Hempstead
+11553,Uniondale
+11554,East Meadow
+11555,Uniondale
+11556,Uniondale
+11557,Hewlett
+11558,Island Park
+11559,Lawrence
+11560,Locust Valley
+11561,Long Beach
+11563,Lynbrook
+11564,Lynbrook
+11565,Malverne
+11566,Merrick
+11568,Old Westbury
+11569,Point Lookout
+11570,Rockville Centre
+11571,Rockville Centre
+11572,Oceanside
+11575,Roosevelt
+11576,Roslyn
+11577,Roslyn Heights
+11579,Sea Cliff
+11580,Valley Stream
+11581,Valley Stream
+11582,Valley Stream
+11583,Valley Stream
+11588,Uniondale
+11590,Westbury
+11592,Rockville Centre
+11593,Westbury
+11594,Westbury
+11595,Westbury
+11596,Williston Park
+11597,Westbury
+11598,Woodmere
+11599,Garden City
+11690,Far Rockaway
+11691,Far Rockaway
+11692,Arverne
+11693,Far Rockaway
+11694,Rockaway Park
+11695,Far Rockaway
+11696,Inwood
+11697,Breezy Point
+11701,Amityville
+11702,Babylon
+11703,North Babylon
+11704,West Babylon
+11705,Bayport
+11706,Bay Shore
+11707,West Babylon
+11708,Amityville
+11709,Bayville
+11710,Bellmore
+11713,Bellport
+11714,Bethpage
+11715,Blue Point
+11716,Bohemia
+11717,Brentwood
+11718,Brightwaters
+11719,Brookhaven
+11720,Centereach
+11721,Centerport
+11722,Central Islip
+11724,Cold Spring Harbor
+11725,Commack
+11726,Copiague
+11727,Coram
+11729,Deer Park
+11730,East Islip
+11731,East Northport
+11732,East Norwich
+11733,East Setauket
+11735,Farmingdale
+11736,Farmingdale
+11737,Farmingdale
+11738,Farmingville
+11739,Great River
+11740,Greenlawn
+11741,Holbrook
+11742,Holtsville
+11743,Huntington
+11745,Smithtown
+11746,Huntington Station
+11747,Melville
+11749,Islandia
+11750,Huntington Station
+11751,Islip
+11752,Islip Terrace
+11753,Jericho
+11754,Kings Park
+11755,Lake Grove
+11756,Levittown
+11757,Lindenhurst
+11758,Massapequa
+11760,Hauppauge
+11762,Massapequa Park
+11763,Medford
+11764,Miller Place
+11765,Mill Neck
+11766,Mount Sinai
+11767,Nesconset
+11768,Northport
+11769,Oakdale
+11770,Ocean Beach
+11771,Oyster Bay
+11772,Patchogue
+11773,Syosset
+11774,Farmingdale
+11775,Melville
+11776,Port Jefferson Station
+11777,Port Jefferson
+11778,Rocky Point
+11779,Ronkonkoma
+11780,Saint James
+11782,Sayville
+11783,Seaford
+11784,Selden
+11786,Shoreham
+11787,Smithtown
+11788,Hauppauge
+11789,Sound Beach
+11790,Stony Brook
+11791,Syosset
+11792,Wading River
+11793,Wantagh
+11794,Stony Brook
+11795,West Islip
+11796,West Sayville
+11797,Woodbury
+11798,Wyandanch
+11801,Hicksville
+11802,Hicksville
+11803,Plainview
+11804,Old Bethpage
+11805,Mid Island
+11815,Hicksville
+11819,Hicksville
+11853,Jericho
+11854,Hicksville
+11855,Hicksville
+11901,Riverhead
+11930,Amagansett
+11931,Aquebogue
+11932,Bridgehampton
+11933,Calverton
+11934,Center Moriches
+11935,Cutchogue
+11937,East Hampton
+11939,East Marion
+11940,East Moriches
+11941,Eastport
+11942,East Quogue
+11944,Greenport
+11946,Hampton Bays
+11947,Jamesport
+11948,Laurel
+11949,Manorville
+11950,Mastic
+11951,Mastic Beach
+11952,Mattituck
+11953,Middle Island
+11954,Montauk
+11955,Moriches
+11956,New Suffolk
+11957,Orient
+11958,Peconic
+11959,Quogue
+11960,Remsenburg
+11961,Ridge
+11962,Sagaponack
+11963,Sag Harbor
+11964,Shelter Island
+11965,Shelter Island Heights
+11967,Shirley
+11968,Southampton
+11969,Southampton
+11970,South Jamesport
+11971,Southold
+11972,Speonk
+11973,Upton
+11975,Wainscott
+11976,Water Mill
+11977,Westhampton
+11978,Westhampton Beach
+11980,Yaphank
+12007,Alcove
+12008,Alplaus
+12009,Altamont
+12010,Amsterdam
+12015,Athens
+12016,Auriesville
+12017,Austerlitz
+12018,Averill Park
+12019,Ballston Lake
+12020,Ballston Spa
+12022,Berlin
+12023,Berne
+12024,Brainard
+12025,Broadalbin
+12027,Burnt Hills
+12028,Buskirk
+12029,Canaan
+12031,Carlisle
+12032,Caroga Lake
+12033,Castleton on Hudson
+12035,Central Bridge
+12036,Charlotteville
+12037,Chatham
+12040,Cherry Plain
+12041,Clarksville
+12042,Climax
+12043,Cobleskill
+12045,Coeymans
+12046,Coeymans Hollow
+12047,Cohoes
+12050,Columbiaville
+12051,Coxsackie
+12052,Cropseyville
+12053,Delanson
+12054,Delmar
+12055,Dormansville
+12056,Duanesburg
+12057,Eagle Bridge
+12058,Earlton
+12059,East Berne
+12060,East Chatham
+12061,East Greenbush
+12062,East Nassau
+12063,East Schodack
+12064,East Worcester
+12065,Clifton Park
+12066,Esperance
+12067,Feura Bush
+12068,Fonda
+12069,Fort Hunter
+12070,Fort Johnson
+12071,Fultonham
+12072,Fultonville
+12073,Gallupville
+12074,Galway
+12075,Ghent
+12076,Gilboa
+12077,Glenmont
+12078,Gloversville
+12082,Grafton
+12083,Greenville
+12084,Guilderland
+12085,Guilderland Center
+12086,Hagaman
+12087,Hannacroix
+12089,Hoosick
+12090,Hoosick Falls
+12092,Howes Cave
+12093,Jefferson
+12094,Johnsonville
+12095,Johnstown
+12106,Kinderhook
+12107,Knox
+12108,Lake Pleasant
+12110,Latham
+12111,Latham
+12115,Malden Bridge
+12116,Maryland
+12117,Mayfield
+12118,Mechanicville
+12120,Medusa
+12121,Melrose
+12122,Middleburgh
+12123,Nassau
+12124,New Baltimore
+12125,New Lebanon
+12128,Newtonville
+12130,Niverville
+12131,North Blenheim
+12132,North Chatham
+12133,North Hoosick
+12134,Northville
+12136,Old Chatham
+12137,Pattersonville
+12138,Petersburg
+12139,Piseco
+12140,Poestenkill
+12141,Quaker Street
+12143,Ravena
+12144,Rensselaer
+12147,Rensselaerville
+12148,Rexford
+12149,Richmondville
+12150,Rotterdam Junction
+12151,Round Lake
+12153,Sand Lake
+12154,Schaghticoke
+12155,Schenevus
+12156,Schodack Landing
+12157,Schoharie
+12158,Selkirk
+12159,Slingerlands
+12160,Sloansville
+12161,South Bethlehem
+12162,South Schodack
+12164,Speculator
+12165,Spencertown
+12166,Sprakers
+12167,Stamford
+12168,Stephentown
+12169,Stephentown
+12170,Stillwater
+12172,Stottville
+12173,Stuyvesant
+12174,Stuyvesant Falls
+12175,Summit
+12176,Surprise
+12177,Tribes Hill
+12179,Troy
+12180,Troy
+12181,Troy
+12182,Troy
+12183,Troy
+12184,Valatie
+12185,Valley Falls
+12186,Voorheesville
+12187,Warnerville
+12188,Waterford
+12189,Watervliet
+12190,Wells
+12192,West Coxsackie
+12193,Westerlo
+12194,West Fulton
+12195,West Lebanon
+12196,West Sand Lake
+12197,Worcester
+12198,Wynantskill
+12201,Albany
+12202,Albany
+12203,Albany
+12204,Albany
+12205,Albany
+12206,Albany
+12207,Albany
+12208,Albany
+12209,Albany
+12210,Albany
+12211,Albany
+12212,Albany
+12214,Albany
+12220,Albany
+12222,Albany
+12223,Albany
+12224,Albany
+12225,Albany
+12226,Albany
+12227,Albany
+12228,Albany
+12229,Albany
+12230,Albany
+12231,Albany
+12232,Albany
+12233,Albany
+12234,Albany
+12235,Albany
+12236,Albany
+12237,Albany
+12238,Albany
+12239,Albany
+12240,Albany
+12241,Albany
+12242,Albany
+12243,Albany
+12244,Albany
+12245,Albany
+12246,Albany
+12247,Albany
+12248,Albany
+12249,Albany
+12250,Albany
+12252,Albany
+12255,Albany
+12256,Albany
+12257,Albany
+12260,Albany
+12261,Albany
+12262,Albany
+12288,Albany
+12301,Schenectady
+12302,Schenectady
+12303,Schenectady
+12304,Schenectady
+12305,Schenectady
+12306,Schenectady
+12307,Schenectady
+12308,Schenectady
+12309,Schenectady
+12325,Schenectady
+12345,Schenectady
+12401,Kingston
+12402,Kingston
+12404,Accord
+12405,Acra
+12406,Arkville
+12407,Ashland
+12409,Bearsville
+12410,Big Indian
+12411,Bloomington
+12412,Boiceville
+12413,Cairo
+12414,Catskill
+12416,Chichester
+12417,Connelly
+12418,Cornwallville
+12419,Cottekill
+12420,Cragsmoor
+12421,Denver
+12422,Durham
+12423,East Durham
+12424,East Jewett
+12427,Elka Park
+12428,Ellenville
+12429,Esopus
+12430,Fleischmanns
+12431,Freehold
+12432,Glasco
+12433,Glenford
+12434,Grand Gorge
+12435,Greenfield Park
+12436,Haines Falls
+12438,Halcottsville
+12439,Hensonville
+12440,High Falls
+12441,Highmount
+12442,Hunter
+12443,Hurley
+12444,Jewett
+12446,Kerhonkson
+12448,Lake Hill
+12449,Lake Katrine
+12450,Lanesville
+12451,Leeds
+12452,Lexington
+12453,Malden on Hudson
+12454,Maplecrest
+12455,Margaretville
+12456,Mount Marion
+12457,Mount Tremper
+12458,Napanoch
+12459,New Kingston
+12460,Oak Hill
+12461,Olivebridge
+12463,Palenville
+12464,Phoenicia
+12465,Pine Hill
+12466,Port Ewen
+12468,Prattsville
+12469,Preston Hollow
+12470,Purling
+12471,Rifton
+12472,Rosendale
+12473,Round Top
+12474,Roxbury
+12475,Ruby
+12477,Saugerties
+12480,Shandaken
+12481,Shokan
+12482,South Cairo
+12483,Spring Glen
+12484,Stone Ridge
+12485,Tannersville
+12486,Tillson
+12487,Ulster Park
+12489,Wawarsing
+12490,West Camp
+12491,West Hurley
+12492,West Kill
+12493,West Park
+12494,West Shokan
+12495,Willow
+12496,Windham
+12498,Woodstock
+12501,Amenia
+12502,Ancram
+12503,Ancramdale
+12504,Annandale on Hudson
+12506,Bangall
+12507,Barrytown
+12508,Beacon
+12510,Billings
+12511,Castle Point
+12512,Chelsea
+12513,Claverack
+12514,Clinton Corners
+12515,Clintondale
+12516,Copake
+12517,Copake Falls
+12518,Cornwall
+12520,Cornwall on Hudson
+12521,Craryville
+12522,Dover Plains
+12523,Elizaville
+12524,Fishkill
+12525,Gardiner
+12526,Germantown
+12527,Glenham
+12528,Highland
+12529,Hillsdale
+12530,Hollowville
+12531,Holmes
+12533,Hopewell Junction
+12534,Hudson
+12537,Hughsonville
+12538,Hyde Park
+12540,Lagrangeville
+12541,Livingston
+12542,Marlboro
+12543,Maybrook
+12544,Mellenville
+12545,Millbrook
+12546,Millerton
+12547,Milton
+12548,Modena
+12549,Montgomery
+12550,Newburgh
+12551,Newburgh
+12552,Newburgh
+12553,New Windsor
+12555,Mid Hudson
+12561,New Paltz
+12563,Patterson
+12564,Pawling
+12565,Philmont
+12566,Pine Bush
+12567,Pine Plains
+12568,Plattekill
+12569,Pleasant Valley
+12570,Poughquag
+12571,Red Hook
+12572,Rhinebeck
+12574,Rhinecliff
+12575,Rock Tavern
+12577,Salisbury Mills
+12578,Salt Point
+12580,Staatsburg
+12581,Stanfordville
+12582,Stormville
+12583,Tivoli
+12584,Vails Gate
+12585,Verbank
+12586,Walden
+12588,Walker Valley
+12589,Wallkill
+12590,Wappingers Falls
+12592,Wassaic
+12593,West Copake
+12594,Wingdale
+12601,Poughkeepsie
+12602,Poughkeepsie
+12603,Poughkeepsie
+12604,Poughkeepsie
+12701,Monticello
+12719,Barryville
+12720,Bethel
+12721,Bloomingburg
+12722,Burlingham
+12723,Callicoon
+12724,Callicoon Center
+12725,Claryville
+12726,Cochecton
+12727,Cochecton Center
+12729,Cuddebackville
+12732,Eldred
+12733,Fallsburg
+12734,Ferndale
+12736,Fremont Center
+12737,Glen Spey
+12738,Glen Wild
+12739,Godeffroy
+12740,Grahamsville
+12741,Hankins
+12742,Harris
+12743,Highland Lake
+12745,Hortonville
+12746,Huguenot
+12747,Hurleyville
+12748,Jeffersonville
+12749,Kauneonga Lake
+12750,Kenoza Lake
+12751,Kiamesha Lake
+12752,Lake Huntington
+12754,Liberty
+12758,Livingston Manor
+12759,Loch Sheldrake
+12760,Long Eddy
+12762,Mongaup Valley
+12763,Mountain Dale
+12764,Narrowsburg
+12765,Neversink
+12766,North Branch
+12767,Obernburg
+12768,Parksville
+12769,Phillipsport
+12770,Pond Eddy
+12771,Port Jervis
+12775,Rock Hill
+12776,Roscoe
+12777,Forestburgh
+12778,Smallwood
+12779,South Fallsburg
+12780,Sparrow Bush
+12781,Summitville
+12782,Sundown
+12783,Swan Lake
+12784,Thompsonville
+12785,Westbrookville
+12786,White Lake
+12787,White Sulphur Springs
+12788,Woodbourne
+12789,Woodridge
+12790,Wurtsboro
+12791,Youngsville
+12792,Yulan
+12801,Glens Falls
+12803,South Glens Falls
+12804,Queensbury
+12808,Adirondack
+12809,Argyle
+12810,Athol
+12811,Bakers Mills
+12812,Blue Mountain Lake
+12814,Bolton Landing
+12815,Brant Lake
+12816,Cambridge
+12817,Chestertown
+12819,Clemons
+12820,Cleverdale
+12821,Comstock
+12822,Corinth
+12823,Cossayuna
+12824,Diamond Point
+12827,Fort Ann
+12828,Fort Edward
+12831,Gansevoort
+12832,Granville
+12833,Greenfield Center
+12834,Greenwich
+12835,Hadley
+12836,Hague
+12837,Hampton
+12838,Hartford
+12839,Hudson Falls
+12841,Huletts Landing
+12842,Indian Lake
+12843,Johnsburg
+12844,Kattskill Bay
+12845,Lake George
+12846,Lake Luzerne
+12847,Long Lake
+12848,Middle Falls
+12849,Middle Granville
+12850,Middle Grove
+12851,Minerva
+12852,Newcomb
+12853,North Creek
+12854,North Granville
+12855,North Hudson
+12856,North River
+12857,Olmstedville
+12858,Paradox
+12859,Porter Corners
+12860,Pottersville
+12861,Putnam Station
+12862,Riparius
+12863,Rock City Falls
+12864,Sabael
+12865,Salem
+12866,Saratoga Springs
+12870,Schroon Lake
+12871,Schuylerville
+12872,Severance
+12873,Shushan
+12874,Silver Bay
+12878,Stony Creek
+12879,Newcomb
+12883,Ticonderoga
+12884,Victory Mills
+12885,Warrensburg
+12886,Wevertown
+12887,Whitehall
+12901,Plattsburgh
+12903,Plattsburgh
+12910,Altona
+12911,Keeseville
+12912,Au Sable Forks
+12913,Bloomingdale
+12914,Bombay
+12915,Brainardsville
+12916,Brushton
+12917,Burke
+12918,Cadyville
+12919,Champlain
+12920,Chateaugay
+12921,Chazy
+12922,Childwold
+12923,Churubusco
+12924,Keeseville
+12926,Constable
+12927,Cranberry Lake
+12928,Crown Point
+12929,Dannemora
+12930,Dickinson Center
+12932,Elizabethtown
+12933,Ellenburg
+12934,Ellenburg Center
+12935,Ellenburg Depot
+12936,Essex
+12937,Fort Covington
+12939,Gabriels
+12941,Jay
+12942,Keene
+12943,Keene Valley
+12944,Keeseville
+12945,Lake Clear
+12946,Lake Placid
+12949,Lawrenceville
+12950,Lewis
+12952,Lyon Mountain
+12953,Malone
+12955,Lyon Mountain
+12956,Mineville
+12957,Moira
+12958,Mooers
+12959,Mooers Forks
+12960,Moriah
+12961,Moriah Center
+12962,Morrisonville
+12964,New Russia
+12965,Nicholville
+12966,North Bangor
+12967,North Lawrence
+12969,Owls Head
+12970,Paul Smiths
+12972,Peru
+12973,Piercefield
+12974,Port Henry
+12975,Port Kent
+12976,Rainbow Lake
+12977,Ray Brook
+12978,Redford
+12979,Rouses Point
+12980,Saint Regis Falls
+12981,Saranac
+12983,Saranac Lake
+12985,Schuyler Falls
+12986,Tupper Lake
+12987,Upper Jay
+12989,Vermontville
+12992,West Chazy
+12993,Westport
+12995,Whippleville
+12996,Willsboro
+12997,Wilmington
+12998,Witherbee
+13020,Apulia Station
+13021,Auburn
+13022,Auburn
+13024,Auburn
+13026,Aurora
+13027,Baldwinsville
+13028,Bernhards Bay
+13029,Brewerton
+13030,Bridgeport
+13031,Camillus
+13032,Canastota
+13033,Cato
+13034,Cayuga
+13035,Cazenovia
+13036,Central Square
+13037,Chittenango
+13039,Cicero
+13040,Cincinnatus
+13041,Clay
+13042,Cleveland
+13043,Clockville
+13044,Constantia
+13045,Cortland
+13051,Delphi Falls
+13052,De Ruyter
+13053,Dryden
+13054,Durhamville
+13056,East Homer
+13057,East Syracuse
+13060,Elbridge
+13061,Erieville
+13062,Etna
+13063,Fabius
+13064,Fair Haven
+13065,Fayette
+13066,Fayetteville
+13068,Freeville
+13069,Fulton
+13071,Genoa
+13072,Georgetown
+13073,Groton
+13074,Hannibal
+13076,Hastings
+13077,Homer
+13078,Jamesville
+13080,Jordan
+13081,King Ferry
+13082,Kirkville
+13083,Lacona
+13084,La Fayette
+13087,Little York
+13088,Liverpool
+13089,Liverpool
+13090,Liverpool
+13092,Locke
+13093,Lycoming
+13101,Mc Graw
+13102,Mc Lean
+13103,Mallory
+13104,Manlius
+13107,Maple View
+13108,Marcellus
+13110,Marietta
+13111,Martville
+13112,Memphis
+13113,Meridian
+13114,Mexico
+13115,Minetto
+13116,Minoa
+13117,Montezuma
+13118,Moravia
+13119,Mottville
+13120,Nedrow
+13121,New Haven
+13122,New Woodstock
+13123,North Bay
+13124,North Pitcher
+13126,Oswego
+13129,Georgetown
+13131,Parish
+13132,Pennellville
+13134,Peterboro
+13135,Phoenix
+13136,Pitcher
+13137,Plainville
+13138,Pompey
+13139,Poplar Ridge
+13140,Port Byron
+13141,Preble
+13142,Pulaski
+13143,Red Creek
+13144,Richland
+13145,Sandy Creek
+13146,Savannah
+13147,Scipio Center
+13148,Seneca Falls
+13152,Skaneateles
+13153,Skaneateles Falls
+13154,South Butler
+13155,South Otselic
+13156,Sterling
+13157,Sylvan Beach
+13158,Truxton
+13159,Tully
+13160,Union Springs
+13162,Verona Beach
+13163,Wampsville
+13164,Warners
+13165,Waterloo
+13166,Weedsport
+13167,West Monroe
+13201,Syracuse
+13202,Syracuse
+13203,Syracuse
+13204,Syracuse
+13205,Syracuse
+13206,Syracuse
+13207,Syracuse
+13208,Syracuse
+13209,Syracuse
+13210,Syracuse
+13211,Syracuse
+13212,Syracuse
+13214,Syracuse
+13215,Syracuse
+13217,Syracuse
+13218,Syracuse
+13219,Syracuse
+13220,Syracuse
+13221,Syracuse
+13224,Syracuse
+13225,Syracuse
+13244,Syracuse
+13250,Syracuse
+13251,Syracuse
+13252,Syracuse
+13260,Syracuse
+13261,Syracuse
+13290,Syracuse
+13301,Alder Creek
+13302,Altmar
+13303,Ava
+13304,Barneveld
+13305,Beaver Falls
+13308,Blossvale
+13309,Boonville
+13310,Bouckville
+13312,Brantingham
+13313,Bridgewater
+13314,Brookfield
+13315,Burlington Flats
+13316,Camden
+13317,Canajoharie
+13318,Cassville
+13319,Chadwicks
+13320,Cherry Valley
+13321,Clark Mills
+13322,Clayville
+13323,Clinton
+13324,Cold Brook
+13325,Constableville
+13326,Cooperstown
+13327,Croghan
+13328,Deansboro
+13329,Dolgeville
+13331,Eagle Bay
+13332,Earlville
+13333,East Springfield
+13334,Eaton
+13335,Edmeston
+13337,Fly Creek
+13338,Forestport
+13339,Fort Plain
+13340,Frankfort
+13341,Franklin Springs
+13342,Garrattsville
+13343,Glenfield
+13345,Greig
+13346,Hamilton
+13348,Hartwick
+13350,Herkimer
+13352,Hinckley
+13353,Hoffmeister
+13354,Holland Patent
+13355,Hubbardsville
+13357,Ilion
+13360,Inlet
+13361,Jordanville
+13362,Knoxboro
+13363,Lee Center
+13364,Leonardsville
+13365,Little Falls
+13367,Lowville
+13368,Lyons Falls
+13401,Mc Connellsville
+13402,Madison
+13403,Marcy
+13404,Martinsburg
+13406,Middleville
+13407,Mohawk
+13408,Morrisville
+13409,Munnsville
+13410,Nelliston
+13411,New Berlin
+13413,New Hartford
+13415,New Lisbon
+13416,Newport
+13417,New York Mills
+13418,North Brookfield
+13420,Old Forge
+13421,Oneida
+13424,Oriskany
+13425,Oriskany Falls
+13426,Orwell
+13428,Palatine Bridge
+13431,Poland
+13433,Port Leyden
+13435,Prospect
+13436,Raquette Lake
+13437,Redfield
+13438,Remsen
+13439,Richfield Springs
+13440,Rome
+13441,Rome
+13442,Rome
+13449,Rome
+13450,Roseboom
+13452,Saint Johnsville
+13454,Salisbury Center
+13455,Sangerfield
+13456,Sauquoit
+13457,Schuyler Lake
+13459,Sharon Springs
+13460,Sherburne
+13461,Sherrill
+13464,Smyrna
+13465,Solsville
+13468,Springfield Center
+13469,Stittville
+13470,Stratford
+13471,Taberg
+13472,Thendara
+13473,Turin
+13475,Van Hornesville
+13476,Vernon
+13477,Vernon Center
+13478,Verona
+13479,Washington Mills
+13480,Waterville
+13482,West Burlington
+13483,Westdale
+13484,West Eaton
+13485,West Edmeston
+13486,Westernville
+13488,Westford
+13489,West Leyden
+13490,Westmoreland
+13491,West Winfield
+13492,Whitesboro
+13493,Williamstown
+13494,Woodgate
+13495,Yorkville
+13501,Utica
+13502,Utica
+13503,Utica
+13504,Utica
+13505,Utica
+13599,Utica
+13601,Watertown
+13602,Fort Drum
+13603,Watertown
+13605,Adams
+13606,Adams Center
+13607,Alexandria Bay
+13608,Antwerp
+13610,Rodman
+13611,Belleville
+13612,Black River
+13613,Brasher Falls
+13614,Brier Hill
+13615,Brownville
+13616,Calcium
+13617,Canton
+13618,Cape Vincent
+13619,Carthage
+13620,Castorland
+13621,Chase Mills
+13622,Chaumont
+13623,Chippewa Bay
+13624,Clayton
+13625,Colton
+13626,Copenhagen
+13627,Deer River
+13628,Deferiet
+13630,De Kalb Junction
+13631,Denmark
+13632,Depauville
+13633,De Peyster
+13634,Dexter
+13635,Edwards
+13636,Ellisburg
+13637,Evans Mills
+13638,Felts Mills
+13639,Fine
+13640,Wellesley Island
+13641,Fishers Landing
+13642,Gouverneur
+13643,Great Bend
+13645,Hailesboro
+13646,Hammond
+13647,Hannawa Falls
+13648,Harrisville
+13649,Helena
+13650,Henderson
+13651,Henderson Harbor
+13652,Hermon
+13654,Heuvelton
+13655,Hogansburg
+13656,La Fargeville
+13657,Limerick
+13658,Lisbon
+13659,Lorraine
+13660,Madrid
+13661,Mannsville
+13662,Massena
+13664,Morristown
+13665,Natural Bridge
+13666,Newton Falls
+13667,Norfolk
+13668,Norwood
+13669,Ogdensburg
+13670,Oswegatchie
+13671,Oxbow
+13672,Parishville
+13673,Philadelphia
+13674,Pierrepont Manor
+13675,Plessis
+13676,Potsdam
+13677,Pyrites
+13678,Raymondville
+13679,Redwood
+13680,Rensselaer Falls
+13681,Richville
+13682,Rodman
+13683,Rooseveltown
+13684,Russell
+13685,Sackets Harbor
+13687,South Colton
+13688,South Rutland
+13690,Star Lake
+13691,Theresa
+13692,Thousand Island Park
+13693,Three Mile Bay
+13694,Waddington
+13695,Wanakena
+13696,West Stockholm
+13697,Winthrop
+13699,Potsdam
+13730,Afton
+13731,Andes
+13732,Apalachin
+13733,Bainbridge
+13734,Barton
+13736,Berkshire
+13737,Bible School Park
+13738,Blodgett Mills
+13739,Bloomville
+13740,Bovina Center
+13743,Candor
+13744,Castle Creek
+13745,Chenango Bridge
+13746,Chenango Forks
+13747,Colliersville
+13748,Conklin
+13749,Corbettsville
+13750,Davenport
+13751,Davenport Center
+13752,De Lancey
+13753,Delhi
+13754,Deposit
+13755,Downsville
+13756,East Branch
+13757,East Meredith
+13758,East Pharsalia
+13760,Endicott
+13761,Endicott
+13762,Endwell
+13763,Endicott
+13774,Fishs Eddy
+13775,Franklin
+13776,Gilbertsville
+13777,Glen Aubrey
+13778,Greene
+13780,Guilford
+13782,Hamden
+13783,Hancock
+13784,Harford
+13786,Harpersfield
+13787,Harpursville
+13788,Hobart
+13790,Johnson City
+13794,Killawog
+13795,Kirkwood
+13796,Laurens
+13797,Lisle
+13801,Mc Donough
+13802,Maine
+13803,Marathon
+13804,Masonville
+13806,Meridale
+13807,Milford
+13808,Morris
+13809,Mount Upton
+13810,Mount Vision
+13811,Newark Valley
+13812,Nichols
+13813,Nineveh
+13814,North Norwich
+13815,Norwich
+13820,Oneonta
+13825,Otego
+13826,Ouaquaga
+13827,Owego
+13830,Oxford
+13832,Plymouth
+13833,Port Crane
+13834,Portlandville
+13835,Richford
+13837,Shinhopple
+13838,Sidney
+13839,Sidney Center
+13840,Smithboro
+13841,Smithville Flats
+13842,South Kortright
+13843,South New Berlin
+13844,South Plymouth
+13845,Tioga Center
+13846,Treadwell
+13847,Trout Creek
+13848,Tunnel
+13849,Unadilla
+13850,Vestal
+13851,Vestal
+13856,Walton
+13859,Wells Bridge
+13860,West Davenport
+13861,West Oneonta
+13862,Whitney Point
+13863,Willet
+13864,Willseyville
+13865,Windsor
+13901,Binghamton
+13902,Binghamton
+13903,Binghamton
+13904,Binghamton
+13905,Binghamton
+14001,Akron
+14003,Alabama
+14004,Alden
+14005,Alexander
+14006,Angola
+14008,Appleton
+14009,Arcade
+14010,Athol Springs
+14011,Attica
+14012,Barker
+14013,Basom
+14020,Batavia
+14021,Batavia
+14024,Bliss
+14025,Boston
+14026,Bowmansville
+14027,Brant
+14028,Burt
+14029,Centerville
+14030,Chaffee
+14031,Clarence
+14032,Clarence Center
+14033,Colden
+14034,Collins
+14035,Collins Center
+14036,Corfu
+14037,Cowlesville
+14038,Crittenden
+14039,Dale
+14040,Darien Center
+14041,Dayton
+14042,Delevan
+14043,Depew
+14047,Derby
+14048,Dunkirk
+14051,East Amherst
+14052,East Aurora
+14054,East Bethany
+14055,East Concord
+14056,East Pembroke
+14057,Eden
+14058,Elba
+14059,Elma
+14060,Farmersville Station
+14061,Farnham
+14062,Forestville
+14063,Fredonia
+14065,Freedom
+14066,Gainesville
+14067,Gasport
+14068,Getzville
+14069,Glenwood
+14070,Gowanda
+14072,Grand Island
+14075,Hamburg
+14080,Holland
+14081,Irving
+14082,Java Center
+14083,Java Village
+14085,Lake View
+14086,Lancaster
+14091,Lawtons
+14092,Lewiston
+14094,Lockport
+14095,Lockport
+14098,Lyndonville
+14101,Machias
+14102,Marilla
+14103,Medina
+14105,Middleport
+14107,Model City
+14108,Newfane
+14109,Niagara University
+14110,North Boston
+14111,North Collins
+14112,North Evans
+14113,North Java
+14120,North Tonawanda
+14125,Oakfield
+14126,Olcott
+14127,Orchard Park
+14129,Perrysburg
+14130,Pike
+14131,Ransomville
+14132,Sanborn
+14133,Sandusky
+14134,Sardinia
+14135,Sheridan
+14136,Silver Creek
+14138,South Dayton
+14139,South Wales
+14140,Spring Brook
+14141,Springville
+14143,Stafford
+14144,Stella Niagara
+14145,Strykersville
+14150,Tonawanda
+14151,Tonawanda
+14166,Van Buren Point
+14167,Varysburg
+14168,Versailles
+14169,Wales Center
+14170,West Falls
+14171,West Valley
+14172,Wilson
+14173,Yorkshire
+14174,Youngstown
+14201,Buffalo
+14202,Buffalo
+14203,Buffalo
+14204,Buffalo
+14205,Buffalo
+14206,Buffalo
+14207,Buffalo
+14208,Buffalo
+14209,Buffalo
+14210,Buffalo
+14211,Buffalo
+14212,Buffalo
+14213,Buffalo
+14214,Buffalo
+14215,Buffalo
+14216,Buffalo
+14217,Buffalo
+14218,Buffalo
+14219,Buffalo
+14220,Buffalo
+14221,Buffalo
+14222,Buffalo
+14223,Buffalo
+14224,Buffalo
+14225,Buffalo
+14226,Buffalo
+14227,Buffalo
+14228,Buffalo
+14231,Buffalo
+14233,Buffalo
+14240,Buffalo
+14241,Buffalo
+14260,Buffalo
+14261,Buffalo
+14263,Buffalo
+14264,Buffalo
+14265,Buffalo
+14267,Buffalo
+14269,Buffalo
+14270,Buffalo
+14272,Buffalo
+14273,Buffalo
+14276,Buffalo
+14280,Buffalo
+14301,Niagara Falls
+14302,Niagara Falls
+14303,Niagara Falls
+14304,Niagara Falls
+14305,Niagara Falls
+14410,Adams Basin
+14411,Albion
+14413,Alton
+14414,Avon
+14415,Bellona
+14416,Bergen
+14418,Branchport
+14420,Brockport
+14422,Byron
+14423,Caledonia
+14424,Canandaigua
+14425,Farmington
+14427,Castile
+14428,Churchville
+14429,Clarendon
+14430,Clarkson
+14432,Clifton Springs
+14433,Clyde
+14435,Conesus
+14437,Dansville
+14441,Dresden
+14443,East Bloomfield
+14445,East Rochester
+14449,East Williamson
+14450,Fairport
+14452,Fancher
+14453,Fishers
+14454,Geneseo
+14456,Geneva
+14461,Gorham
+14462,Groveland
+14463,Hall
+14464,Hamlin
+14466,Hemlock
+14467,Henrietta
+14468,Hilton
+14469,Bloomfield
+14470,Holley
+14471,Honeoye
+14472,Honeoye Falls
+14475,Ionia
+14476,Kendall
+14477,Kent
+14478,Keuka Park
+14479,Knowlesville
+14480,Lakeville
+14481,Leicester
+14482,Le Roy
+14485,Lima
+14486,Linwood
+14487,Livonia
+14488,Livonia Center
+14489,Lyons
+14502,Macedon
+14504,Manchester
+14505,Marion
+14506,Mendon
+14507,Middlesex
+14508,Morton
+14510,Mount Morris
+14511,Mumford
+14512,Naples
+14513,Newark
+14514,North Chili
+14515,North Greece
+14516,North Rose
+14517,Nunda
+14518,Oaks Corners
+14519,Ontario
+14520,Ontario Center
+14521,Ovid
+14522,Palmyra
+14525,Pavilion
+14526,Penfield
+14527,Penn Yan
+14529,Perkinsville
+14530,Perry
+14532,Phelps
+14533,Piffard
+14534,Pittsford
+14536,Portageville
+14537,Port Gibson
+14538,Pultneyville
+14539,Retsof
+14541,Romulus
+14542,Rose
+14543,Rush
+14544,Rushville
+14545,Scottsburg
+14546,Scottsville
+14547,Seneca Castle
+14548,Shortsville
+14549,Silver Lake
+14550,Silver Springs
+14551,Sodus
+14555,Sodus Point
+14556,Sonyea
+14557,South Byron
+14558,South Lima
+14559,Spencerport
+14560,Springwater
+14561,Stanley
+14563,Union Hill
+14564,Victor
+14568,Walworth
+14569,Warsaw
+14571,Waterport
+14572,Wayland
+14580,Webster
+14585,West Bloomfield
+14586,West Henrietta
+14588,Willard
+14589,Williamson
+14590,Wolcott
+14591,Wyoming
+14592,York
+14601,Rochester
+14602,Rochester
+14603,Rochester
+14604,Rochester
+14605,Rochester
+14606,Rochester
+14607,Rochester
+14608,Rochester
+14609,Rochester
+14610,Rochester
+14611,Rochester
+14612,Rochester
+14613,Rochester
+14614,Rochester
+14615,Rochester
+14616,Rochester
+14617,Rochester
+14618,Rochester
+14619,Rochester
+14620,Rochester
+14621,Rochester
+14622,Rochester
+14623,Rochester
+14624,Rochester
+14625,Rochester
+14626,Rochester
+14627,Rochester
+14638,Rochester
+14639,Rochester
+14642,Rochester
+14643,Rochester
+14644,Rochester
+14645,Rochester
+14646,Rochester
+14647,Rochester
+14649,Rochester
+14650,Rochester
+14651,Rochester
+14652,Rochester
+14653,Rochester
+14660,Rochester
+14664,Rochester
+14673,Rochester
+14683,Rochester
+14692,Rochester
+14694,Rochester
+14701,Jamestown
+14702,Jamestown
+14703,Jamestown
+14704,Jamestown
+14706,Allegany
+14707,Allentown
+14708,Alma
+14709,Angelica
+14710,Ashville
+14711,Belfast
+14712,Bemus Point
+14714,Black Creek
+14715,Bolivar
+14716,Brocton
+14717,Caneadea
+14718,Cassadaga
+14719,Cattaraugus
+14720,Celoron
+14721,Ceres
+14722,Chautauqua
+14723,Cherry Creek
+14724,Clymer
+14726,Conewango Valley
+14727,Cuba
+14728,Dewittville
+14729,East Otto
+14730,East Randolph
+14731,Ellicottville
+14732,Ellington
+14733,Falconer
+14735,Fillmore
+14736,Findley Lake
+14737,Franklinville
+14738,Frewsburg
+14739,Friendship
+14740,Gerry
+14741,Great Valley
+14742,Greenhurst
+14743,Hinsdale
+14744,Houghton
+14745,Hume
+14747,Kennedy
+14748,Kill Buck
+14750,Lakewood
+14751,Leon
+14752,Lily Dale
+14753,Limestone
+14754,Little Genesee
+14755,Little Valley
+14756,Maple Springs
+14757,Mayville
+14758,Niobe
+14760,Olean
+14766,Otto
+14767,Panama
+14769,Portland
+14770,Portville
+14772,Randolph
+14774,Richburg
+14775,Ripley
+14776,Rossburg
+14777,Rushford
+14778,Saint Bonaventure
+14779,Salamanca
+14781,Sherman
+14782,Sinclairville
+14783,Steamburg
+14784,Stockton
+14785,Stow
+14786,West Clarksville
+14787,Westfield
+14788,Westons Mills
+14801,Addison
+14802,Alfred
+14803,Alfred Station
+14804,Almond
+14805,Alpine
+14806,Andover
+14807,Arkport
+14808,Atlanta
+14809,Avoca
+14810,Bath
+14812,Beaver Dams
+14813,Belmont
+14814,Big Flats
+14815,Bradford
+14816,Breesport
+14817,Brooktondale
+14818,Burdett
+14819,Cameron
+14820,Cameron Mills
+14821,Campbell
+14822,Canaseraga
+14823,Canisteo
+14824,Cayuta
+14825,Chemung
+14826,Cohocton
+14827,Coopers Plains
+14830,Corning
+14831,Corning
+14836,Dalton
+14837,Dundee
+14838,Erin
+14839,Greenwood
+14840,Hammondsport
+14841,Hector
+14842,Himrod
+14843,Hornell
+14844,Horseheads
+14845,Horseheads
+14846,Hunt
+14847,Interlaken
+14850,Ithaca
+14851,Ithaca
+14852,Ithaca
+14853,Ithaca
+14854,Jacksonville
+14855,Jasper
+14856,Kanona
+14857,Lakemont
+14858,Lindley
+14859,Lockwood
+14860,Lodi
+14861,Lowman
+14863,Mecklenburg
+14864,Millport
+14865,Montour Falls
+14867,Newfield
+14869,Odessa
+14870,Painted Post
+14871,Pine City
+14872,Pine Valley
+14873,Prattsburgh
+14874,Pulteney
+14876,Reading Center
+14877,Rexville
+14878,Rock Stream
+14879,Savona
+14880,Scio
+14881,Slaterville Springs
+14882,Lansing
+14883,Spencer
+14884,Swain
+14885,Troupsburg
+14886,Trumansburg
+14887,Tyrone
+14889,Van Etten
+14891,Watkins Glen
+14892,Waverly
+14893,Wayne
+14894,Wellsburg
+14895,Wellsville
+14897,Whitesville
+14898,Woodhull
+14901,Elmira
+14902,Elmira
+14903,Elmira
+14904,Elmira
+14905,Elmira
+14925,Elmira
+88901,The Lakes
+88905,The Lakes
+89001,Alamo
+89003,Beatty
+89004,Blue Diamond
+89005,Boulder City
+89006,Boulder City
+89007,Bunkerville
+89008,Caliente
+89009,Henderson
+89010,Dyer
+89011,Henderson
+89012,Henderson
+89013,Goldfield
+89014,Henderson
+89015,Henderson
+89016,Henderson
+89017,Hiko
+89018,Indian Springs
+89019,Jean
+89020,Amargosa Valley
+89021,Logandale
+89022,Manhattan
+89023,Mercury
+89024,Mesquite
+89025,Moapa
+89026,Jean
+89027,Mesquite
+89028,Laughlin
+89029,Laughlin
+89030,North Las Vegas
+89031,North Las Vegas
+89032,North Las Vegas
+89033,North Las Vegas
+89036,North Las Vegas
+89039,Cal Nev Ari
+89040,Overton
+89041,Pahrump
+89042,Panaca
+89043,Pioche
+89045,Round Mountain
+89046,Searchlight
+89047,Silverpeak
+89048,Pahrump
+89049,Tonopah
+89052,Henderson
+89053,Henderson
+89070,Indian Springs
+89101,Las Vegas
+89102,Las Vegas
+89103,Las Vegas
+89104,Las Vegas
+89106,Las Vegas
+89107,Las Vegas
+89108,Las Vegas
+89109,Las Vegas
+89110,Las Vegas
+89111,Las Vegas
+89112,Las Vegas
+89113,Las Vegas
+89114,Las Vegas
+89115,Las Vegas
+89116,Las Vegas
+89117,Las Vegas
+89118,Las Vegas
+89119,Las Vegas
+89120,Las Vegas
+89121,Las Vegas
+89122,Las Vegas
+89123,Las Vegas
+89124,Las Vegas
+89125,Las Vegas
+89126,Las Vegas
+89127,Las Vegas
+89128,Las Vegas
+89129,Las Vegas
+89130,Las Vegas
+89131,Las Vegas
+89132,Las Vegas
+89133,Las Vegas
+89134,Las Vegas
+89135,Las Vegas
+89137,Las Vegas
+89138,Las Vegas
+89139,Las Vegas
+89141,Las Vegas
+89142,Las Vegas
+89143,Las Vegas
+89144,Las Vegas
+89145,Las Vegas
+89146,Las Vegas
+89147,Las Vegas
+89148,Las Vegas
+89149,Las Vegas
+89150,Las Vegas
+89151,Las Vegas
+89152,Las Vegas
+89153,Las Vegas
+89154,Las Vegas
+89155,Las Vegas
+89156,Las Vegas
+89158,Las Vegas
+89159,Las Vegas
+89160,Las Vegas
+89163,The Lakes
+89164,Las Vegas
+89170,Las Vegas
+89173,Las Vegas
+89177,Las Vegas
+89180,Las Vegas
+89185,Las Vegas
+89191,Nellis AFB
+89193,Las Vegas
+89195,Las Vegas
+89199,Las Vegas
+89301,Ely
+89310,Austin
+89311,Baker
+89314,Duckwater
+89315,Ely
+89316,Eureka
+89317,Lund
+89318,Mc Gill
+89319,Ruth
+89402,Crystal Bay
+89403,Dayton
+89404,Denio
+89405,Empire
+89406,Fallon
+89407,Fallon
+89408,Fernley
+89409,Gabbs
+89410,Gardnerville
+89411,Genoa
+89412,Gerlach
+89413,Glenbrook
+89414,Golconda
+89415,Hawthorne
+89418,Imlay
+89419,Lovelock
+89420,Luning
+89421,Mc Dermitt
+89422,Mina
+89423,Minden
+89424,Nixon
+89425,Orovada
+89426,Paradise Valley
+89427,Schurz
+89428,Silver City
+89429,Silver Springs
+89430,Smith
+89431,Sparks
+89432,Sparks
+89433,Sun Valley
+89434,Sparks
+89435,Sparks
+89436,Sparks
+89438,Valmy
+89439,Verdi
+89440,Virginia City
+89442,Wadsworth
+89444,Wellington
+89445,Winnemucca
+89446,Winnemucca
+89447,Yerington
+89448,Zephyr Cove
+89449,Stateline
+89450,Incline Village
+89451,Incline Village
+89452,Incline Village
+89496,Fallon
+89501,Reno
+89502,Reno
+89503,Reno
+89504,Reno
+89505,Reno
+89506,Reno
+89507,Reno
+89509,Reno
+89510,Reno
+89511,Reno
+89512,Reno
+89513,Reno
+89515,Reno
+89520,Reno
+89523,Reno
+89533,Reno
+89557,Reno
+89564,Reno
+89570,Reno
+89595,Reno
+89599,Reno
+89701,Carson City
+89702,Carson City
+89703,Carson City
+89704,Washoe Valley
+89705,Carson City
+89706,Carson City
+89710,Carson City
+89711,Carson City
+89712,Carson City
+89713,Carson City
+89714,Carson City
+89721,Carson City
+89801,Elko
+89802,Elko
+89803,Elko
+89815,Spring Creek
+89820,Battle Mountain
+89821,Crescent Valley
+89822,Carlin
+89823,Deeth
+89824,Halleck
+89825,Jackpot
+89826,Jarbidge
+89828,Lamoille
+89830,Montello
+89831,Mountain City
+89832,Owyhee
+89833,Ruby Valley
+89834,Tuscarora
+89835,Wells
+89883,West Wendover
+87001,Algodones
+87002,Belen
+87004,Bernalillo
+87005,Bluewater
+87006,Bosque
+87007,Casa Blanca
+87008,Cedar Crest
+87009,Cedarvale
+87010,Cerrillos
+87011,Claunch
+87012,Coyote
+87013,Cuba
+87014,Cubero
+87015,Edgewood
+87016,Estancia
+87017,Gallina
+87018,Counselor
+87020,Grants
+87021,Milan
+87022,Isleta
+87023,Jarales
+87024,Jemez Pueblo
+87025,Jemez Springs
+87026,Laguna
+87027,La Jara
+87028,La Joya
+87029,Lindrith
+87031,Los Lunas
+87032,Mc Intosh
+87034,Pueblo of Acoma
+87035,Moriarty
+87036,Mountainair
+87037,Nageezi
+87038,New Laguna
+87040,Paguate
+87041,Pena Blanca
+87042,Peralta
+87043,Placitas
+87044,Ponderosa
+87045,Prewitt
+87046,Regina
+87047,Sandia Park
+87048,Corrales
+87049,San Fidel
+87051,San Rafael
+87052,Santo Domingo Pueblo
+87053,San Ysidro
+87056,Stanley
+87057,Tajique
+87059,Tijeras
+87060,Tome
+87061,Torreon
+87062,Veguita
+87063,Willard
+87064,Youngsville
+87068,Bosque Farms
+87070,Clines Corners
+87072,Cochiti Pueblo
+87083,Cochiti Lake
+87101,Albuquerque
+87102,Albuquerque
+87103,Albuquerque
+87104,Albuquerque
+87105,Albuquerque
+87106,Albuquerque
+87107,Albuquerque
+87108,Albuquerque
+87109,Albuquerque
+87110,Albuquerque
+87111,Albuquerque
+87112,Albuquerque
+87113,Albuquerque
+87114,Albuquerque
+87115,Albuquerque
+87116,Albuquerque
+87117,Kirtland AFB
+87118,Albuquerque
+87119,Albuquerque
+87120,Albuquerque
+87121,Albuquerque
+87122,Albuquerque
+87123,Albuquerque
+87124,Rio Rancho
+87125,Albuquerque
+87131,Albuquerque
+87140,Albuquerque
+87153,Albuquerque
+87154,Albuquerque
+87158,Albuquerque
+87174,Rio Rancho
+87176,Albuquerque
+87180,Albuquerque
+87181,Albuquerque
+87184,Albuquerque
+87185,Albuquerque
+87187,Albuquerque
+87190,Albuquerque
+87191,Albuquerque
+87192,Albuquerque
+87193,Albuquerque
+87194,Albuquerque
+87195,Albuquerque
+87196,Albuquerque
+87197,Albuquerque
+87198,Albuquerque
+87199,Albuquerque
+87201,Albuquerque
+87300,Gallup
+87301,Gallup
+87302,Gallup
+87305,Gallup
+87310,Brimhall
+87311,Church Rock
+87312,Continental Divide
+87313,Crownpoint
+87315,Fence Lake
+87316,Fort Wingate
+87317,Gamerco
+87319,Mentmore
+87320,Mexican Springs
+87321,Ramah
+87322,Rehoboth
+87323,Thoreau
+87325,Tohatchi
+87326,Vanderwagen
+87327,Zuni
+87328,Navajo
+87347,Jamestown
+87357,Pinehill
+87364,Sheep Springs
+87365,Smith Lake
+87375,Yatahey
+87401,Farmington
+87402,Farmington
+87410,Aztec
+87412,Blanco
+87413,Bloomfield
+87415,Flora Vista
+87416,Fruitland
+87417,Kirtland
+87418,La Plata
+87419,Navajo Dam
+87420,Shiprock
+87421,Waterflow
+87455,Newcomb
+87461,Sanostee
+87499,Farmington
+87500,Santa FE
+87501,Santa FE
+87502,Santa FE
+87503,Santa FE
+87504,Santa FE
+87505,Santa FE
+87506,Santa FE
+87509,Santa FE
+87510,Abiquiu
+87511,Alcalde
+87512,Amalia
+87513,Arroyo Hondo
+87514,Arroyo Seco
+87515,Canjilon
+87516,Canones
+87517,Carson
+87518,Cebolla
+87519,Cerro
+87520,Chama
+87521,Chamisal
+87522,Chimayo
+87523,Cordova
+87524,Costilla
+87525,Taos Ski Valley
+87527,Dixon
+87528,Dulce
+87529,El Prado
+87530,El Rito
+87531,Embudo
+87532,Espanola
+87533,Espanola
+87535,Glorieta
+87537,Hernandez
+87538,Ilfeld
+87539,La Madera
+87540,Lamy
+87543,Llano
+87544,Los Alamos
+87545,Los Alamos
+87548,Medanales
+87549,Ojo Caliente
+87551,Los Ojos
+87552,Pecos
+87553,Penasco
+87554,Petaca
+87556,Questa
+87557,Ranchos de Taos
+87558,Red River
+87560,Ribera
+87562,Rowe
+87564,San Cristobal
+87565,San Jose
+87566,San Juan Pueblo
+87567,Santa Cruz
+87569,Serafina
+87571,Taos
+87573,Tererro
+87574,Tesuque
+87575,Tierra Amarilla
+87576,Trampas
+87577,Tres Piedras
+87578,Truchas
+87579,Vadito
+87580,Valdez
+87581,Vallecitos
+87582,Velarde
+87583,Villanueva
+87592,Santa FE
+87594,Santa FE
+87701,Las Vegas
+87710,Angel Fire
+87711,Anton Chico
+87712,Buena Vista
+87713,Chacon
+87714,Cimarron
+87715,Cleveland
+87718,Eagle Nest
+87722,Guadalupita
+87723,Holman
+87724,La Loma
+87728,Maxwell
+87729,Miami
+87730,Mills
+87731,Montezuma
+87732,Mora
+87733,Mosquero
+87734,Ocate
+87735,Ojo Feliz
+87736,Rainsville
+87740,Raton
+87742,Rociada
+87743,Roy
+87745,Sapello
+87746,Solano
+87747,Springer
+87749,Ute Park
+87750,Valmora
+87752,Wagon Mound
+87753,Watrous
+87801,Socorro
+87820,Aragon
+87821,Datil
+87823,Lemitar
+87824,Luna
+87825,Magdalena
+87827,Pie Town
+87828,Polvadera
+87829,Quemado
+87830,Reserve
+87831,San Acacia
+87832,San Antonio
+87901,Truth or Consequences
+87930,Arrey
+87931,Caballo
+87933,Derry
+87935,Elephant Butte
+87936,Garfield
+87937,Hatch
+87939,Monticello
+87940,Rincon
+87941,Salem
+87942,Williamsburg
+87943,Winston
+88001,Las Cruces
+88002,White Sands Missile Range
+88003,Las Cruces
+88004,Las Cruces
+88005,Las Cruces
+88006,Las Cruces
+88008,Santa Teresa
+88009,Playas
+88011,Las Cruces
+88012,Las Cruces
+88020,Animas
+88021,Anthony
+88022,Arenas Valley
+88023,Bayard
+88024,Berino
+88025,Buckhorn
+88026,Santa Clara
+88027,Chamberino
+88028,Cliff
+88029,Columbus
+88030,Deming
+88031,Deming
+88032,Dona Ana
+88033,Fairacres
+88034,Faywood
+88036,Fort Bayard
+88038,Gila
+88039,Glenwood
+88040,Hachita
+88041,Hanover
+88042,Hillsboro
+88043,Hurley
+88044,La Mesa
+88045,Lordsburg
+88046,Mesilla
+88047,Mesilla Park
+88048,Mesquite
+88049,Mimbres
+88051,Mule Creek
+88052,Organ
+88053,Pinos Altos
+88054,Radium Springs
+88055,Redrock
+88056,Rodeo
+88058,San Miguel
+88061,Silver City
+88062,Silver City
+88063,Sunland Park
+88065,Tyrone
+88072,Vado
+88101,Clovis
+88102,Clovis
+88103,Cannon AFB
+88112,Broadview
+88113,Causey
+88114,Crossroads
+88115,Dora
+88116,Elida
+88118,Floyd
+88119,Fort Sumner
+88120,Grady
+88121,House
+88122,Kenna
+88123,Lingo
+88124,Melrose
+88125,Milnesand
+88126,Pep
+88130,Portales
+88132,Rogers
+88133,Saint Vrain
+88134,Taiban
+88135,Texico
+88136,Yeso
+88201,Roswell
+88202,Roswell
+88210,Artesia
+88211,Artesia
+88213,Caprock
+88220,Carlsbad
+88221,Carlsbad
+88230,Dexter
+88231,Eunice
+88232,Hagerman
+88240,Hobbs
+88241,Hobbs
+88242,Hobbs
+88244,Hobbs
+88250,Hope
+88252,Jal
+88253,Lake Arthur
+88254,Lakewood
+88255,Loco Hills
+88256,Loving
+88260,Lovington
+88262,Mc Donald
+88263,Malaga
+88264,Maljamar
+88265,Monument
+88267,Tatum
+88268,Whites City
+88301,Carrizozo
+88310,Alamogordo
+88311,Alamogordo
+88312,Alto
+88314,Bent
+88316,Capitan
+88317,Cloudcroft
+88318,Corona
+88321,Encino
+88323,Fort Stanton
+88324,Glencoe
+88325,High Rolls Mountain Park
+88330,Holloman Air Force Base
+88336,Hondo
+88337,La Luz
+88338,Lincoln
+88339,Mayhill
+88340,Mescalero
+88341,Nogal
+88342,Orogrande
+88343,Picacho
+88344,Pinon
+88345,Ruidoso
+88346,Ruidoso Downs
+88347,Sacramento
+88348,San Patricio
+88349,Sunspot
+88350,Timberon
+88351,Tinnie
+88352,Tularosa
+88353,Vaughn
+88354,Weed
+88355,Ruidoso
+88401,Tucumcari
+88410,Amistad
+88411,Bard
+88414,Capulin
+88415,Clayton
+88416,Conchas Dam
+88417,Cuervo
+88418,Des Moines
+88419,Folsom
+88421,Garita
+88422,Gladstone
+88424,Grenville
+88426,Logan
+88427,Mc Alister
+88429,Mount Dora
+88430,Nara Visa
+88431,Newkirk
+88433,Quay
+88434,San Jon
+88435,Santa Rosa
+88436,Sedan
+88437,Seneca
+88439,Trementina
+88441,Bell Ranch
+07001,Avenel
+07002,Bayonne
+07003,Bloomfield
+07004,Fairfield
+07005,Boonton
+07006,Caldwell
+07007,Caldwell
+07008,Carteret
+07009,Cedar Grove
+07010,Cliffside Park
+07011,Clifton
+07012,Clifton
+07013,Clifton
+07014,Clifton
+07015,Clifton
+07016,Cranford
+07017,East Orange
+07018,East Orange
+07019,East Orange
+07020,Edgewater
+07021,Essex Fells
+07022,Fairview
+07023,Fanwood
+07024,Fort Lee
+07026,Garfield
+07027,Garwood
+07028,Glen Ridge
+07029,Harrison
+07030,Hoboken
+07031,North Arlington
+07032,Kearny
+07033,Kenilworth
+07034,Lake Hiawatha
+07035,Lincoln Park
+07036,Linden
+07039,Livingston
+07040,Maplewood
+07041,Millburn
+07042,Montclair
+07043,Montclair
+07044,Verona
+07045,Montville
+07046,Mountain Lakes
+07047,North Bergen
+07050,Orange
+07051,Orange
+07052,West Orange
+07054,Parsippany
+07055,Passaic
+07057,Wallington
+07058,Pine Brook
+07059,Warren
+07060,Plainfield
+07061,Plainfield
+07062,Plainfield
+07063,Plainfield
+07064,Port Reading
+07065,Rahway
+07066,Clark
+07067,Colonia
+07068,Roseland
+07070,Rutherford
+07071,Lyndhurst
+07072,Carlstadt
+07073,East Rutherford
+07074,Moonachie
+07075,Wood Ridge
+07076,Scotch Plains
+07077,Sewaren
+07078,Short Hills
+07079,South Orange
+07080,South Plainfield
+07081,Springfield
+07082,Towaco
+07083,Union
+07087,Union City
+07088,Vauxhall
+07090,Westfield
+07091,Westfield
+07092,Mountainside
+07093,West New York
+07094,Secaucus
+07095,Woodbridge
+07096,Secaucus
+07097,Jersey City
+07099,Kearny
+07101,Newark
+07102,Newark
+07103,Newark
+07104,Newark
+07105,Newark
+07106,Newark
+07107,Newark
+07108,Newark
+07109,Belleville
+07110,Nutley
+07111,Irvington
+07112,Newark
+07114,Newark
+07175,Newark
+07182,Newark
+07184,Newark
+07188,Newark
+07189,Newark
+07191,Newark
+07192,Newark
+07193,Newark
+07194,Newark
+07195,Newark
+07197,Newark
+07198,Newark
+07199,Newark
+07201,Elizabeth
+07202,Elizabeth
+07203,Roselle
+07204,Roselle Park
+07205,Hillside
+07206,Elizabeth
+07207,Elizabeth
+07208,Elizabeth
+07302,Jersey City
+07303,Jersey City
+07304,Jersey City
+07305,Jersey City
+07306,Jersey City
+07307,Jersey City
+07308,Jersey City
+07309,Jersey City
+07310,Jersey City
+07311,Jersey City
+07399,Jersey City
+07401,Allendale
+07403,Bloomingdale
+07405,Butler
+07407,Elmwood Park
+07410,Fair Lawn
+07416,Franklin
+07417,Franklin Lakes
+07418,Glenwood
+07419,Hamburg
+07420,Haskell
+07421,Hewitt
+07422,Highland Lakes
+07423,Ho Ho Kus
+07424,Little Falls
+07428,Mc Afee
+07430,Mahwah
+07432,Midland Park
+07435,Newfoundland
+07436,Oakland
+07438,Oak Ridge
+07439,Ogdensburg
+07440,Pequannock
+07442,Pompton Lakes
+07444,Pompton Plains
+07446,Ramsey
+07450,Ridgewood
+07451,Ridgewood
+07452,Glen Rock
+07456,Ringwood
+07457,Riverdale
+07458,Saddle River
+07460,Stockholm
+07461,Sussex
+07462,Vernon
+07463,Waldwick
+07465,Wanaque
+07470,Wayne
+07474,Wayne
+07477,Wayne
+07480,West Milford
+07481,Wyckoff
+07495,Mahwah
+07498,Mahwah
+07501,Paterson
+07502,Paterson
+07503,Paterson
+07504,Paterson
+07505,Paterson
+07506,Hawthorne
+07507,Hawthorne
+07508,Haledon
+07509,Paterson
+07510,Paterson
+07511,Totowa
+07512,Totowa
+07513,Paterson
+07514,Paterson
+07522,Paterson
+07524,Paterson
+07530,Paterson
+07533,Paterson
+07538,Haledon
+07543,Paterson
+07544,Paterson
+07601,Hackensack
+07602,Hackensack
+07603,Bogota
+07604,Hasbrouck Heights
+07605,Leonia
+07606,South Hackensack
+07607,Maywood
+07608,Teterboro
+07620,Alpine
+07621,Bergenfield
+07624,Closter
+07626,Cresskill
+07627,Demarest
+07628,Dumont
+07630,Emerson
+07631,Englewood
+07632,Englewood Cliffs
+07640,Harrington Park
+07641,Haworth
+07642,Hillsdale
+07643,Little Ferry
+07644,Lodi
+07645,Montvale
+07646,New Milford
+07647,Northvale
+07648,Norwood
+07649,Oradell
+07650,Palisades Park
+07652,Paramus
+07653,Paramus
+07656,Park Ridge
+07657,Ridgefield
+07660,Ridgefield Park
+07661,River Edge
+07662,Rochelle Park
+07663,Saddle Brook
+07666,Teaneck
+07670,Tenafly
+07675,Westwood
+07701,Red Bank
+07702,Shrewsbury
+07703,Fort Monmouth
+07704,Fair Haven
+07709,Allenhurst
+07710,Adelphia
+07711,Allenhurst
+07712,Asbury Park
+07715,Belmar
+07716,Atlantic Highlands
+07717,Avon by the Sea
+07718,Belford
+07719,Belmar
+07720,Bradley Beach
+07721,Cliffwood
+07722,Colts Neck
+07723,Deal
+07724,Eatontown
+07726,Englishtown
+07727,Farmingdale
+07728,Freehold
+07730,Hazlet
+07731,Howell
+07732,Highlands
+07733,Holmdel
+07734,Keansburg
+07735,Keyport
+07737,Leonardo
+07738,Lincroft
+07739,Little Silver
+07740,Long Branch
+07746,Marlboro
+07747,Matawan
+07748,Middletown
+07750,Monmouth Beach
+07751,Morganville
+07752,Navesink
+07753,Neptune
+07754,Neptune
+07755,Oakhurst
+07756,Ocean Grove
+07757,Oceanport
+07758,Port Monmouth
+07760,Rumson
+07762,Spring Lake
+07763,Tennent
+07764,West Long Branch
+07765,Wickatunk
+07777,Holmdel
+07799,Eatontown
+07801,Dover
+07802,Dover
+07803,Mine Hill
+07806,Picatinny Arsenal
+07820,Allamuchy
+07821,Andover
+07822,Augusta
+07823,Belvidere
+07825,Blairstown
+07826,Branchville
+07827,Montague
+07828,Budd Lake
+07829,Buttzville
+07830,Califon
+07831,Changewater
+07832,Columbia
+07833,Delaware
+07834,Denville
+07836,Flanders
+07837,Glasser
+07838,Great Meadows
+07839,Greendell
+07840,Hackettstown
+07842,Hibernia
+07843,Hopatcong
+07844,Hope
+07845,Ironia
+07846,Johnsonburg
+07847,Kenvil
+07848,Lafayette
+07849,Lake Hopatcong
+07850,Landing
+07851,Layton
+07852,Ledgewood
+07853,Long Valley
+07855,Middleville
+07856,Mount Arlington
+07857,Netcong
+07860,Newton
+07863,Oxford
+07865,Port Murray
+07866,Rockaway
+07869,Randolph
+07870,Schooleys Mountain
+07871,Sparta
+07874,Stanhope
+07875,Stillwater
+07876,Succasunna
+07877,Swartswood
+07878,Mount Tabor
+07879,Tranquility
+07880,Vienna
+07881,Wallpack Center
+07882,Washington
+07885,Wharton
+07890,Branchville
+07901,Summit
+07902,Summit
+07920,Basking Ridge
+07921,Bedminster
+07922,Berkeley Heights
+07924,Bernardsville
+07926,Brookside
+07927,Cedar Knolls
+07928,Chatham
+07930,Chester
+07931,Far Hills
+07932,Florham Park
+07933,Gillette
+07934,Gladstone
+07935,Green Village
+07936,East Hanover
+07938,Liberty Corner
+07939,Lyons
+07940,Madison
+07945,Mendham
+07946,Millington
+07950,Morris Plains
+07960,Morristown
+07961,Morristown
+07962,Morristown
+07963,Morristown
+07970,Mount Freedom
+07974,New Providence
+07976,New Vernon
+07977,Peapack
+07978,Pluckemin
+07979,Pottersville
+07980,Stirling
+07981,Whippany
+07983,Whippany
+07999,Whippany
+08001,Alloway
+08002,Cherry Hill
+08003,Cherry Hill
+08004,Atco
+08005,Barnegat
+08006,Barnegat Light
+08007,Barrington
+08008,Beach Haven
+08009,Berlin
+08010,Beverly
+08011,Birmingham
+08012,Blackwood
+08014,Bridgeport
+08015,Browns Mills
+08016,Burlington
+08018,Cedar Brook
+08019,Chatsworth
+08020,Clarksboro
+08021,Clementon
+08022,Columbus
+08023,Deepwater
+08025,Ewan
+08026,Gibbsboro
+08027,Gibbstown
+08028,Glassboro
+08029,Glendora
+08030,Gloucester City
+08031,Bellmawr
+08032,Grenloch
+08033,Haddonfield
+08034,Cherry Hill
+08035,Haddon Heights
+08036,Hainesport
+08037,Hammonton
+08038,Hancocks Bridge
+08039,Harrisonville
+08041,Jobstown
+08042,Juliustown
+08043,Voorhees
+08045,Lawnside
+08046,Willingboro
+08048,Lumberton
+08049,Magnolia
+08050,Manahawkin
+08051,Mantua
+08052,Maple Shade
+08053,Marlton
+08054,Mount Laurel
+08055,Medford
+08056,Mickleton
+08057,Moorestown
+08059,Mount Ephraim
+08060,Mount Holly
+08061,Mount Royal
+08062,Mullica Hill
+08063,National Park
+08064,New Lisbon
+08065,Palmyra
+08066,Paulsboro
+08067,Pedricktown
+08068,Pemberton
+08069,Penns Grove
+08070,Pennsville
+08071,Pitman
+08072,Quinton
+08073,Rancocas
+08074,Richwood
+08075,Riverside
+08076,Riverton
+08077,Riverton
+08078,Runnemede
+08079,Salem
+08080,Sewell
+08081,Sicklerville
+08083,Somerdale
+08084,Stratford
+08085,Swedesboro
+08086,Thorofare
+08087,Tuckerton
+08088,Vincentown
+08089,Waterford Works
+08090,Wenonah
+08091,West Berlin
+08092,West Creek
+08093,Westville
+08094,Williamstown
+08095,Winslow
+08096,Woodbury
+08097,Woodbury Heights
+08098,Woodstown
+08099,Bellmawr
+08101,Camden
+08102,Camden
+08103,Camden
+08104,Camden
+08105,Camden
+08106,Audubon
+08107,Oaklyn
+08108,Collingswood
+08109,Merchantville
+08110,Pennsauken
+08201,Absecon
+08202,Avalon
+08203,Brigantine
+08204,Cape May
+08210,Cape May Court House
+08212,Cape May Point
+08213,Cologne
+08214,Dennisville
+08215,Egg Harbor City
+08217,Elwood
+08218,Goshen
+08219,Green Creek
+08220,Leeds Point
+08221,Linwood
+08223,Marmora
+08224,New Gretna
+08225,Northfield
+08226,Ocean City
+08230,Ocean View
+08231,Oceanville
+08232,Pleasantville
+08234,Egg Harbor Township
+08240,Pomona
+08241,Port Republic
+08242,Rio Grande
+08243,Sea Isle City
+08244,Somers Point
+08245,South Dennis
+08246,South Seaville
+08247,Stone Harbor
+08248,Strathmere
+08250,Tuckahoe
+08251,Villas
+08252,Whitesboro
+08260,Wildwood
+08270,Woodbine
+08302,Bridgeton
+08310,Buena
+08311,Cedarville
+08312,Clayton
+08313,Deerfield Street
+08314,Delmont
+08315,Dividing Creek
+08316,Dorchester
+08317,Dorothy
+08318,Elmer
+08319,Estell Manor
+08320,Fairton
+08321,Fortescue
+08322,Franklinville
+08323,Greenwich
+08324,Heislerville
+08326,Landisville
+08327,Leesburg
+08328,Malaga
+08329,Mauricetown
+08330,Mays Landing
+08332,Millville
+08340,Milmay
+08341,Minotola
+08342,Mizpah
+08343,Monroeville
+08344,Newfield
+08345,Newport
+08346,Newtonville
+08347,Norma
+08348,Port Elizabeth
+08349,Port Norris
+08350,Richland
+08352,Rosenhayn
+08353,Shiloh
+08360,Vineland
+08361,Vineland
+08362,Vineland
+08370,Riverside
+08401,Atlantic City
+08402,Margate City
+08403,Longport
+08404,Atlantic City
+08405,Atlantic City
+08406,Ventnor City
+08501,Allentown
+08502,Belle Mead
+08504,Blawenburg
+08505,Bordentown
+08510,Clarksburg
+08511,Cookstown
+08512,Cranbury
+08514,Cream Ridge
+08515,Crosswicks
+08518,Florence
+08520,Hightstown
+08525,Hopewell
+08526,Imlaystown
+08527,Jackson
+08528,Kingston
+08530,Lambertville
+08533,New Egypt
+08534,Pennington
+08535,Perrineville
+08536,Plainsboro
+08540,Princeton
+08541,Princeton
+08542,Princeton
+08543,Princeton
+08544,Princeton
+08550,Princeton Junction
+08551,Ringoes
+08553,Rocky Hill
+08554,Roebling
+08555,Roosevelt
+08556,Rosemont
+08557,Sergeantsville
+08558,Skillman
+08559,Stockton
+08560,Titusville
+08561,Windsor
+08562,Wrightstown
+08570,Cranbury
+08601,Trenton
+08602,Trenton
+08603,Trenton
+08604,Trenton
+08605,Trenton
+08606,Trenton
+08607,Trenton
+08608,Trenton
+08609,Trenton
+08610,Trenton
+08611,Trenton
+08618,Trenton
+08619,Trenton
+08620,Trenton
+08625,Trenton
+08628,Trenton
+08629,Trenton
+08638,Trenton
+08640,Trenton
+08641,Trenton
+08645,Trenton
+08646,Trenton
+08647,Trenton
+08648,Trenton
+08650,Trenton
+08666,Trenton
+08677,Trenton
+08690,Trenton
+08691,Trenton
+08695,Trenton
+08701,Lakewood
+08720,Allenwood
+08721,Bayville
+08722,Beachwood
+08723,Brick
+08724,Brick
+08730,Brielle
+08731,Forked River
+08732,Island Heights
+08733,Lakehurst
+08734,Lanoka Harbor
+08735,Lavallette
+08736,Manasquan
+08738,Mantoloking
+08739,Normandy Beach
+08740,Ocean Gate
+08741,Pine Beach
+08742,Point Pleasant Beach
+08750,Sea Girt
+08751,Seaside Heights
+08752,Seaside Park
+08753,Toms River
+08754,Toms River
+08755,Toms River
+08756,Toms River
+08757,Toms River
+08758,Waretown
+08759,Whiting
+08801,Annandale
+08802,Asbury
+08803,Baptistown
+08804,Bloomsbury
+08805,Bound Brook
+08807,Bridgewater
+08808,Broadway
+08809,Clinton
+08810,Dayton
+08812,Dunellen
+08816,East Brunswick
+08817,Edison
+08818,Edison
+08820,Edison
+08821,Flagtown
+08822,Flemington
+08823,Franklin Park
+08824,Kendall Park
+08825,Frenchtown
+08826,Glen Gardner
+08827,Hampton
+08828,Helmetta
+08829,High Bridge
+08830,Iselin
+08831,Monroe Township
+08832,Keasbey
+08833,Lebanon
+08834,Little York
+08835,Manville
+08836,Martinsville
+08837,Edison
+08840,Metuchen
+08846,Middlesex
+08848,Milford
+08850,Milltown
+08852,Monmouth Junction
+08853,Neshanic Station
+08854,Piscataway
+08855,Piscataway
+08857,Old Bridge
+08858,Oldwick
+08859,Parlin
+08861,Perth Amboy
+08862,Perth Amboy
+08863,Fords
+08865,Phillipsburg
+08867,Pittstown
+08868,Quakertown
+08869,Raritan
+08870,Readington
+08871,Sayreville
+08872,Sayreville
+08873,Somerset
+08875,Somerset
+08876,Somerville
+08877,South River
+08878,South Amboy
+08879,South Amboy
+08880,South Bound Brook
+08882,South River
+08884,Spotswood
+08885,Stanton
+08886,Stewartsville
+08887,Three Bridges
+08888,Whitehouse
+08889,Whitehouse Station
+08890,Zarephath
+08896,Raritan
+08899,Edison
+08901,New Brunswick
+08902,North Brunswick
+08903,New Brunswick
+08904,Highland Park
+08905,New Brunswick
+08906,New Brunswick
+08922,New Brunswick
+08933,New Brunswick
+08988,New Brunswick
+08989,New Brunswick
+00210,Portsmouth
+00211,Portsmouth
+00212,Portsmouth
+00213,Portsmouth
+00214,Portsmouth
+00215,Portsmouth
+03031,Amherst
+03032,Auburn
+03033,Brookline
+03034,Candia
+03036,Chester
+03037,Deerfield
+03038,Derry
+03040,East Candia
+03041,East Derry
+03042,Epping
+03043,Francestown
+03044,Fremont
+03045,Goffstown
+03047,Greenfield
+03048,Greenville
+03049,Hollis
+03051,Hudson
+03052,Litchfield
+03053,Londonderry
+03054,Merrimack
+03055,Milford
+03057,Mont Vernon
+03060,Nashua
+03061,Nashua
+03062,Nashua
+03063,Nashua
+03064,Nashua
+03070,New Boston
+03071,New Ipswich
+03073,North Salem
+03076,Pelham
+03077,Raymond
+03079,Salem
+03082,Lyndeborough
+03084,Temple
+03086,Wilton
+03087,Windham
+03101,Manchester
+03102,Manchester
+03103,Manchester
+03104,Manchester
+03105,Manchester
+03106,Hooksett
+03107,Manchester
+03108,Manchester
+03109,Manchester
+03110,Bedford
+03111,Manchester
+03215,Waterville Valley
+03216,Andover
+03217,Ashland
+03218,Barnstead
+03220,Belmont
+03221,Bradford
+03222,Bristol
+03223,Campton
+03224,Canterbury
+03225,Center Barnstead
+03226,Center Harbor
+03227,Center Sandwich
+03229,Contoocook
+03230,Danbury
+03231,East Andover
+03232,East Hebron
+03233,Elkins
+03234,Epsom
+03235,Franklin
+03237,Gilmanton
+03238,Glencliff
+03240,Grafton
+03241,Hebron
+03242,Henniker
+03243,Hill
+03244,Hillsboro
+03245,Holderness
+03246,Laconia
+03247,Laconia
+03251,Lincoln
+03252,Lochmere
+03253,Meredith
+03254,Moultonborough
+03255,Newbury
+03256,New Hampton
+03257,New London
+03259,North Sandwich
+03260,North Sutton
+03261,Northwood
+03262,North Woodstock
+03263,Pittsfield
+03264,Plymouth
+03266,Rumney
+03268,Salisbury
+03269,Sanbornton
+03272,South Newbury
+03273,South Sutton
+03274,Stinson Lake
+03275,Suncook
+03276,Tilton
+03278,Warner
+03279,Warren
+03280,Washington
+03281,Weare
+03282,Wentworth
+03284,Springfield
+03287,Wilmot
+03289,Winnisquam
+03290,Nottingham
+03291,West Nottingham
+03293,Woodstock
+03298,Tilton
+03299,Tilton
+03301,Concord
+03302,Concord
+03303,Concord
+03304,Bow
+03305,Concord
+03307,Loudon
+03431,Keene
+03435,Keene
+03440,Antrim
+03441,Ashuelot
+03442,Bennington
+03443,Chesterfield
+03444,Dublin
+03445,Sullivan
+03446,Swanzey
+03447,Fitzwilliam
+03448,Gilsum
+03449,Hancock
+03450,Harrisville
+03451,Hinsdale
+03452,Jaffrey
+03455,Marlborough
+03456,Marlow
+03457,Munsonville
+03458,Peterborough
+03461,Rindge
+03462,Spofford
+03464,Stoddard
+03465,Troy
+03466,West Chesterfield
+03467,Westmoreland
+03468,West Peterborough
+03469,West Swanzey
+03470,Winchester
+03561,Littleton
+03570,Berlin
+03574,Bethlehem
+03575,Bretton Woods
+03576,Colebrook
+03579,Errol
+03580,Franconia
+03581,Gorham
+03582,Groveton
+03583,Jefferson
+03584,Lancaster
+03585,Lisbon
+03587,Meadows
+03588,Milan
+03589,Mount Washington
+03590,North Stratford
+03592,Pittsburg
+03595,Twin Mountain
+03597,West Stewartstown
+03598,Whitefield
+03601,Acworth
+03602,Alstead
+03603,Charlestown
+03604,Drewsville
+03605,Lempster
+03607,South Acworth
+03608,Walpole
+03609,North Walpole
+03740,Bath
+03741,Canaan
+03743,Claremont
+03745,Cornish
+03746,Cornish Flat
+03748,Enfield
+03749,Enfield Center
+03750,Etna
+03751,Georges Mills
+03752,Goshen
+03753,Grantham
+03754,Guild
+03755,Hanover
+03756,Lebanon
+03765,Haverhill
+03766,Lebanon
+03768,Lyme
+03769,Lyme Center
+03770,Meriden
+03771,Monroe
+03773,Newport
+03774,North Haverhill
+03777,Orford
+03779,Piermont
+03780,Pike
+03781,Plainfield
+03782,Sunapee
+03784,West Lebanon
+03785,Woodsville
+03801,Portsmouth
+03802,Portsmouth
+03803,Portsmouth
+03804,Portsmouth
+03805,Rollinsford
+03809,Alton
+03810,Alton Bay
+03811,Atkinson
+03812,Bartlett
+03813,Center Conway
+03814,Center Ossipee
+03815,Center Strafford
+03816,Center Tuftonboro
+03817,Chocorua
+03818,Conway
+03819,Danville
+03820,Dover
+03821,Dover
+03822,Dover
+03824,Durham
+03825,Barrington
+03826,East Hampstead
+03827,East Kingston
+03830,East Wakefield
+03832,Eaton Center
+03833,Exeter
+03835,Farmington
+03836,Freedom
+03837,Gilmanton Iron Works
+03838,Glen
+03839,Rochester
+03840,Greenland
+03841,Hampstead
+03842,Hampton
+03843,Hampton
+03844,Hampton Falls
+03845,Intervale
+03846,Jackson
+03847,Kearsarge
+03848,Kingston
+03849,Madison
+03850,Melvin Village
+03851,Milton
+03852,Milton Mills
+03853,Mirror Lake
+03854,New Castle
+03855,New Durham
+03856,Newfields
+03857,Newmarket
+03858,Newton
+03859,Newton Junction
+03860,North Conway
+03862,North Hampton
+03864,Ossipee
+03865,Plaistow
+03866,Rochester
+03867,Rochester
+03868,Rochester
+03869,Rollinsford
+03870,Rye
+03871,Rye Beach
+03872,Sanbornville
+03873,Sandown
+03874,Seabrook
+03875,Silver Lake
+03878,Somersworth
+03882,South Effingham
+03883,South Tamworth
+03884,Strafford
+03885,Stratham
+03886,Tamworth
+03887,Union
+03890,West Ossipee
+03894,Wolfeboro
+03896,Wolfeboro Falls
+03897,Wonalancet
+68001,Abie
+68002,Arlington
+68003,Ashland
+68004,Bancroft
+68005,Bellevue
+68007,Bennington
+68008,Blair
+68009,Blair
+68010,Boys Town
+68014,Bruno
+68015,Cedar Bluffs
+68016,Cedar Creek
+68017,Ceresco
+68018,Colon
+68019,Craig
+68020,Decatur
+68022,Elkhorn
+68023,Fort Calhoun
+68025,Fremont
+68026,Fremont
+68028,Gretna
+68029,Herman
+68030,Homer
+68031,Hooper
+68033,Ithaca
+68034,Kennard
+68035,Leshara
+68036,Linwood
+68037,Louisville
+68038,Lyons
+68039,Macy
+68040,Malmo
+68041,Mead
+68042,Memphis
+68044,Nickerson
+68045,Oakland
+68046,Papillion
+68047,Pender
+68048,Plattsmouth
+68050,Prague
+68054,Richfield
+68055,Rosalie
+68056,St Columbans
+68057,Scribner
+68058,South Bend
+68059,Springfield
+68061,Tekamah
+68062,Thurston
+68063,Uehling
+68064,Valley
+68065,Valparaiso
+68066,Wahoo
+68067,Walthill
+68068,Washington
+68069,Waterloo
+68070,Weston
+68071,Winnebago
+68072,Winslow
+68073,Yutan
+68101,Omaha
+68102,Omaha
+68103,Omaha
+68104,Omaha
+68105,Omaha
+68106,Omaha
+68107,Omaha
+68108,Omaha
+68109,Omaha
+68110,Omaha
+68111,Omaha
+68112,Omaha
+68113,Offutt A F B
+68114,Omaha
+68116,Omaha
+68117,Omaha
+68118,Omaha
+68119,Omaha
+68120,Omaha
+68122,Omaha
+68123,Bellevue
+68124,Omaha
+68127,Omaha
+68128,La Vista
+68130,Omaha
+68131,Omaha
+68132,Omaha
+68133,Papillion
+68134,Omaha
+68135,Omaha
+68136,Omaha
+68137,Omaha
+68138,Omaha
+68139,Omaha
+68142,Omaha
+68144,Omaha
+68145,Omaha
+68147,Bellevue
+68152,Omaha
+68154,Omaha
+68155,Omaha
+68157,Omaha
+68164,Omaha
+68172,Omaha
+68175,Omaha
+68176,Omaha
+68178,Omaha
+68179,Omaha
+68180,Omaha
+68181,Omaha
+68182,Omaha
+68183,Omaha
+68198,Omaha
+68301,Adams
+68303,Alexandria
+68304,Alvo
+68305,Auburn
+68307,Avoca
+68309,Barneston
+68310,Beatrice
+68313,Beaver Crossing
+68314,Bee
+68315,Belvidere
+68316,Benedict
+68317,Bennet
+68318,Blue Springs
+68319,Bradshaw
+68320,Brock
+68321,Brownville
+68322,Bruning
+68323,Burchard
+68324,Burr
+68325,Byron
+68326,Carleton
+68327,Chester
+68328,Clatonia
+68329,Cook
+68330,Cordova
+68331,Cortland
+68332,Crab Orchard
+68333,Crete
+68335,Davenport
+68336,Davey
+68337,Dawson
+68338,Daykin
+68339,Denton
+68340,Deshler
+68341,De Witt
+68342,Diller
+68343,Dorchester
+68344,Douglas
+68345,Du Bois
+68346,Dunbar
+68347,Eagle
+68348,Elk Creek
+68349,Elmwood
+68350,Endicott
+68351,Exeter
+68352,Fairbury
+68354,Fairmont
+68355,Falls City
+68357,Filley
+68358,Firth
+68359,Friend
+68360,Garland
+68361,Geneva
+68362,Gilead
+68364,Goehner
+68365,Grafton
+68366,Greenwood
+68367,Gresham
+68368,Hallam
+68370,Hebron
+68371,Henderson
+68372,Hickman
+68374,Holmesville
+68375,Hubbell
+68376,Humboldt
+68377,Jansen
+68378,Johnson
+68380,Lewiston
+68381,Liberty
+68382,Lorton
+68401,Mc Cool Junction
+68402,Malcolm
+68403,Manley
+68404,Martell
+68405,Milford
+68406,Milligan
+68407,Murdock
+68409,Murray
+68410,Nebraska City
+68413,Nehawka
+68414,Nemaha
+68415,Odell
+68416,Ohiowa
+68417,Otoe
+68418,Palmyra
+68419,Panama
+68420,Pawnee City
+68421,Peru
+68422,Pickrell
+68423,Pleasant Dale
+68424,Plymouth
+68428,Raymond
+68429,Reynolds
+68430,Roca
+68431,Rulo
+68433,Salem
+68434,Seward
+68436,Shickley
+68437,Shubert
+68438,Sprague
+68439,Staplehurst
+68440,Steele City
+68441,Steinauer
+68442,Stella
+68443,Sterling
+68444,Strang
+68445,Swanton
+68446,Syracuse
+68447,Table Rock
+68448,Talmage
+68450,Tecumseh
+68452,Ong
+68453,Tobias
+68454,Unadilla
+68455,Union
+68456,Utica
+68457,Verdon
+68458,Virginia
+68460,Waco
+68461,Walton
+68462,Waverly
+68463,Weeping Water
+68464,Western
+68465,Wilber
+68466,Wymore
+68467,York
+68501,Lincoln
+68502,Lincoln
+68503,Lincoln
+68504,Lincoln
+68505,Lincoln
+68506,Lincoln
+68507,Lincoln
+68508,Lincoln
+68509,Lincoln
+68510,Lincoln
+68512,Lincoln
+68514,Lincoln
+68516,Lincoln
+68517,Lincoln
+68520,Lincoln
+68521,Lincoln
+68522,Lincoln
+68523,Lincoln
+68524,Lincoln
+68526,Lincoln
+68527,Lincoln
+68528,Lincoln
+68529,Lincoln
+68531,Lincoln
+68532,Lincoln
+68542,Lincoln
+68544,Lincoln
+68572,Lincoln
+68583,Lincoln
+68588,Lincoln
+68601,Columbus
+68602,Columbus
+68620,Albion
+68621,Ames
+68622,Bartlett
+68623,Belgrade
+68624,Bellwood
+68626,Brainard
+68627,Cedar Rapids
+68628,Clarks
+68629,Clarkson
+68631,Creston
+68632,David City
+68633,Dodge
+68634,Duncan
+68635,Dwight
+68636,Elgin
+68637,Ericson
+68638,Fullerton
+68640,Genoa
+68641,Howells
+68642,Humphrey
+68643,Leigh
+68644,Lindsay
+68647,Monroe
+68648,Morse Bluff
+68649,North Bend
+68651,Osceola
+68652,Petersburg
+68653,Platte Center
+68654,Polk
+68655,Primrose
+68658,Rising City
+68659,Rogers
+68660,Saint Edward
+68661,Schuyler
+68662,Shelby
+68663,Silver Creek
+68664,Snyder
+68665,Spalding
+68666,Stromsburg
+68667,Surprise
+68669,Ulysses
+68701,Norfolk
+68702,Norfolk
+68710,Allen
+68711,Amelia
+68713,Atkinson
+68714,Bassett
+68715,Battle Creek
+68716,Beemer
+68717,Belden
+68718,Bloomfield
+68719,Bristow
+68720,Brunswick
+68722,Butte
+68723,Carroll
+68724,Center
+68725,Chambers
+68726,Clearwater
+68727,Coleridge
+68728,Concord
+68729,Creighton
+68730,Crofton
+68731,Dakota City
+68732,Dixon
+68733,Emerson
+68734,Emmet
+68735,Ewing
+68736,Fordyce
+68737,Foster
+68738,Hadar
+68739,Hartington
+68740,Hoskins
+68741,Hubbard
+68742,Inman
+68743,Jackson
+68745,Laurel
+68746,Lynch
+68747,McLean
+68748,Madison
+68749,Magnet
+68751,Maskell
+68752,Meadow Grove
+68753,Mills
+68755,Naper
+68756,Neligh
+68757,Newcastle
+68758,Newman Grove
+68759,Newport
+68760,Niobrara
+68761,Oakdale
+68763,Oneill
+68764,Orchard
+68765,Osmond
+68766,Page
+68767,Pierce
+68768,Pilger
+68769,Plainview
+68770,Ponca
+68771,Randolph
+68772,Rose
+68773,Royal
+68774,Saint Helena
+68776,South Sioux City
+68777,Spencer
+68778,Springview
+68779,Stanton
+68780,Stuart
+68781,Tilden
+68783,Verdigre
+68784,Wakefield
+68785,Waterbury
+68786,Wausa
+68787,Wayne
+68788,West Point
+68789,Winnetoon
+68790,Winside
+68791,Wisner
+68792,Wynot
+68801,Grand Island
+68802,Grand Island
+68803,Grand Island
+68810,Alda
+68812,Amherst
+68813,Anselmo
+68814,Ansley
+68815,Arcadia
+68816,Archer
+68817,Ashton
+68818,Aurora
+68819,Berwyn
+68820,Boelus
+68821,Brewster
+68822,Broken Bow
+68823,Burwell
+68824,Cairo
+68825,Callaway
+68826,Central City
+68827,Chapman
+68828,Comstock
+68831,Dannebrog
+68832,Doniphan
+68833,Dunning
+68834,Eddyville
+68835,Elba
+68836,Elm Creek
+68837,Elyria
+68838,Farwell
+68840,Gibbon
+68841,Giltner
+68842,Greeley
+68843,Hampton
+68844,Hazard
+68845,Kearney
+68846,Hordville
+68847,Kearney
+68848,Kearney
+68849,Kearney
+68850,Lexington
+68852,Litchfield
+68853,Loup City
+68854,Marquette
+68855,Mason City
+68856,Merna
+68858,Miller
+68859,North Loup
+68860,Oconto
+68861,Odessa
+68862,Ord
+68863,Overton
+68864,Palmer
+68865,Phillips
+68866,Pleasanton
+68869,Ravenna
+68870,Riverdale
+68871,Rockville
+68872,Saint Libory
+68873,Saint Paul
+68874,Sargent
+68875,Scotia
+68876,Shelton
+68878,Sumner
+68879,Taylor
+68880,Weissert
+68881,Westerville
+68882,Wolbach
+68883,Wood River
+68901,Hastings
+68902,Hastings
+68920,Alma
+68922,Arapahoe
+68923,Atlanta
+68924,Axtell
+68925,Ayr
+68926,Beaver City
+68927,Bertrand
+68928,Bladen
+68929,Bloomington
+68930,Blue Hill
+68932,Campbell
+68933,Clay Center
+68934,Deweese
+68935,Edgar
+68936,Edison
+68937,Elwood
+68938,Fairfield
+68939,Franklin
+68940,Funk
+68941,Glenvil
+68942,Guide Rock
+68943,Hardy
+68944,Harvard
+68945,Heartwell
+68946,Hendley
+68947,Hildreth
+68948,Holbrook
+68949,Holdrege
+68950,Holstein
+68952,Inavale
+68954,Inland
+68955,Juniata
+68956,Kenesaw
+68957,Lawrence
+68958,Loomis
+68959,Minden
+68960,Naponee
+68961,Nelson
+68963,Norman
+68964,Oak
+68966,Orleans
+68967,Oxford
+68969,Ragan
+68970,Red Cloud
+68971,Republican City
+68972,Riverton
+68973,Roseland
+68974,Ruskin
+68975,Saronville
+68976,Smithfield
+68977,Stamford
+68978,Superior
+68979,Sutton
+68980,Trumbull
+68981,Upland
+68982,Wilcox
+69001,Mc Cook
+69020,Bartley
+69021,Benkelman
+69022,Cambridge
+69023,Champion
+69024,Culbertson
+69025,Curtis
+69026,Danbury
+69027,Enders
+69028,Eustis
+69029,Farnam
+69030,Haigler
+69031,Hamlet
+69032,Hayes Center
+69033,Imperial
+69034,Indianola
+69036,Lebanon
+69037,Max
+69038,Maywood
+69039,Moorefield
+69040,Palisade
+69041,Parks
+69042,Stockville
+69043,Stratton
+69044,Trenton
+69045,Wauneta
+69046,Wilsonville
+69101,North Platte
+69103,North Platte
+69120,Arnold
+69121,Arthur
+69122,Big Springs
+69123,Brady
+69125,Broadwater
+69127,Brule
+69128,Bushnell
+69129,Chappell
+69130,Cozad
+69131,Dalton
+69132,Dickens
+69133,Dix
+69134,Elsie
+69135,Elsmere
+69138,Gothenburg
+69140,Grant
+69141,Gurley
+69142,Halsey
+69143,Hershey
+69144,Keystone
+69145,Kimball
+69146,Lemoyne
+69147,Lewellen
+69148,Lisco
+69149,Lodgepole
+69150,Madrid
+69151,Maxwell
+69152,Mullen
+69153,Ogallala
+69154,Oshkosh
+69155,Paxton
+69156,Potter
+69157,Purdum
+69160,Sidney
+69161,Seneca
+69162,Sidney
+69163,Stapleton
+69165,Sutherland
+69166,Thedford
+69167,Tryon
+69168,Venango
+69169,Wallace
+69170,Wellfleet
+69171,Willow Island
+69190,Oshkosh
+69201,Valentine
+69210,Ainsworth
+69211,Cody
+69212,Crookston
+69214,Johnstown
+69216,Kilgore
+69217,Long Pine
+69218,Merriman
+69219,Nenzel
+69220,Sparks
+69221,Wood Lake
+69301,Alliance
+69331,Angora
+69333,Ashby
+69334,Bayard
+69335,Bingham
+69336,Bridgeport
+69337,Chadron
+69339,Crawford
+69340,Ellsworth
+69341,Gering
+69343,Gordon
+69345,Harrisburg
+69346,Harrison
+69347,Hay Springs
+69348,Hemingford
+69349,Henry
+69350,Hyannis
+69351,Lakeside
+69352,Lyman
+69353,McGrew
+69354,Marsland
+69355,Melbeta
+69356,Minatare
+69357,Mitchell
+69358,Morrill
+69360,Rushville
+69361,Scottsbluff
+69363,Scottsbluff
+69365,Whiteclay
+69366,Whitman
+69367,Whitney
+58001,Abercrombie
+58002,Absaraka
+58004,Amenia
+58005,Argusville
+58006,Arthur
+58007,Ayr
+58008,Barney
+58009,Blanchard
+58011,Buffalo
+58012,Casselton
+58013,Cayuga
+58014,Chaffee
+58015,Christine
+58016,Clifford
+58017,Cogswell
+58018,Colfax
+58021,Davenport
+58027,Enderlin
+58029,Erie
+58030,Fairmount
+58031,Fingal
+58032,Forman
+58033,Fort Ransom
+58035,Galesburg
+58036,Gardner
+58038,Grandin
+58039,Great Bend
+58040,Gwinner
+58041,Hankinson
+58042,Harwood
+58043,Havana
+58045,Hillsboro
+58046,Hope
+58047,Horace
+58048,Hunter
+58049,Kathryn
+58051,Kindred
+58052,Leonard
+58053,Lidgerwood
+58054,Lisbon
+58056,Luverne
+58057,McLeod
+58058,Mantador
+58059,Mapleton
+58060,Milnor
+58061,Mooreton
+58062,Nome
+58063,Oriska
+58064,Page
+58065,Pillsbury
+58067,Rutland
+58068,Sheldon
+58069,Stirum
+58071,Tower City
+58072,Valley City
+58074,Wahpeton
+58075,Wahpeton
+58076,Wahpeton
+58077,Walcott
+58078,West Fargo
+58079,Wheatland
+58081,Wyndmere
+58102,Fargo
+58103,Fargo
+58104,Fargo
+58105,Fargo
+58106,Fargo
+58107,Fargo
+58108,Fargo
+58109,Fargo
+58121,Fargo
+58122,Fargo
+58123,Fargo
+58124,Fargo
+58125,Fargo
+58126,Fargo
+58201,Grand Forks
+58202,Grand Forks
+58203,Grand Forks
+58204,Grand Forks AFB
+58205,Grand Forks AFB
+58206,Grand Forks
+58207,Grand Forks
+58208,Grand Forks
+58210,Adams
+58212,Aneta
+58213,Ardoch
+58214,Arvilla
+58216,Bathgate
+58218,Buxton
+58219,Caledonia
+58220,Cavalier
+58222,Crystal
+58223,Cummings
+58224,Dahlen
+58225,Drayton
+58227,Edinburg
+58228,Emerado
+58229,Fairdale
+58230,Finley
+58231,Fordville
+58233,Forest River
+58235,Gilby
+58236,Glasston
+58237,Grafton
+58238,Hamilton
+58239,Hannah
+58240,Hatton
+58241,Hensel
+58243,Hoople
+58244,Inkster
+58249,Langdon
+58250,Lankin
+58251,Larimore
+58254,McVille
+58255,Maida
+58256,Manvel
+58257,Mayville
+58258,Mekinock
+58259,Michigan
+58260,Milton
+58261,Minto
+58262,Mountain
+58265,Neche
+58266,Niagara
+58267,Northwood
+58269,Osnabrock
+58270,Park River
+58271,Pembina
+58272,Petersburg
+58273,Pisek
+58274,Portland
+58275,Reynolds
+58276,Saint Thomas
+58277,Sharon
+58278,Thompson
+58281,Wales
+58282,Walhalla
+58301,Devils Lake
+58310,Agate
+58311,Alsen
+58313,Balta
+58316,Belcourt
+58317,Bisbee
+58318,Bottineau
+58319,Bremen
+58320,Brinsmade
+58321,Brocket
+58323,Calvin
+58324,Cando
+58325,Churchs Ferry
+58327,Crary
+58329,Dunseith
+58330,Edmore
+58331,Egeland
+58332,Esmond
+58335,Fort Totten
+58337,Hamberg
+58338,Hampden
+58339,Hansboro
+58341,Harvey
+58343,Knox
+58344,Lakota
+58345,Lawton
+58346,Leeds
+58348,Maddock
+58351,Minnewaukan
+58352,Munich
+58353,Mylo
+58355,Nekoma
+58356,New Rockford
+58357,Oberon
+58359,Orrin
+58361,Pekin
+58362,Penn
+58363,Perth
+58365,Rocklake
+58366,Rolette
+58367,Rolla
+58368,Rugby
+58369,Saint John
+58370,Saint Michael
+58372,Sarles
+58374,Sheyenne
+58377,Starkweather
+58379,Tokio
+58380,Tolna
+58381,Warwick
+58382,Webster
+58384,Willow City
+58385,Wolford
+58386,York
+58401,Jamestown
+58402,Jamestown
+58405,Jamestown
+58413,Ashley
+58415,Berlin
+58416,Binford
+58418,Bowdon
+58420,Buchanan
+58421,Carrington
+58422,Cathay
+58423,Chaseley
+58424,Cleveland
+58425,Cooperstown
+58426,Courtenay
+58428,Dawson
+58429,Dazey
+58430,Denhoff
+58431,Dickey
+58432,Eckelson
+58433,Edgeley
+58436,Ellendale
+58438,Fessenden
+58439,Forbes
+58440,Fredonia
+58441,Fullerton
+58442,Gackle
+58443,Glenfield
+58444,Goodrich
+58445,Grace City
+58448,Hannaford
+58451,Hurdsfield
+58452,Jessie
+58454,Jud
+58455,Kensal
+58456,Kulm
+58458,Lamoure
+58460,Lehr
+58461,Litchville
+58463,McClusky
+58464,McHenry
+58466,Marion
+58467,Medina
+58472,Montpelier
+58474,Oakes
+58475,Pettibone
+58476,Pingree
+58477,Regan
+58478,Robinson
+58479,Rogers
+58480,Sanborn
+58481,Spiritwood
+58482,Steele
+58483,Streeter
+58484,Sutton
+58486,Sykeston
+58487,Tappen
+58488,Tuttle
+58489,Venturia
+58490,Verona
+58492,Wimbledon
+58494,Wing
+58495,Wishek
+58496,Woodworth
+58497,Ypsilanti
+58501,Bismarck
+58502,Bismarck
+58504,Bismarck
+58505,Bismarck
+58506,Bismarck
+58507,Bismarck
+58520,Almont
+58521,Baldwin
+58523,Beulah
+58524,Braddock
+58528,Cannon Ball
+58529,Carson
+58530,Center
+58531,Coleharbor
+58532,Driscoll
+58533,Elgin
+58535,Flasher
+58538,Fort Yates
+58540,Garrison
+58541,Golden Valley
+58542,Hague
+58544,Hazelton
+58545,Hazen
+58549,Kintyre
+58552,Linton
+58553,McKenzie
+58554,Mandan
+58558,Menoken
+58559,Mercer
+58560,Moffit
+58561,Napoleon
+58562,New Leipzig
+58563,New Salem
+58564,Raleigh
+58565,Riverdale
+58566,Saint Anthony
+58568,Selfridge
+58569,Shields
+58570,Solen
+58571,Stanton
+58572,Sterling
+58573,Strasburg
+58575,Turtle Lake
+58576,Underwood
+58577,Washburn
+58579,Wilton
+58580,Zap
+58581,Zeeland
+58601,Dickinson
+58602,Dickinson
+58620,Amidon
+58621,Beach
+58622,Belfield
+58623,Bowman
+58625,Dodge
+58626,Dunn Center
+58627,Fairfield
+58630,Gladstone
+58631,Glen Ullin
+58632,Golva
+58634,Grassy Butte
+58636,Halliday
+58638,Hebron
+58639,Hettinger
+58640,Killdeer
+58641,Lefor
+58642,Manning
+58643,Marmarth
+58644,Marshall
+58645,Medora
+58646,Mott
+58647,New England
+58649,Reeder
+58650,Regent
+58651,Rhame
+58652,Richardton
+58653,Scranton
+58654,Sentinel Butte
+58655,South Heart
+58656,Taylor
+58701,Minot
+58702,Minot
+58703,Minot
+58704,Minot AFB
+58705,Minot AFB
+58707,Minot
+58710,Anamoose
+58711,Antler
+58712,Balfour
+58713,Bantry
+58716,Benedict
+58718,Berthold
+58721,Bowbells
+58722,Burlington
+58723,Butte
+58725,Carpio
+58727,Columbus
+58730,Crosby
+58731,Deering
+58733,Des Lacs
+58734,Donnybrook
+58735,Douglas
+58736,Drake
+58737,Flaxton
+58740,Glenburn
+58741,Granville
+58744,Karlsruhe
+58746,Kenmare
+58747,Kief
+58748,Kramer
+58750,Lansford
+58752,Lignite
+58755,McGregor
+58756,Makoti
+58757,Mandaree
+58758,Martin
+58759,Max
+58760,Maxbass
+58761,Mohall
+58762,Newburg
+58763,New Town
+58765,Noonan
+58768,Norwich
+58769,Palermo
+58770,Parshall
+58771,Plaza
+58772,Portal
+58773,Powers Lake
+58775,Roseglen
+58776,Ross
+58778,Ruso
+58779,Ryder
+58781,Sawyer
+58782,Sherwood
+58783,Souris
+58784,Stanley
+58785,Surrey
+58787,Tolley
+58788,Towner
+58789,Upham
+58790,Velva
+58792,Voltaire
+58793,Westhope
+58794,White Earth
+58795,Wildrose
+58801,Williston
+58802,Williston
+58830,Alamo
+58831,Alexander
+58833,Ambrose
+58835,Arnegard
+58838,Cartwright
+58843,Epping
+58844,Fortuna
+58845,Grenora
+58847,Keene
+58849,Ray
+58852,Tioga
+58853,Trenton
+58854,Watford City
+58856,Zahl
+27006,Advance
+27007,Ararat
+27009,Belews Creek
+27010,Bethania
+27011,Boonville
+27012,Clemmons
+27013,Cleveland
+27014,Cooleemee
+27016,Danbury
+27017,Dobson
+27018,East Bend
+27019,Germanton
+27020,Hamptonville
+27021,King
+27022,Lawsonville
+27023,Lewisville
+27024,Lowgap
+27025,Madison
+27027,Mayodan
+27028,Mocksville
+27030,Mount Airy
+27031,White Plains
+27040,Pfafftown
+27041,Pilot Mountain
+27042,Pine Hall
+27043,Pinnacle
+27045,Rural Hall
+27046,Sandy Ridge
+27047,Siloam
+27048,Stoneville
+27049,Toast
+27050,Tobaccoville
+27051,Walkertown
+27052,Walnut Cove
+27053,Westfield
+27054,Woodleaf
+27055,Yadkinville
+27094,Rural Hall
+27098,Rural Hall
+27099,Rural Hall
+27101,Winston Salem
+27102,Winston Salem
+27103,Winston Salem
+27104,Winston Salem
+27105,Winston Salem
+27106,Winston Salem
+27107,Winston Salem
+27108,Winston Salem
+27109,Winston Salem
+27110,Winston Salem
+27111,Winston Salem
+27113,Winston Salem
+27114,Winston Salem
+27115,Winston Salem
+27116,Winston Salem
+27117,Winston Salem
+27120,Winston Salem
+27127,Winston Salem
+27130,Winston Salem
+27150,Winston Salem
+27151,Winston Salem
+27152,Winston Salem
+27155,Winston Salem
+27156,Winston Salem
+27157,Winston Salem
+27198,Winston Salem
+27199,Winston Salem
+27201,Alamance
+27202,Altamahaw
+27203,Asheboro
+27204,Asheboro
+27207,Bear Creek
+27208,Bennett
+27209,Biscoe
+27212,Blanch
+27213,Bonlee
+27214,Browns Summit
+27215,Burlington
+27216,Burlington
+27217,Burlington
+27220,Burlington
+27228,Bynum
+27229,Candor
+27230,Cedar Falls
+27231,Cedar Grove
+27233,Climax
+27235,Colfax
+27237,Cumnock
+27239,Denton
+27242,Eagle Springs
+27243,Efland
+27244,Elon College
+27247,Ether
+27248,Franklinville
+27249,Gibsonville
+27252,Goldston
+27253,Graham
+27256,Gulf
+27258,Haw River
+27259,Highfalls
+27260,High Point
+27261,High Point
+27262,High Point
+27263,High Point
+27264,High Point
+27265,High Point
+27278,Hillsborough
+27281,Jackson Springs
+27282,Jamestown
+27283,Julian
+27284,Kernersville
+27285,Kernersville
+27288,Eden
+27289,Eden
+27291,Leasburg
+27292,Lexington
+27293,Lexington
+27294,Lexington
+27295,Lexington
+27298,Liberty
+27299,Linwood
+27301,Mc Leansville
+27302,Mebane
+27305,Milton
+27306,Mount Gilead
+27310,Oak Ridge
+27311,Pelham
+27312,Pittsboro
+27313,Pleasant Garden
+27314,Prospect Hill
+27315,Providence
+27316,Ramseur
+27317,Randleman
+27320,Reidsville
+27321,Reidsville
+27322,Reidsville
+27323,Reidsville
+27325,Robbins
+27326,Ruffin
+27330,Sanford
+27331,Sanford
+27340,Saxapahaw
+27341,Seagrove
+27342,Sedalia
+27343,Semora
+27344,Siler City
+27349,Snow Camp
+27350,Sophia
+27351,Southmont
+27355,Staley
+27356,Star
+27357,Stokesdale
+27358,Summerfield
+27359,Swepsonville
+27360,Thomasville
+27361,Thomasville
+27370,Trinity
+27371,Troy
+27373,Wallburg
+27374,Welcome
+27375,Wentworth
+27376,West End
+27377,Whitsett
+27379,Yanceyville
+27401,Greensboro
+27402,Greensboro
+27403,Greensboro
+27404,Greensboro
+27405,Greensboro
+27406,Greensboro
+27407,Greensboro
+27408,Greensboro
+27409,Greensboro
+27410,Greensboro
+27411,Greensboro
+27412,Greensboro
+27413,Greensboro
+27415,Greensboro
+27416,Greensboro
+27417,Greensboro
+27419,Greensboro
+27420,Greensboro
+27425,Greensboro
+27427,Greensboro
+27429,Greensboro
+27435,Greensboro
+27438,Greensboro
+27455,Greensboro
+27480,Greensboro
+27495,Greensboro
+27498,Greensboro
+27499,Greensboro
+27501,Angier
+27502,Apex
+27503,Bahama
+27504,Benson
+27505,Broadway
+27506,Buies Creek
+27507,Bullock
+27508,Bunn
+27509,Butner
+27510,Carrboro
+27511,Cary
+27512,Cary
+27513,Cary
+27514,Chapel Hill
+27515,Chapel Hill
+27516,Chapel Hill
+27518,Cary
+27519,Cary
+27520,Clayton
+27521,Coats
+27522,Creedmoor
+27523,Eagle Rock
+27524,Four Oaks
+27525,Franklinton
+27526,Fuquay Varina
+27529,Garner
+27530,Goldsboro
+27531,Goldsboro
+27532,Goldsboro
+27533,Goldsboro
+27534,Goldsboro
+27536,Henderson
+27540,Holly Springs
+27541,Hurdle Mills
+27542,Kenly
+27543,Kipling
+27544,Kittrell
+27545,Knightdale
+27546,Lillington
+27549,Louisburg
+27551,Macon
+27552,Mamers
+27553,Manson
+27555,Micro
+27556,Middleburg
+27557,Middlesex
+27559,Moncure
+27560,Morrisville
+27562,New Hill
+27563,Norlina
+27564,Creedmoor
+27565,Oxford
+27568,Pine Level
+27569,Princeton
+27570,Ridgeway
+27571,Rolesville
+27572,Rougemont
+27573,Roxboro
+27576,Selma
+27577,Smithfield
+27581,Stem
+27582,Stovall
+27583,Timberlake
+27584,Townsville
+27586,Vaughan
+27587,Wake Forest
+27588,Wake Forest
+27589,Warrenton
+27591,Wendell
+27592,Willow Spring
+27593,Wilsons Mills
+27594,Wise
+27596,Youngsville
+27597,Zebulon
+27599,Chapel Hill
+27601,Raleigh
+27602,Raleigh
+27603,Raleigh
+27604,Raleigh
+27605,Raleigh
+27606,Raleigh
+27607,Raleigh
+27608,Raleigh
+27609,Raleigh
+27610,Raleigh
+27611,Raleigh
+27612,Raleigh
+27613,Raleigh
+27614,Raleigh
+27615,Raleigh
+27616,Raleigh
+27619,Raleigh
+27620,Raleigh
+27621,Raleigh
+27622,Raleigh
+27623,Raleigh
+27624,Raleigh
+27625,Raleigh
+27626,Raleigh
+27627,Raleigh
+27628,Raleigh
+27629,Raleigh
+27634,Raleigh
+27635,Raleigh
+27636,Raleigh
+27640,Raleigh
+27650,Raleigh
+27656,Raleigh
+27658,Raleigh
+27661,Raleigh
+27668,Raleigh
+27675,Raleigh
+27676,Raleigh
+27690,Raleigh
+27695,Raleigh
+27697,Raleigh
+27698,Raleigh
+27699,Raleigh
+27701,Durham
+27702,Durham
+27703,Durham
+27704,Durham
+27705,Durham
+27706,Durham
+27707,Durham
+27708,Durham
+27709,Durham
+27710,Durham
+27711,Durham
+27712,Durham
+27713,Durham
+27715,Durham
+27717,Durham
+27722,Durham
+27801,Rocky Mount
+27802,Rocky Mount
+27803,Rocky Mount
+27804,Rocky Mount
+27805,Aulander
+27806,Aurora
+27807,Bailey
+27808,Bath
+27809,Battleboro
+27810,Belhaven
+27811,Bellarthur
+27812,Bethel
+27813,Black Creek
+27814,Blounts Creek
+27816,Castalia
+27817,Chocowinity
+27818,Como
+27819,Conetoe
+27820,Conway
+27821,Edward
+27822,Elm City
+27823,Enfield
+27824,Engelhard
+27825,Everetts
+27826,Fairfield
+27827,Falkland
+27828,Farmville
+27829,Fountain
+27830,Fremont
+27831,Garysburg
+27832,Gaston
+27833,Greenville
+27834,Greenville
+27835,Greenville
+27836,Greenville
+27837,Grimesland
+27838,Gumberry
+27839,Halifax
+27840,Hamilton
+27841,Hassell
+27842,Henrico
+27843,Hobgood
+27844,Hollister
+27845,Jackson
+27846,Jamesville
+27847,Kelford
+27849,Lewiston Woodville
+27850,Littleton
+27851,Lucama
+27852,Macclesfield
+27853,Margarettsville
+27854,Milwaukee
+27855,Murfreesboro
+27856,Nashville
+27857,Oak City
+27858,Greenville
+27859,Palmyra
+27860,Pantego
+27861,Parmele
+27862,Pendleton
+27863,Pikeville
+27864,Pinetops
+27865,Pinetown
+27866,Pleasant Hill
+27867,Potecasi
+27868,Red Oak
+27869,Rich Square
+27870,Roanoke Rapids
+27871,Robersonville
+27872,Roxobel
+27873,Saratoga
+27874,Scotland Neck
+27875,Scranton
+27876,Seaboard
+27877,Severn
+27878,Sharpsburg
+27879,Simpson
+27880,Sims
+27881,Speed
+27882,Spring Hope
+27883,Stantonsburg
+27884,Stokes
+27885,Swanquarter
+27886,Tarboro
+27887,Tillery
+27888,Walstonburg
+27889,Washington
+27890,Weldon
+27891,Whitakers
+27892,Williamston
+27893,Wilson
+27894,Wilson
+27895,Wilson
+27896,Wilson
+27897,Woodland
+27906,Elizabeth City
+27907,Elizabeth City
+27909,Elizabeth City
+27910,Ahoskie
+27915,Avon
+27916,Aydlett
+27917,Barco
+27919,Belvidere
+27920,Buxton
+27921,Camden
+27922,Cofield
+27923,Coinjock
+27924,Colerain
+27925,Columbia
+27926,Corapeake
+27927,Corolla
+27928,Creswell
+27929,Currituck
+27930,Durants Neck
+27932,Edenton
+27935,Eure
+27936,Frisco
+27937,Gates
+27938,Gatesville
+27939,Grandy
+27941,Harbinger
+27942,Harrellsville
+27943,Hatteras
+27944,Hertford
+27946,Hobbsville
+27947,Jarvisburg
+27948,Kill Devil Hills
+27949,Kitty Hawk
+27950,Knotts Island
+27953,Manns Harbor
+27954,Manteo
+27956,Maple
+27957,Merry Hill
+27958,Moyock
+27959,Nags Head
+27960,Ocracoke
+27962,Plymouth
+27964,Point Harbor
+27965,Poplar Branch
+27966,Powells Point
+27967,Powellsville
+27968,Rodanthe
+27969,Roduco
+27970,Roper
+27972,Salvo
+27973,Shawboro
+27974,Shiloh
+27976,South Mills
+27978,Stumpy Point
+27979,Sunbury
+27980,Tyner
+27981,Wanchese
+27982,Waves
+27983,Windsor
+27985,Winfall
+27986,Winton
+28001,Albemarle
+28002,Albemarle
+28006,Alexis
+28007,Ansonville
+28009,Badin
+28010,Barium Springs
+28012,Belmont
+28016,Bessemer City
+28017,Boiling Springs
+28018,Bostic
+28019,Caroleen
+28020,Casar
+28021,Cherryville
+28023,China Grove
+28024,Cliffside
+28025,Concord
+28026,Concord
+28027,Concord
+28031,Cornelius
+28032,Cramerton
+28033,Crouse
+28034,Dallas
+28036,Davidson
+28037,Denver
+28038,Earl
+28039,East Spencer
+28040,Ellenboro
+28041,Faith
+28042,Fallston
+28043,Forest City
+28051,Gastonia
+28052,Gastonia
+28053,Gastonia
+28054,Gastonia
+28055,Gastonia
+28056,Gastonia
+28070,Huntersville
+28071,Gold Hill
+28072,Granite Quarry
+28073,Grover
+28074,Harris
+28075,Harrisburg
+28076,Henrietta
+28077,High Shoals
+28078,Huntersville
+28079,Indian Trail
+28080,Iron Station
+28081,Kannapolis
+28082,Kannapolis
+28083,Kannapolis
+28086,Kings Mountain
+28088,Landis
+28089,Lattimore
+28090,Lawndale
+28091,Lilesville
+28092,Lincolnton
+28093,Lincolnton
+28097,Locust
+28098,Lowell
+28101,Mc Adenville
+28102,Mc Farlan
+28103,Marshville
+28104,Matthews
+28105,Matthews
+28106,Matthews
+28107,Midland
+28108,Mineral Springs
+28109,Misenheimer
+28110,Monroe
+28111,Monroe
+28112,Monroe
+28114,Mooresboro
+28115,Mooresville
+28117,Mooresville
+28119,Morven
+28120,Mount Holly
+28123,Mount Mourne
+28124,Mount Pleasant
+28125,Mount Ulla
+28126,Newell
+28127,New London
+28128,Norwood
+28129,Oakboro
+28130,Paw Creek
+28133,Peachland
+28134,Pineville
+28135,Polkton
+28136,Polkville
+28137,Richfield
+28138,Rockwell
+28139,Rutherfordton
+28144,Salisbury
+28145,Salisbury
+28146,Salisbury
+28147,Salisbury
+28150,Shelby
+28151,Shelby
+28152,Shelby
+28159,Spencer
+28160,Spindale
+28163,Stanfield
+28164,Stanley
+28166,Troutman
+28167,Union Mills
+28168,Vale
+28169,Waco
+28170,Wadesboro
+28173,Waxhaw
+28174,Wingate
+28201,Charlotte
+28202,Charlotte
+28203,Charlotte
+28204,Charlotte
+28205,Charlotte
+28206,Charlotte
+28207,Charlotte
+28208,Charlotte
+28209,Charlotte
+28210,Charlotte
+28211,Charlotte
+28212,Charlotte
+28213,Charlotte
+28214,Charlotte
+28215,Charlotte
+28216,Charlotte
+28217,Charlotte
+28218,Charlotte
+28219,Charlotte
+28220,Charlotte
+28221,Charlotte
+28222,Charlotte
+28223,Charlotte
+28224,Charlotte
+28225,Charlotte
+28226,Charlotte
+28227,Charlotte
+28228,Charlotte
+28229,Charlotte
+28230,Charlotte
+28231,Charlotte
+28232,Charlotte
+28233,Charlotte
+28234,Charlotte
+28235,Charlotte
+28236,Charlotte
+28237,Charlotte
+28240,Charlotte
+28241,Charlotte
+28242,Charlotte
+28243,Charlotte
+28244,Charlotte
+28246,Charlotte
+28247,Charlotte
+28250,Charlotte
+28253,Charlotte
+28254,Charlotte
+28255,Charlotte
+28256,Charlotte
+28258,Charlotte
+28260,Charlotte
+28261,Charlotte
+28262,Charlotte
+28265,Charlotte
+28266,Charlotte
+28269,Charlotte
+28270,Charlotte
+28272,Charlotte
+28273,Charlotte
+28274,Charlotte
+28275,Charlotte
+28277,Charlotte
+28278,Charlotte
+28280,Charlotte
+28281,Charlotte
+28282,Charlotte
+28283,Charlotte
+28284,Charlotte
+28285,Charlotte
+28286,Charlotte
+28287,Charlotte
+28288,Charlotte
+28289,Charlotte
+28290,Charlotte
+28296,Charlotte
+28297,Charlotte
+28299,Charlotte
+28301,Fayetteville
+28302,Fayetteville
+28303,Fayetteville
+28304,Fayetteville
+28305,Fayetteville
+28306,Fayetteville
+28307,Fort Bragg
+28308,Pope A F B
+28309,Fayetteville
+28310,Fort Bragg
+28311,Fayetteville
+28314,Fayetteville
+28315,Aberdeen
+28318,Autryville
+28319,Barnesville
+28320,Bladenboro
+28323,Bunnlevel
+28325,Calypso
+28326,Cameron
+28327,Carthage
+28328,Clinton
+28329,Clinton
+28330,Cordova
+28331,Cumberland
+28332,Dublin
+28333,Dudley
+28334,Dunn
+28335,Dunn
+28337,Elizabethtown
+28338,Ellerbe
+28339,Erwin
+28340,Fairmont
+28341,Faison
+28342,Falcon
+28343,Gibson
+28344,Godwin
+28345,Hamlet
+28347,Hoffman
+28348,Hope Mills
+28349,Kenansville
+28350,Lakeview
+28351,Laurel Hill
+28352,Laurinburg
+28353,Laurinburg
+28355,Lemon Springs
+28356,Linden
+28357,Lumber Bridge
+28358,Lumberton
+28359,Lumberton
+28360,Lumberton
+28361,McCain
+28362,Marietta
+28363,Marston
+28364,Maxton
+28365,Mount Olive
+28366,Newton Grove
+28367,Norman
+28368,Olivia
+28369,Orrum
+28370,Pinehurst
+28371,Parkton
+28372,Pembroke
+28373,Pinebluff
+28374,Pinehurst
+28375,Proctorville
+28376,Raeford
+28377,Red Springs
+28378,Rex
+28379,Rockingham
+28380,Rockingham
+28382,Roseboro
+28383,Rowland
+28384,Saint Pauls
+28385,Salemburg
+28386,Shannon
+28387,Southern Pines
+28388,Southern Pines
+28390,Spring Lake
+28391,Stedman
+28392,Tar Heel
+28393,Turkey
+28394,Vass
+28395,Wade
+28396,Wagram
+28398,Warsaw
+28399,White Oak
+28401,Wilmington
+28402,Wilmington
+28403,Wilmington
+28404,Wilmington
+28405,Wilmington
+28406,Wilmington
+28407,Wilmington
+28408,Wilmington
+28409,Wilmington
+28410,Wilmington
+28411,Wilmington
+28412,Wilmington
+28420,Ash
+28421,Atkinson
+28422,Bolivia
+28423,Bolton
+28424,Brunswick
+28425,Burgaw
+28428,Carolina Beach
+28429,Castle Hayne
+28430,Cerro Gordo
+28431,Chadbourn
+28432,Clarendon
+28433,Clarkton
+28434,Council
+28435,Currie
+28436,Delco
+28438,Evergreen
+28439,Fair Bluff
+28441,Garland
+28442,Hallsboro
+28443,Hampstead
+28444,Harrells
+28445,Holly Ridge
+28446,Ingold
+28447,Ivanhoe
+28448,Kelly
+28449,Kure Beach
+28450,Lake Waccamaw
+28451,Leland
+28452,Longwood
+28453,Magnolia
+28454,Maple Hill
+28455,Nakina
+28456,Riegelwood
+28457,Rocky Point
+28458,Rose Hill
+28459,Shallotte
+28460,Sneads Ferry
+28461,Southport
+28462,Supply
+28463,Tabor City
+28464,Teachey
+28465,Oak Island
+28466,Wallace
+28467,Calabash
+28468,Sunset Beach
+28469,Ocean Isle Beach
+28470,South Brunswick
+28471,Watha
+28472,Whiteville
+28478,Willard
+28479,Winnabow
+28480,Wrightsville Beach
+28501,Kinston
+28502,Kinston
+28503,Kinston
+28504,Kinston
+28508,Albertson
+28509,Alliance
+28510,Arapahoe
+28511,Atlantic
+28512,Atlantic Beach
+28513,Ayden
+28515,Bayboro
+28516,Beaufort
+28518,Beulaville
+28519,Bridgeton
+28520,Cedar Island
+28521,Chinquapin
+28522,Comfort
+28523,Cove City
+28524,Davis
+28525,Deep Run
+28526,Dover
+28527,Ernul
+28528,Gloucester
+28529,Grantsboro
+28530,Grifton
+28531,Harkers Island
+28532,Havelock
+28533,Cherry Point
+28537,Hobucken
+28538,Hookerton
+28539,Hubert
+28540,Jacksonville
+28541,Jacksonville
+28542,Camp Lejeune
+28543,Tarawa Terrace
+28544,Midway Park
+28545,McCutcheon Field
+28546,Jacksonville
+28547,Camp Lejeune
+28551,La Grange
+28552,Lowland
+28553,Marshallberg
+28554,Maury
+28555,Maysville
+28556,Merritt
+28557,Morehead City
+28560,New Bern
+28561,New Bern
+28562,New Bern
+28563,New Bern
+28564,New Bern
+28570,Newport
+28571,Oriental
+28572,Pink Hill
+28573,Pollocksville
+28574,Richlands
+28575,Salter Path
+28577,Sealevel
+28578,Seven Springs
+28579,Smyrna
+28580,Snow Hill
+28581,Stacy
+28582,Stella
+28583,Stonewall
+28584,Swansboro
+28585,Trenton
+28586,Vanceboro
+28587,Vandemere
+28589,Williston
+28590,Winterville
+28594,Emerald Isle
+28601,Hickory
+28602,Hickory
+28603,Hickory
+28604,Banner Elk
+28605,Blowing Rock
+28606,Boomer
+28607,Boone
+28608,Boone
+28609,Catawba
+28610,Claremont
+28611,Collettsville
+28612,Connellys Springs
+28613,Conover
+28615,Creston
+28616,Crossnore
+28617,Crumpler
+28618,Deep Gap
+28619,Drexel
+28621,Elkin
+28622,Elk Park
+28623,Ennice
+28624,Ferguson
+28625,Statesville
+28626,Fleetwood
+28627,Glade Valley
+28628,Glen Alpine
+28629,Glendale Springs
+28630,Granite Falls
+28631,Grassy Creek
+28633,Lenoir
+28634,Harmony
+28635,Hays
+28636,Hiddenite
+28637,Hildebran
+28638,Hudson
+28640,Jefferson
+28641,Jonas Ridge
+28642,Jonesville
+28643,Lansing
+28644,Laurel Springs
+28645,Lenoir
+28646,Linville
+28647,Linville Falls
+28649,Mc Grady
+28650,Maiden
+28651,Millers Creek
+28652,Minneapolis
+28653,Montezuma
+28654,Moravian Falls
+28655,Morganton
+28656,North Wilkesboro
+28657,Newland
+28658,Newton
+28659,North Wilkesboro
+28660,Olin
+28661,Patterson
+28662,Pineola
+28663,Piney Creek
+28664,Plumtree
+28665,Purlear
+28666,Icard
+28667,Rhodhiss
+28668,Roaring Gap
+28669,Roaring River
+28670,Ronda
+28671,Rutherford College
+28672,Scottville
+28673,Sherrills Ford
+28674,North Wilkesboro
+28675,Sparta
+28676,State Road
+28677,Statesville
+28678,Stony Point
+28679,Sugar Grove
+28680,Morganton
+28681,Taylorsville
+28682,Terrell
+28683,Thurmond
+28684,Todd
+28685,Traphill
+28687,Statesville
+28688,Turnersburg
+28689,Union Grove
+28690,Valdese
+28691,Valle Crucis
+28692,Vilas
+28693,Warrensville
+28694,West Jefferson
+28697,Wilkesboro
+28698,Zionville
+28699,Scotts
+28701,Alexander
+28702,Almond
+28704,Arden
+28705,Bakersville
+28707,Balsam
+28708,Balsam Grove
+28709,Barnardsville
+28710,Bat Cave
+28711,Black Mountain
+28712,Brevard
+28713,Bryson City
+28714,Burnsville
+28715,Candler
+28716,Canton
+28717,Cashiers
+28718,Cedar Mountain
+28719,Cherokee
+28720,Chimney Rock
+28721,Clyde
+28722,Columbus
+28723,Cullowhee
+28724,Dana
+28725,Dillsboro
+28726,East Flat Rock
+28727,Edneyville
+28728,Enka
+28729,Etowah
+28730,Fairview
+28731,Flat Rock
+28732,Fletcher
+28733,Fontana Dam
+28734,Franklin
+28735,Gerton
+28736,Glenville
+28737,Glenwood
+28738,Hazelwood
+28739,Hendersonville
+28740,Greenmountain
+28741,Highlands
+28742,Horse Shoe
+28743,Hot Springs
+28744,Franklin
+28745,Lake Junaluska
+28746,Lake Lure
+28747,Lake Toxaway
+28748,Leicester
+28749,Little Switzerland
+28750,Lynn
+28751,Maggie Valley
+28752,Marion
+28753,Marshall
+28754,Mars Hill
+28755,Micaville
+28756,Mill Spring
+28757,Montreat
+28758,Mountain Home
+28760,Naples
+28761,Nebo
+28762,Old Fort
+28763,Otto
+28765,Penland
+28766,Penrose
+28768,Pisgah Forest
+28770,Ridgecrest
+28771,Robbinsville
+28772,Rosman
+28773,Saluda
+28774,Sapphire
+28775,Scaly Mountain
+28776,Skyland
+28777,Spruce Pine
+28778,Swannanoa
+28779,Sylva
+28781,Topton
+28782,Tryon
+28783,Tuckasegee
+28784,Tuxedo
+28786,Waynesville
+28787,Weaverville
+28788,Webster
+28789,Whittier
+28790,Zirconia
+28791,Hendersonville
+28792,Hendersonville
+28793,Hendersonville
+28801,Asheville
+28802,Asheville
+28803,Asheville
+28804,Asheville
+28805,Asheville
+28806,Asheville
+28810,Asheville
+28813,Asheville
+28814,Asheville
+28815,Asheville
+28816,Asheville
+28901,Andrews
+28902,Brasstown
+28903,Culberson
+28904,Hayesville
+28905,Marble
+28906,Murphy
+28909,Warne
+59001,Absarokee
+59002,Acton
+59003,Ashland
+59004,Ashland
+59006,Ballantine
+59007,Bearcreek
+59008,Belfry
+59010,Bighorn
+59011,Big Timber
+59012,Birney
+59013,Boyd
+59014,Bridger
+59015,Broadview
+59016,Busby
+59018,Clyde Park
+59019,Columbus
+59020,Cooke City
+59022,Crow Agency
+59024,Custer
+59025,Decker
+59026,Edgar
+59027,Emigrant
+59028,Fishtail
+59029,Fromberg
+59030,Gardiner
+59031,Garryowen
+59032,Grass Range
+59033,Greycliff
+59034,Hardin
+59035,Yellowtail
+59036,Harlowton
+59037,Huntley
+59038,Hysham
+59039,Ingomar
+59041,Joliet
+59043,Lame Deer
+59044,Laurel
+59046,Lavina
+59047,Livingston
+59050,Lodge Grass
+59052,Mc Leod
+59053,Martinsdale
+59054,Melstone
+59055,Melville
+59057,Molt
+59058,Mosby
+59059,Musselshell
+59061,Nye
+59062,Otter
+59063,Park City
+59064,Pompeys Pillar
+59065,Pray
+59066,Pryor
+59067,Rapelje
+59068,Red Lodge
+59069,Reed Point
+59070,Roberts
+59071,Roscoe
+59072,Roundup
+59073,Roundup
+59074,Ryegate
+59075,Saint Xavier
+59076,Sanders
+59077,Sand Springs
+59078,Shawmut
+59079,Shepherd
+59081,Silver Gate
+59082,Springdale
+59083,Sumatra
+59084,Teigen
+59085,Two Dot
+59086,Wilsall
+59087,Winnett
+59088,Worden
+59089,Wyola
+59101,Billings
+59102,Billings
+59103,Billings
+59104,Billings
+59105,Billings
+59106,Billings
+59107,Billings
+59108,Billings
+59111,Billings
+59112,Billings
+59114,Billings
+59115,Billings
+59116,Billings
+59117,Billings
+59201,Wolf Point
+59211,Antelope
+59212,Bainville
+59213,Brockton
+59214,Brockway
+59215,Circle
+59217,Crane
+59218,Culbertson
+59219,Dagmar
+59221,Fairview
+59222,Flaxville
+59223,Fort Peck
+59225,Frazer
+59226,Froid
+59230,Glasgow
+59231,Saint Marie
+59240,Glentana
+59241,Hinsdale
+59242,Homestead
+59243,Lambert
+59244,Larslan
+59245,Mc Cabe
+59247,Medicine Lake
+59248,Nashua
+59250,Opheim
+59252,Outlook
+59253,Peerless
+59254,Plentywood
+59255,Poplar
+59256,Raymond
+59257,Redstone
+59258,Reserve
+59259,Richey
+59260,Richland
+59261,Saco
+59262,Savage
+59263,Scobey
+59270,Sidney
+59273,Vandalia
+59274,Vida
+59275,Westby
+59276,Whitetail
+59301,Miles City
+59311,Alzada
+59312,Angela
+59313,Baker
+59314,Biddle
+59315,Bloomfield
+59316,Boyes
+59317,Broadus
+59318,Brusett
+59319,Capitol
+59322,Cohagen
+59323,Colstrip
+59324,Ekalaka
+59326,Fallon
+59327,Forsyth
+59330,Glendive
+59332,Hammond
+59333,Hathaway
+59336,Ismay
+59337,Jordan
+59338,Kinsey
+59339,Lindsay
+59341,Mildred
+59343,Olive
+59344,Plevna
+59345,Powderville
+59347,Rosebud
+59348,Sonnette
+59349,Terry
+59351,Volborg
+59353,Wibaux
+59354,Willard
+59401,Great Falls
+59402,Malmstrom A F B
+59403,Great Falls
+59404,Great Falls
+59405,Great Falls
+59406,Great Falls
+59410,Augusta
+59411,Babb
+59412,Belt
+59414,Black Eagle
+59416,Brady
+59417,Browning
+59418,Buffalo
+59419,Bynum
+59420,Carter
+59421,Cascade
+59422,Choteau
+59424,Coffee Creek
+59425,Conrad
+59427,Cut Bank
+59430,Denton
+59432,Dupuyer
+59433,Dutton
+59434,East Glacier Park
+59435,Ethridge
+59436,Fairfield
+59440,Floweree
+59441,Forestgrove
+59442,Fort Benton
+59443,Fort Shaw
+59444,Galata
+59445,Garneill
+59446,Geraldine
+59447,Geyser
+59448,Heart Butte
+59450,Highwood
+59451,Hilger
+59452,Hobson
+59453,Judith Gap
+59454,Kevin
+59456,Ledger
+59457,Lewistown
+59460,Loma
+59461,Lothair
+59462,Moccasin
+59463,Monarch
+59464,Moore
+59465,Neihart
+59466,Oilmont
+59467,Pendroy
+59468,Power
+59469,Raynesford
+59471,Roy
+59472,Sand Coulee
+59473,Santa Rita
+59474,Shelby
+59477,Simms
+59479,Stanford
+59480,Stockett
+59482,Sunburst
+59483,Sun River
+59484,Sweet Grass
+59485,Ulm
+59486,Valier
+59487,Vaughn
+59489,Winifred
+59501,Havre
+59520,Big Sandy
+59521,Box Elder
+59522,Chester
+59523,Chinook
+59524,Dodson
+59525,Gildford
+59526,Harlem
+59527,Hays
+59528,Hingham
+59529,Hogeland
+59530,Inverness
+59531,Joplin
+59532,Kremlin
+59535,Lloyd
+59537,Loring
+59538,Malta
+59540,Rudyard
+59542,Turner
+59544,Whitewater
+59545,Whitlash
+59546,Zortman
+59547,Zurich
+59601,Helena
+59602,Helena
+59604,Helena
+59620,Helena
+59623,Helena
+59624,Helena
+59625,Helena
+59626,Helena
+59631,Basin
+59632,Boulder
+59633,Canyon Creek
+59634,Clancy
+59635,East Helena
+59636,Fort Harrison
+59638,Jefferson City
+59639,Lincoln
+59640,Marysville
+59641,Radersburg
+59642,Ringling
+59643,Toston
+59644,Townsend
+59645,White Sulphur Springs
+59647,Winston
+59648,Wolf Creek
+59701,Butte
+59702,Butte
+59703,Butte
+59707,Butte
+59710,Alder
+59711,Anaconda
+59713,Avon
+59714,Belgrade
+59715,Bozeman
+59716,Big Sky
+59717,Bozeman
+59718,Bozeman
+59719,Bozeman
+59720,Cameron
+59721,Cardwell
+59722,Deer Lodge
+59724,Dell
+59725,Dillon
+59727,Divide
+59728,Elliston
+59729,Ennis
+59730,Gallatin Gateway
+59731,Garrison
+59732,Glen
+59733,Gold Creek
+59735,Harrison
+59736,Jackson
+59739,Lima
+59740,Mc Allister
+59741,Manhattan
+59743,Melrose
+59745,Norris
+59746,Polaris
+59747,Pony
+59748,Ramsay
+59749,Sheridan
+59750,Butte
+59751,Silver Star
+59752,Three Forks
+59754,Twin Bridges
+59755,Virginia City
+59756,Warm Springs
+59758,West Yellowstone
+59759,Whitehall
+59760,Willow Creek
+59761,Wisdom
+59762,Wise River
+59771,Bozeman
+59772,Bozeman
+59773,Bozeman
+59801,Missoula
+59802,Missoula
+59803,Missoula
+59804,Missoula
+59806,Missoula
+59807,Missoula
+59808,Missoula
+59812,Missoula
+59820,Alberton
+59821,Arlee
+59823,Bonner
+59824,Charlo
+59825,Clinton
+59826,Condon
+59827,Conner
+59828,Corvallis
+59829,Darby
+59830,De Borgia
+59831,Dixon
+59832,Drummond
+59833,Florence
+59834,Frenchtown
+59835,Grantsdale
+59836,Greenough
+59837,Hall
+59840,Hamilton
+59841,Pinesdale
+59842,Haugan
+59843,Helmville
+59844,Heron
+59845,Hot Springs
+59846,Huson
+59847,Lolo
+59848,Lonepine
+59851,Milltown
+59853,Noxon
+59854,Ovando
+59855,Pablo
+59856,Paradise
+59858,Philipsburg
+59859,Plains
+59860,Polson
+59863,Ravalli
+59864,Ronan
+59865,Saint Ignatius
+59866,Saint Regis
+59867,Saltese
+59868,Seeley Lake
+59870,Stevensville
+59871,Sula
+59872,Superior
+59873,Thompson Falls
+59874,Trout Creek
+59875,Victor
+59901,Kalispell
+59902,Kalispell
+59903,Kalispell
+59904,Kalispell
+59910,Big Arm
+59911,Bigfork
+59912,Columbia Falls
+59913,Coram
+59914,Dayton
+59915,Elmo
+59916,Essex
+59917,Eureka
+59918,Fortine
+59919,Hungry Horse
+59920,Kila
+59921,Lake Mc Donald
+59922,Lakeside
+59923,Libby
+59925,Marion
+59926,Martin City
+59927,Olney
+59928,Polebridge
+59929,Proctor
+59930,Rexford
+59931,Rollins
+59932,Somers
+59933,Stryker
+59934,Trego
+59935,Troy
+59936,West Glacier
+59937,Whitefish
+38601,Abbeville
+38602,Arkabutla
+38603,Ashland
+38606,Batesville
+38609,Belen
+38610,Blue Mountain
+38611,Byhalia
+38614,Clarksdale
+38617,Coahoma
+38618,Coldwater
+38619,Como
+38620,Courtland
+38621,Crenshaw
+38622,Crowder
+38623,Darling
+38625,Dumas
+38626,Dundee
+38627,Etta
+38628,Falcon
+38629,Falkner
+38630,Farrell
+38631,Friars Point
+38632,Hernando
+38633,Hickory Flat
+38634,Holly Springs
+38635,Holly Springs
+38637,Horn Lake
+38638,Independence
+38639,Jonestown
+38641,Lake Cormorant
+38642,Lamar
+38643,Lambert
+38644,Lula
+38645,Lyon
+38646,Marks
+38647,Michigan City
+38649,Mount Pleasant
+38650,Myrtle
+38651,Nesbit
+38652,New Albany
+38654,Olive Branch
+38655,Oxford
+38658,Pope
+38659,Potts Camp
+38661,Red Banks
+38663,Ripley
+38664,Robinsonville
+38665,Sarah
+38666,Sardis
+38668,Senatobia
+38669,Sherard
+38670,Sledge
+38671,Southaven
+38672,Southaven
+38673,Taylor
+38674,Tiplersville
+38675,Tula
+38676,Tunica
+38677,University
+38679,Victoria
+38680,Walls
+38683,Walnut
+38685,Waterford
+38686,Walls
+38701,Greenville
+38702,Greenville
+38703,Greenville
+38704,Greenville
+38720,Alligator
+38721,Anguilla
+38722,Arcola
+38723,Avon
+38725,Benoit
+38726,Beulah
+38730,Boyle
+38731,Chatham
+38732,Cleveland
+38733,Cleveland
+38736,Doddsville
+38737,Drew
+38738,Parchman
+38739,Dublin
+38740,Duncan
+38744,Glen Allan
+38745,Grace
+38746,Gunnison
+38748,Hollandale
+38749,Holly Ridge
+38751,Indianola
+38753,Inverness
+38754,Isola
+38756,Leland
+38758,Mattson
+38759,Merigold
+38760,Metcalfe
+38761,Moorhead
+38762,Mound Bayou
+38763,Nitta Yuma
+38764,Pace
+38765,Panther Burn
+38767,Rena Lara
+38768,Rome
+38769,Rosedale
+38771,Ruleville
+38772,Scott
+38773,Shaw
+38774,Shelby
+38776,Stoneville
+38778,Sunflower
+38780,Wayside
+38781,Winstonville
+38782,Winterville
+38801,Tupelo
+38802,Tupelo
+38803,Tupelo
+38804,Tupelo
+38820,Algoma
+38821,Amory
+38824,Baldwyn
+38825,Becker
+38826,Belden
+38827,Belmont
+38828,Blue Springs
+38829,Booneville
+38833,Burnsville
+38834,Corinth
+38835,Corinth
+38838,Dennis
+38839,Derma
+38841,Ecru
+38843,Fulton
+38844,Gattman
+38846,Glen
+38847,Golden
+38848,Greenwood Springs
+38849,Guntown
+38850,Houlka
+38851,Houston
+38852,Iuka
+38854,Mc Condy
+38855,Mantachie
+38856,Marietta
+38857,Mooreville
+38858,Nettleton
+38859,New Site
+38860,Okolona
+38862,Plantersville
+38863,Pontotoc
+38864,Randolph
+38865,Rienzi
+38866,Saltillo
+38868,Shannon
+38869,Sherman
+38870,Smithville
+38871,Thaxton
+38873,Tishomingo
+38874,Toccopola
+38875,Trebloc
+38876,Tremont
+38877,Van Vleet
+38878,Vardaman
+38879,Verona
+38880,Wheeler
+38901,Grenada
+38902,Grenada
+38912,Avalon
+38913,Banner
+38914,Big Creek
+38915,Bruce
+38916,Calhoun City
+38917,Carrollton
+38920,Cascilla
+38921,Charleston
+38922,Coffeeville
+38923,Coila
+38924,Cruger
+38925,Duck Hill
+38926,Elliott
+38927,Enid
+38928,Glendora
+38929,Gore Springs
+38930,Greenwood
+38935,Greenwood
+38940,Holcomb
+38941,Itta Bena
+38943,Mc Carley
+38944,Minter City
+38945,Money
+38946,Morgan City
+38947,North Carrollton
+38948,Oakland
+38949,Paris
+38950,Philipp
+38951,Pittsboro
+38952,Schlater
+38953,Scobey
+38954,Sidon
+38955,Slate Spring
+38957,Sumner
+38958,Swan Lake
+38959,Swiftown
+38960,Tie Plant
+38961,Tillatoba
+38962,Tippo
+38963,Tutwiler
+38964,Vance
+38965,Water Valley
+38966,Webb
+38967,Winona
+39038,Belzoni
+39039,Benton
+39040,Bentonia
+39041,Bolton
+39042,Brandon
+39043,Brandon
+39044,Braxton
+39045,Camden
+39046,Canton
+39047,Brandon
+39051,Carthage
+39054,Cary
+39056,Clinton
+39057,Conehatta
+39058,Clinton
+39059,Crystal Springs
+39060,Clinton
+39061,Delta City
+39062,D Lo
+39063,Durant
+39066,Edwards
+39067,Ethel
+39069,Fayette
+39071,Flora
+39072,Pocahontas
+39073,Florence
+39074,Forest
+39077,Gallman
+39078,Georgetown
+39079,Goodman
+39080,Harperville
+39081,Harriston
+39082,Harrisville
+39083,Hazlehurst
+39086,Hermanville
+39087,Hillsboro
+39088,Holly Bluff
+39090,Kosciusko
+39092,Lake
+39094,Lena
+39095,Lexington
+39096,Lorman
+39097,Louise
+39098,Ludlow
+39107,Mc Adams
+39108,Mc Cool
+39109,Madden
+39110,Madison
+39111,Magee
+39112,Sanatorium
+39113,Mayersville
+39114,Mendenhall
+39115,Midnight
+39116,Mize
+39117,Morton
+39119,Mount Olive
+39120,Natchez
+39121,Natchez
+39122,Natchez
+39130,Madison
+39140,Newhebron
+39144,Pattison
+39145,Pelahatchie
+39146,Pickens
+39148,Piney Woods
+39149,Pinola
+39150,Port Gibson
+39151,Puckett
+39152,Pulaski
+39153,Raleigh
+39154,Raymond
+39156,Redwood
+39157,Ridgeland
+39158,Ridgeland
+39159,Rolling Fork
+39160,Sallis
+39161,Sandhill
+39162,Satartia
+39163,Sharon
+39165,Sibley
+39166,Silver City
+39167,Star
+39168,Taylorsville
+39169,Tchula
+39170,Terry
+39171,Thomastown
+39173,Tinsley
+39174,Tougaloo
+39175,Utica
+39176,Vaiden
+39177,Valley Park
+39179,Vaughan
+39180,Vicksburg
+39181,Vicksburg
+39182,Vicksburg
+39183,Vicksburg
+39189,Walnut Grove
+39190,Washington
+39191,Wesson
+39192,West
+39193,Whitfield
+39194,Yazoo City
+39200,Jackson
+39201,Jackson
+39202,Jackson
+39203,Jackson
+39204,Jackson
+39205,Jackson
+39206,Jackson
+39207,Jackson
+39208,Jackson
+39209,Jackson
+39210,Jackson
+39211,Jackson
+39212,Jackson
+39213,Jackson
+39215,Jackson
+39216,Jackson
+39217,Jackson
+39218,Jackson
+39225,Jackson
+39232,Jackson
+39235,Jackson
+39236,Jackson
+39250,Jackson
+39269,Jackson
+39271,Jackson
+39272,Jackson
+39282,Jackson
+39283,Jackson
+39284,Jackson
+39286,Jackson
+39288,Jackson
+39289,Jackson
+39296,Jackson
+39298,Jackson
+39301,Meridian
+39302,Meridian
+39303,Meridian
+39304,Meridian
+39305,Meridian
+39307,Meridian
+39309,Meridian
+39320,Bailey
+39322,Buckatunna
+39323,Chunky
+39324,Clara
+39325,Collinsville
+39326,Daleville
+39327,Decatur
+39328,De Kalb
+39330,Enterprise
+39332,Hickory
+39335,Lauderdale
+39336,Lawrence
+39337,Little Rock
+39338,Louin
+39339,Louisville
+39341,Macon
+39342,Marion
+39345,Newton
+39346,Noxapater
+39347,Pachuta
+39348,Paulding
+39350,Philadelphia
+39352,Porterville
+39354,Preston
+39355,Quitman
+39356,Rose Hill
+39358,Scooba
+39359,Sebastopol
+39360,Shubuta
+39361,Shuqualak
+39362,State Line
+39363,Stonewall
+39364,Toomsuba
+39365,Union
+39366,Vossburg
+39367,Waynesboro
+39401,Hattiesburg
+39402,Hattiesburg
+39403,Hattiesburg
+39404,Hattiesburg
+39406,Hattiesburg
+39407,Hattiesburg
+39421,Bassfield
+39422,Bay Springs
+39423,Beaumont
+39425,Brooklyn
+39426,Carriere
+39427,Carson
+39428,Collins
+39429,Columbia
+39436,Eastabuchie
+39437,Ellisville
+39439,Heidelberg
+39440,Laurel
+39441,Laurel
+39442,Laurel
+39443,Laurel
+39451,Leakesville
+39452,Lucedale
+39455,Lumberton
+39456,Mc Lain
+39457,Mc Neill
+39459,Moselle
+39460,Moss
+39461,Neely
+39462,New Augusta
+39463,Nicholson
+39464,Ovett
+39465,Petal
+39466,Picayune
+39470,Poplarville
+39474,Prentiss
+39475,Purvis
+39476,Richton
+39477,Sandersville
+39478,Sandy Hook
+39479,Seminary
+39480,Soso
+39481,Stringer
+39482,Sumrall
+39483,Foxworth
+39501,Gulfport
+39502,Gulfport
+39503,Gulfport
+39505,Gulfport
+39506,Gulfport
+39507,Gulfport
+39520,Bay Saint Louis
+39521,Bay Saint Louis
+39522,Stennis Space Center
+39525,Diamondhead
+39529,Stennis Space Center
+39530,Biloxi
+39531,Biloxi
+39532,Biloxi
+39533,Biloxi
+39534,Biloxi
+39535,Biloxi
+39552,Escatawpa
+39553,Gautier
+39555,Hurley
+39556,Kiln
+39558,Lakeshore
+39560,Long Beach
+39561,Mc Henry
+39562,Moss Point
+39563,Moss Point
+39564,Ocean Springs
+39565,Ocean Springs
+39566,Ocean Springs
+39567,Pascagoula
+39568,Pascagoula
+39569,Pascagoula
+39571,Pass Christian
+39572,Pearlington
+39573,Perkinston
+39574,Saucier
+39576,Waveland
+39577,Wiggins
+39581,Pascagoula
+39595,Pascagoula
+39601,Brookhaven
+39602,Brookhaven
+39603,Brookhaven
+39629,Bogue Chitto
+39630,Bude
+39631,Centreville
+39632,Chatawa
+39633,Crosby
+39635,Fernwood
+39638,Gloster
+39641,Jayess
+39643,Kokomo
+39645,Liberty
+39647,Mc Call Creek
+39648,McComb
+39649,McComb
+39652,Magnolia
+39653,Meadville
+39654,Monticello
+39656,Oak Vale
+39657,Osyka
+39661,Roxie
+39662,Ruth
+39663,Silver Creek
+39664,Smithdale
+39665,Sontag
+39666,Summit
+39667,Tylertown
+39668,Union Church
+39669,Woodville
+39701,Columbus
+39702,Columbus
+39703,Columbus
+39704,Columbus
+39705,Columbus
+39710,Columbus
+39730,Aberdeen
+39735,Ackerman
+39736,Artesia
+39737,Bellefontaine
+39739,Brooksville
+39740,Caledonia
+39741,Cedarbluff
+39743,Crawford
+39744,Eupora
+39745,French Camp
+39746,Hamilton
+39747,Kilmichael
+39750,Maben
+39751,Mantee
+39752,Mathiston
+39753,Mayhew
+39754,Montpelier
+39755,Pheba
+39756,Prairie
+39759,Starkville
+39760,Starkville
+39762,Mississippi State
+39766,Steens
+39767,Stewart
+39769,Sturgis
+39771,Walthall
+39772,Weir
+39773,West Point
+39776,Woodland
+96950,Saipan
+96951,Rota
+96952,Tinian
+63001,Allenton
+63005,Chesterfield
+63006,Chesterfield
+63010,Arnold
+63011,Ballwin
+63012,Barnhart
+63013,Beaufort
+63014,Berger
+63015,Catawissa
+63016,Cedar Hill
+63017,Chesterfield
+63019,Crystal City
+63020,De Soto
+63021,Ballwin
+63022,Ballwin
+63023,Dittmer
+63024,Ballwin
+63025,Eureka
+63026,Fenton
+63028,Festus
+63030,Fletcher
+63031,Florissant
+63032,Florissant
+63033,Florissant
+63034,Florissant
+63036,French Village
+63037,Gerald
+63038,Glencoe
+63039,Gray Summit
+63040,Grover
+63041,Grubville
+63042,Hazelwood
+63043,Maryland Heights
+63044,Bridgeton
+63045,Earth City
+63047,Hematite
+63048,Herculaneum
+63049,High Ridge
+63050,Hillsboro
+63051,House Springs
+63052,Imperial
+63053,Kimmswick
+63055,Labadie
+63056,Leslie
+63057,Liguori
+63060,Lonedell
+63061,Luebbering
+63065,Mapaville
+63066,Morse Mill
+63068,New Haven
+63069,Pacific
+63070,Pevely
+63071,Richwoods
+63072,Robertsville
+63073,Saint Albans
+63074,Saint Ann
+63077,Saint Clair
+63079,Stanton
+63080,Sullivan
+63084,Union
+63087,Valles Mines
+63088,Valley Park
+63089,Villa Ridge
+63090,Washington
+63091,Rosebud
+63099,Fenton
+63101,Saint Louis
+63102,Saint Louis
+63103,Saint Louis
+63104,Saint Louis
+63105,Saint Louis
+63106,Saint Louis
+63107,Saint Louis
+63108,Saint Louis
+63109,Saint Louis
+63110,Saint Louis
+63111,Saint Louis
+63112,Saint Louis
+63113,Saint Louis
+63114,Saint Louis
+63115,Saint Louis
+63116,Saint Louis
+63117,Saint Louis
+63118,Saint Louis
+63119,Saint Louis
+63120,Saint Louis
+63121,Saint Louis
+63122,Saint Louis
+63123,Saint Louis
+63124,Saint Louis
+63125,Saint Louis
+63126,Saint Louis
+63127,Saint Louis
+63128,Saint Louis
+63129,Saint Louis
+63130,Saint Louis
+63131,Saint Louis
+63132,Saint Louis
+63133,Saint Louis
+63134,Saint Louis
+63135,Saint Louis
+63136,Saint Louis
+63137,Saint Louis
+63138,Saint Louis
+63139,Saint Louis
+63140,Saint Louis
+63141,Saint Louis
+63143,Saint Louis
+63144,Saint Louis
+63145,Saint Louis
+63146,Saint Louis
+63147,Saint Louis
+63150,Saint Louis
+63151,Saint Louis
+63153,Saint Louis
+63155,Saint Louis
+63156,Saint Louis
+63157,Saint Louis
+63158,Saint Louis
+63160,Saint Louis
+63163,Saint Louis
+63164,Saint Louis
+63166,Saint Louis
+63167,Saint Louis
+63169,Saint Louis
+63171,Saint Louis
+63177,Saint Louis
+63178,Saint Louis
+63179,Saint Louis
+63180,Saint Louis
+63182,Saint Louis
+63188,Saint Louis
+63195,Saint Louis
+63196,Saint Louis
+63197,Saint Louis
+63198,Saint Louis
+63199,Saint Louis
+63301,Saint Charles
+63302,Saint Charles
+63303,Saint Charles
+63304,Saint Charles
+63330,Annada
+63332,Augusta
+63333,Bellflower
+63334,Bowling Green
+63336,Clarksville
+63338,Cottleville
+63339,Curryville
+63341,Defiance
+63342,Dutzow
+63343,Elsberry
+63344,Eolia
+63345,Farber
+63346,Flinthill
+63347,Foley
+63348,Foristell
+63349,Hawk Point
+63350,High Hill
+63351,Jonesburg
+63352,Laddonia
+63353,Louisiana
+63357,Marthasville
+63359,Middletown
+63361,Montgomery City
+63362,Moscow Mills
+63363,New Florence
+63365,New Melle
+63366,O Fallon
+63367,Lake Saint Louis
+63369,Old Monroe
+63370,Olney
+63373,Portage des Sioux
+63376,Saint Peters
+63377,Silex
+63378,Treloar
+63379,Troy
+63381,Truxton
+63382,Vandalia
+63383,Warrenton
+63384,Wellsville
+63385,Wentzville
+63386,West Alton
+63387,Whiteside
+63388,Williamsburg
+63389,Winfield
+63390,Wright City
+63401,Hannibal
+63430,Alexandria
+63431,Anabel
+63432,Arbela
+63433,Ashburn
+63434,Bethel
+63435,Canton
+63436,Center
+63437,Clarence
+63438,Durham
+63439,Emden
+63440,Ewing
+63441,Frankford
+63442,Granger
+63443,Hunnewell
+63445,Kahoka
+63446,Knox City
+63447,La Belle
+63448,La Grange
+63450,Lentner
+63451,Leonard
+63452,Lewistown
+63453,Luray
+63454,Maywood
+63456,Monroe City
+63457,Monticello
+63458,Newark
+63459,New London
+63460,Novelty
+63461,Palmyra
+63462,Perry
+63463,Philadelphia
+63464,Plevna
+63465,Revere
+63466,Saint Patrick
+63467,Saverton
+63468,Shelbina
+63469,Shelbyville
+63471,Taylor
+63472,Wayland
+63473,Williamstown
+63474,Wyaconda
+63501,Kirksville
+63530,Atlanta
+63531,Baring
+63532,Bevier
+63533,Brashear
+63534,Callao
+63535,Coatsville
+63536,Downing
+63537,Edina
+63538,Elmer
+63539,Ethel
+63540,Gibbs
+63541,Glenwood
+63543,Gorin
+63544,Green Castle
+63545,Green City
+63546,Greentop
+63547,Hurdland
+63548,Lancaster
+63549,La Plata
+63551,Livonia
+63552,Macon
+63555,Memphis
+63556,Milan
+63557,New Boston
+63558,New Cambria
+63559,Novinger
+63560,Pollock
+63561,Queen City
+63563,Rutledge
+63565,Unionville
+63566,Winigan
+63567,Worthington
+63601,Park Hills
+63620,Annapolis
+63621,Arcadia
+63622,Belgrade
+63623,Belleview
+63624,Bismarck
+63625,Black
+63626,Blackwell
+63627,Bloomsdale
+63628,Bonne Terre
+63629,Bunker
+63630,Cadet
+63631,Caledonia
+63632,Cascade
+63633,Centerville
+63636,Des Arc
+63637,Doe Run
+63638,Ellington
+63640,Farmington
+63645,Fredericktown
+63646,Glover
+63648,Irondale
+63650,Ironton
+63651,Knob Lick
+63653,Leadwood
+63654,Lesterville
+63655,Marquand
+63656,Middle Brook
+63660,Mineral Point
+63661,New Offenburg
+63662,Patton
+63663,Pilot Knob
+63664,Potosi
+63665,Redford
+63666,Reynolds
+63670,Sainte Genevieve
+63673,Saint Mary
+63674,Tiff
+63675,Vulcan
+63701,Cape Girardeau
+63702,Cape Girardeau
+63703,Cape Girardeau
+63705,Cape Girardeau
+63730,Advance
+63732,Altenburg
+63735,Bell City
+63736,Benton
+63737,Brazeau
+63738,Brownwood
+63739,Burfordville
+63740,Chaffee
+63742,Commerce
+63743,Daisy
+63744,Delta
+63745,Dutchtown
+63746,Farrar
+63747,Friedheim
+63748,Frohna
+63750,Gipsy
+63751,Glenallen
+63752,Gordonville
+63753,Grassy
+63755,Jackson
+63758,Kelso
+63760,Leopold
+63763,Mc Gee
+63764,Marble Hill
+63766,Millersville
+63767,Morley
+63769,Oak Ridge
+63770,Old Appleton
+63771,Oran
+63772,Painton
+63774,Perkins
+63775,Perryville
+63776,Mc Bride
+63779,Pocahontas
+63780,Scott City
+63781,Sedgewickville
+63782,Sturdivant
+63783,Uniontown
+63784,Vanduser
+63785,Whitewater
+63787,Zalma
+63801,Sikeston
+63820,Anniston
+63821,Arbyrd
+63822,Bernie
+63823,Bertrand
+63824,Blodgett
+63825,Bloomfield
+63826,Braggadocio
+63827,Bragg City
+63828,Canalou
+63829,Cardwell
+63830,Caruthersville
+63833,Catron
+63834,Charleston
+63837,Clarkton
+63838,Conran
+63839,Cooter
+63840,Deering
+63841,Dexter
+63845,East Prairie
+63846,Essex
+63847,Gibson
+63848,Gideon
+63849,Gobler
+63850,Grayridge
+63851,Hayti
+63852,Holcomb
+63853,Holland
+63855,Hornersville
+63857,Kennett
+63860,Kewanee
+63862,Lilbourn
+63863,Malden
+63866,Marston
+63867,Matthews
+63868,Morehouse
+63869,New Madrid
+63870,Parma
+63871,Pascola
+63873,Portageville
+63874,Risco
+63875,Rives
+63876,Senath
+63877,Steele
+63878,Tallapoosa
+63879,Wardell
+63880,Whiteoak
+63881,Wolf Island
+63882,Wyatt
+63901,Poplar Bluff
+63902,Poplar Bluff
+63931,Briar
+63932,Broseley
+63933,Campbell
+63934,Clubb
+63935,Doniphan
+63936,Dudley
+63937,Ellsinore
+63938,Fagus
+63939,Fairdealing
+63940,Fisk
+63941,Fremont
+63942,Gatewood
+63943,Grandin
+63944,Greenville
+63945,Harviell
+63947,Hiram
+63950,Lodi
+63951,Lowndes
+63952,Mill Spring
+63953,Naylor
+63954,Neelyville
+63955,Oxly
+63956,Patterson
+63957,Piedmont
+63960,Puxico
+63961,Qulin
+63962,Rombauer
+63963,Shook
+63964,Silva
+63965,Van Buren
+63966,Wappapello
+63967,Williamsville
+64001,Alma
+64011,Bates City
+64012,Belton
+64013,Blue Springs
+64014,Blue Springs
+64015,Blue Springs
+64016,Buckner
+64017,Camden
+64018,Camden Point
+64019,Centerview
+64020,Concordia
+64021,Corder
+64022,Dover
+64024,Excelsior Springs
+64028,Farley
+64029,Grain Valley
+64030,Grandview
+64034,Greenwood
+64035,Hardin
+64036,Henrietta
+64037,Higginsville
+64040,Holden
+64048,Holt
+64050,Independence
+64051,Independence
+64052,Independence
+64053,Independence
+64054,Independence
+64055,Independence
+64056,Independence
+64057,Independence
+64058,Independence
+64060,Kearney
+64061,Kingsville
+64062,Lawson
+64063,Lees Summit
+64064,Lees Summit
+64065,Lees Summit
+64066,Levasy
+64067,Lexington
+64068,Liberty
+64069,Liberty
+64070,Lone Jack
+64071,Mayview
+64072,Missouri City
+64073,Mosby
+64074,Napoleon
+64075,Oak Grove
+64076,Odessa
+64077,Orrick
+64078,Peculiar
+64079,Platte City
+64080,Pleasant Hill
+64081,Lees Summit
+64082,Lees Summit
+64083,Raymore
+64084,Rayville
+64085,Richmond
+64086,Lees Summit
+64087,Liberty
+64088,Sibley
+64089,Smithville
+64090,Strasburg
+64092,Waldron
+64093,Warrensburg
+64096,Waverly
+64097,Wellington
+64098,Weston
+64101,Kansas City
+64102,Kansas City
+64105,Kansas City
+64106,Kansas City
+64108,Kansas City
+64109,Kansas City
+64110,Kansas City
+64111,Kansas City
+64112,Kansas City
+64113,Kansas City
+64114,Kansas City
+64116,Kansas City
+64117,Kansas City
+64118,Kansas City
+64119,Kansas City
+64120,Kansas City
+64121,Kansas City
+64123,Kansas City
+64124,Kansas City
+64125,Kansas City
+64126,Kansas City
+64127,Kansas City
+64128,Kansas City
+64129,Kansas City
+64130,Kansas City
+64131,Kansas City
+64132,Kansas City
+64133,Kansas City
+64134,Kansas City
+64136,Kansas City
+64137,Kansas City
+64138,Kansas City
+64139,Kansas City
+64141,Kansas City
+64142,Kansas City
+64144,Kansas City
+64145,Kansas City
+64146,Kansas City
+64147,Kansas City
+64148,Kansas City
+64149,Kansas City
+64150,Riverside
+64151,Kansas City
+64152,Kansas City
+64153,Kansas City
+64154,Kansas City
+64155,Kansas City
+64156,Kansas City
+64157,Kansas City
+64158,Kansas City
+64160,Kansas City
+64161,Kansas City
+64163,Kansas City
+64164,Kansas City
+64165,Kansas City
+64166,Kansas City
+64167,Kansas City
+64168,Kansas City
+64170,Kansas City
+64171,Kansas City
+64172,Kansas City
+64173,Kansas City
+64179,Kansas City
+64180,Kansas City
+64183,Kansas City
+64184,Kansas City
+64185,Kansas City
+64187,Kansas City
+64188,Kansas City
+64189,Kansas City
+64190,Kansas City
+64191,Kansas City
+64192,Kansas City
+64193,Kansas City
+64194,Kansas City
+64195,Kansas City
+64196,Kansas City
+64197,Kansas City
+64198,Kansas City
+64199,Kansas City
+64401,Agency
+64402,Albany
+64420,Allendale
+64421,Amazonia
+64422,Amity
+64423,Barnard
+64424,Bethany
+64426,Blythedale
+64427,Bolckow
+64428,Burlington Junction
+64429,Cameron
+64430,Clarksdale
+64431,Clearmont
+64432,Clyde
+64433,Conception
+64434,Conception Junction
+64436,Cosby
+64437,Craig
+64438,Darlington
+64439,Dearborn
+64440,De Kalb
+64441,Denver
+64442,Eagleville
+64443,Easton
+64444,Edgerton
+64445,Elmo
+64446,Fairfax
+64447,Fairport
+64448,Faucett
+64449,Fillmore
+64451,Forest City
+64453,Gentry
+64454,Gower
+64455,Graham
+64456,Grant City
+64457,Guilford
+64458,Hatfield
+64459,Helena
+64461,Hopkins
+64463,King City
+64465,Lathrop
+64466,Maitland
+64467,Martinsville
+64468,Maryville
+64469,Maysville
+64470,Mound City
+64471,New Hampton
+64473,Oregon
+64474,Osborn
+64475,Parnell
+64476,Pickering
+64477,Plattsburg
+64478,Quitman
+64479,Ravenwood
+64480,Rea
+64481,Ridgeway
+64482,Rock Port
+64483,Rosendale
+64484,Rushville
+64485,Savannah
+64486,Sheridan
+64487,Skidmore
+64489,Stanberry
+64490,Stewartsville
+64491,Tarkio
+64492,Trimble
+64493,Turney
+64494,Union Star
+64496,Watson
+64497,Weatherby
+64498,Westboro
+64499,Worth
+64501,Saint Joseph
+64502,Saint Joseph
+64503,Saint Joseph
+64504,Saint Joseph
+64505,Saint Joseph
+64506,Saint Joseph
+64507,Saint Joseph
+64508,Saint Joseph
+64601,Chillicothe
+64620,Altamont
+64622,Bogard
+64623,Bosworth
+64624,Braymer
+64625,Breckenridge
+64628,Brookfield
+64630,Browning
+64631,Bucklin
+64632,Cainsville
+64633,Carrollton
+64635,Chula
+64636,Coffey
+64637,Cowgill
+64638,Dawn
+64639,De Witt
+64640,Gallatin
+64641,Galt
+64642,Gilman City
+64643,Hale
+64644,Hamilton
+64645,Harris
+64646,Humphreys
+64647,Jameson
+64648,Jamesport
+64649,Kidder
+64650,Kingston
+64651,Laclede
+64652,Laredo
+64653,Linneus
+64654,Lock Springs
+64655,Lucerne
+64656,Ludlow
+64657,Mc Fall
+64658,Marceline
+64659,Meadville
+64660,Mendon
+64661,Mercer
+64664,Mooresville
+64667,Newtown
+64668,Norborne
+64670,Pattonsburg
+64671,Polo
+64672,Powersville
+64673,Princeton
+64674,Purdin
+64676,Rothville
+64679,Spickard
+64680,Stet
+64681,Sumner
+64682,Tina
+64683,Trenton
+64686,Utica
+64687,Wakenda
+64688,Wheeling
+64689,Winston
+64701,Harrisonville
+64720,Adrian
+64722,Amoret
+64723,Amsterdam
+64724,Appleton City
+64725,Archie
+64726,Blairstown
+64728,Bronaugh
+64730,Butler
+64733,Chilhowee
+64734,Cleveland
+64735,Clinton
+64738,Collins
+64739,Creighton
+64740,Deepwater
+64741,Deerfield
+64742,Drexel
+64743,East Lynne
+64744,El Dorado Springs
+64745,Foster
+64746,Freeman
+64747,Garden City
+64748,Golden City
+64750,Harwood
+64751,Horton
+64752,Hume
+64755,Jasper
+64756,Jerico Springs
+64759,Lamar
+64761,Leeton
+64762,Liberal
+64763,Lowry City
+64765,Metz
+64766,Milford
+64767,Milo
+64769,Mindenmines
+64770,Montrose
+64771,Moundville
+64772,Nevada
+64776,Osceola
+64777,Passaic
+64778,Richards
+64779,Rich Hill
+64780,Rockville
+64781,Roscoe
+64783,Schell City
+64784,Sheldon
+64788,Urich
+64789,Vista
+64790,Walker
+64801,Joplin
+64802,Joplin
+64803,Joplin
+64804,Joplin
+64830,Alba
+64831,Anderson
+64832,Asbury
+64833,Avilla
+64834,Carl Junction
+64835,Carterville
+64836,Carthage
+64840,Diamond
+64841,Duenweg
+64842,Fairview
+64843,Goodman
+64844,Granby
+64847,Lanagan
+64848,La Russell
+64849,Neck City
+64850,Neosho
+64853,Newtonia
+64854,Noel
+64855,Oronogo
+64856,Pineville
+64857,Purcell
+64858,Racine
+64859,Reeds
+64861,Rocky Comfort
+64862,Sarcoxie
+64863,South West City
+64864,Saginaw
+64865,Seneca
+64866,Stark City
+64867,Stella
+64868,Tiff City
+64869,Waco
+64870,Webb City
+64873,Wentworth
+64874,Wheaton
+64944,Kansas City
+64999,Kansas City
+65001,Argyle
+65010,Ashland
+65011,Barnett
+65013,Belle
+65014,Bland
+65016,Bonnots Mill
+65017,Brumley
+65018,California
+65020,Camdenton
+65022,Cedar City
+65023,Centertown
+65024,Chamois
+65025,Clarksburg
+65026,Eldon
+65031,Etterville
+65032,Eugene
+65034,Fortuna
+65035,Freeburg
+65036,Gasconade
+65037,Gravois Mills
+65038,Laurie
+65039,Hartsburg
+65040,Henley
+65041,Hermann
+65042,High Point
+65043,Holts Summit
+65046,Jamestown
+65047,Kaiser
+65048,Koeltztown
+65049,Lake Ozark
+65050,Latham
+65051,Linn
+65052,Linn Creek
+65053,Lohman
+65054,Loose Creek
+65055,Mc Girk
+65058,Meta
+65059,Mokane
+65061,Morrison
+65062,Mount Sterling
+65063,New Bloomfield
+65064,Olean
+65065,Osage Beach
+65066,Owensville
+65067,Portland
+65068,Prairie Home
+65069,Rhineland
+65072,Rocky Mount
+65074,Russellville
+65075,Saint Elizabeth
+65076,Saint Thomas
+65077,Steedman
+65078,Stover
+65079,Sunrise Beach
+65080,Tebbetts
+65081,Tipton
+65082,Tuscumbia
+65083,Ulman
+65084,Versailles
+65085,Westphalia
+65101,Jefferson City
+65102,Jefferson City
+65103,Jefferson City
+65104,Jefferson City
+65105,Jefferson City
+65106,Jefferson City
+65107,Jefferson City
+65108,Jefferson City
+65109,Jefferson City
+65110,Jefferson City
+65111,Jefferson City
+65201,Columbia
+65202,Columbia
+65203,Columbia
+65205,Columbia
+65211,Columbia
+65212,Columbia
+65215,Columbia
+65216,Columbia
+65217,Columbia
+65218,Columbia
+65230,Armstrong
+65231,Auxvasse
+65232,Benton City
+65233,Boonville
+65236,Brunswick
+65237,Bunceton
+65239,Cairo
+65240,Centralia
+65243,Clark
+65244,Clifton Hill
+65246,Dalton
+65247,Excello
+65248,Fayette
+65250,Franklin
+65251,Fulton
+65254,Glasgow
+65255,Hallsville
+65256,Harrisburg
+65257,Higbee
+65258,Holliday
+65259,Huntsville
+65260,Jacksonville
+65261,Keytesville
+65262,Kingdom City
+65263,Madison
+65264,Martinsburg
+65265,Mexico
+65270,Moberly
+65274,New Franklin
+65275,Paris
+65276,Pilot Grove
+65278,Renick
+65279,Rocheport
+65280,Rush Hill
+65281,Salisbury
+65282,Santa FE
+65283,Stoutsville
+65284,Sturgeon
+65285,Thompson
+65286,Triplett
+65287,Wooldridge
+65299,Mid Missouri
+65301,Sedalia
+65302,Sedalia
+65305,Whiteman Air Force Base
+65320,Arrow Rock
+65321,Blackburn
+65322,Blackwater
+65323,Calhoun
+65324,Climax Springs
+65325,Cole Camp
+65326,Edwards
+65327,Emma
+65329,Florence
+65330,Gilliam
+65332,Green Ridge
+65333,Houstonia
+65334,Hughesville
+65335,Ionia
+65336,Knob Noster
+65337,La Monte
+65338,Lincoln
+65339,Malta Bend
+65340,Marshall
+65344,Miami
+65345,Mora
+65347,Nelson
+65348,Otterville
+65349,Slater
+65350,Smithton
+65351,Sweet Springs
+65354,Syracuse
+65355,Warsaw
+65360,Windsor
+65401,Rolla
+65402,Rolla
+65409,Rolla
+65433,Bendavis
+65436,Beulah
+65438,Birch Tree
+65439,Bixby
+65440,Boss
+65441,Bourbon
+65443,Brinktown
+65444,Bucyrus
+65446,Cherryville
+65449,Cook Station
+65452,Crocker
+65453,Cuba
+65456,Davisville
+65457,Devils Elbow
+65459,Dixon
+65461,Duke
+65462,Edgar Springs
+65463,Eldridge
+65464,Elk Creek
+65466,Eminence
+65468,Eunice
+65470,Falcon
+65473,Fort Leonard Wood
+65479,Hartshorn
+65483,Houston
+65484,Huggins
+65486,Iberia
+65501,Jadwin
+65529,Jerome
+65532,Lake Spring
+65534,Laquey
+65535,Leasburg
+65536,Lebanon
+65540,Lecoma
+65541,Lenox
+65542,Licking
+65543,Lynchburg
+65546,Montier
+65548,Mountain View
+65550,Newburg
+65552,Plato
+65555,Raymondville
+65556,Richland
+65557,Roby
+65559,Saint James
+65560,Salem
+65564,Solo
+65565,Steelville
+65566,Viburnum
+65567,Stoutland
+65570,Success
+65571,Summersville
+65572,Swedeborg
+65573,Teresita
+65580,Vichy
+65582,Vienna
+65583,Waynesville
+65586,Wesco
+65588,Winona
+65589,Yukon
+65590,Long Lane
+65591,Montreal
+65601,Aldrich
+65603,Arcola
+65604,Ash Grove
+65605,Aurora
+65606,Alton
+65607,Caplinger Mills
+65608,Ava
+65609,Bakersfield
+65610,Billings
+65611,Blue Eye
+65612,Bois D Arc
+65613,Bolivar
+65614,Bradleyville
+65615,Branson
+65616,Branson
+65617,Brighton
+65618,Brixey
+65619,Brookline Station
+65620,Bruner
+65622,Buffalo
+65623,Butterfield
+65624,Cape Fair
+65625,Cassville
+65626,Caulfield
+65627,Cedarcreek
+65629,Chadwick
+65630,Chestnutridge
+65631,Clever
+65632,Conway
+65633,Crane
+65634,Cross Timbers
+65635,Dadeville
+65636,Diggins
+65637,Dora
+65638,Drury
+65640,Dunnegan
+65641,Eagle Rock
+65644,Elkland
+65645,Eudora
+65646,Everton
+65647,Exeter
+65648,Fair Grove
+65649,Fair Play
+65650,Flemington
+65652,Fordland
+65653,Forsyth
+65654,Freistatt
+65655,Gainesville
+65656,Galena
+65657,Garrison
+65658,Golden
+65659,Goodson
+65660,Graff
+65661,Greenfield
+65662,Grovespring
+65663,Half Way
+65664,Halltown
+65666,Hardenville
+65667,Hartville
+65668,Hermitage
+65669,Highlandville
+65672,Hollister
+65673,Hollister
+65674,Humansville
+65675,Hurley
+65676,Isabella
+65679,Kirbyville
+65680,Kissee Mills
+65681,Lampe
+65682,Lockwood
+65685,Louisburg
+65686,Kimberling City
+65688,Brandsville
+65689,Cabool
+65690,Couch
+65692,Koshkonong
+65701,Mc Clurg
+65702,Macomb
+65704,Mansfield
+65705,Marionville
+65706,Marshfield
+65707,Miller
+65708,Monett
+65710,Morrisville
+65711,Mountain Grove
+65712,Mount Vernon
+65713,Niangua
+65714,Nixa
+65715,Noble
+65717,Norwood
+65720,Oldfield
+65721,Ozark
+65722,Phillipsburg
+65723,Pierce City
+65724,Pittsburg
+65725,Pleasant Hope
+65726,Point Lookout
+65727,Polk
+65728,Ponce de Leon
+65729,Pontiac
+65730,Powell
+65731,Powersite
+65732,Preston
+65733,Protem
+65734,Purdy
+65735,Quincy
+65737,Reeds Spring
+65738,Republic
+65739,Ridgedale
+65740,Rockaway Beach
+65741,Rockbridge
+65742,Rogersville
+65744,Rueter
+65745,Seligman
+65746,Seymour
+65747,Shell Knob
+65752,South Greenfield
+65753,Sparta
+65754,Spokane
+65755,Squires
+65756,Stotts City
+65757,Strafford
+65759,Taneyville
+65760,Tecumseh
+65761,Theodosia
+65762,Thornfield
+65764,Tunas
+65765,Turners
+65766,Udall
+65767,Urbana
+65768,Vanzant
+65769,Verona
+65770,Walnut Grove
+65771,Walnut Shade
+65772,Washburn
+65773,Wasola
+65774,Weaubleau
+65775,West Plains
+65776,South Fork
+65777,Moody
+65778,Myrtle
+65779,Wheatland
+65781,Willard
+65783,Windyville
+65784,Zanoni
+65785,Stockton
+65786,Macks Creek
+65787,Roach
+65788,Peace Valley
+65789,Pomona
+65790,Pottersville
+65791,Thayer
+65793,Willow Springs
+65801,Springfield
+65802,Springfield
+65803,Springfield
+65804,Springfield
+65805,Springfield
+65806,Springfield
+65807,Springfield
+65808,Springfield
+65809,Springfield
+65810,Springfield
+65814,Springfield
+65817,Springfield
+65890,Springfield
+65898,Springfield
+65899,Springfield
+55001,Afton
+55002,Almelund
+55003,Bayport
+55005,Bethel
+55006,Braham
+55007,Brook Park
+55008,Cambridge
+55009,Cannon Falls
+55010,Castle Rock
+55011,Cedar
+55012,Center City
+55013,Chisago City
+55014,Circle Pines
+55016,Cottage Grove
+55017,Dalbo
+55018,Dennison
+55019,Dundas
+55020,Elko
+55021,Faribault
+55024,Farmington
+55025,Forest Lake
+55026,Frontenac
+55027,Goodhue
+55029,Grandy
+55030,Grasston
+55031,Hampton
+55032,Harris
+55033,Hastings
+55036,Henriette
+55037,Hinckley
+55038,Hugo
+55040,Isanti
+55041,Lake City
+55042,Lake Elmo
+55043,Lakeland
+55044,Lakeville
+55045,Lindstrom
+55046,Lonsdale
+55047,Marine on Saint Croix
+55049,Medford
+55051,Mora
+55052,Morristown
+55053,Nerstrand
+55054,New Market
+55055,Newport
+55056,North Branch
+55057,Northfield
+55060,Owatonna
+55063,Pine City
+55065,Randolph
+55066,Red Wing
+55067,Rock Creek
+55068,Rosemount
+55069,Rush City
+55070,Saint Francis
+55071,Saint Paul Park
+55072,Sandstone
+55073,Scandia
+55074,Shafer
+55075,South Saint Paul
+55076,Inver Grove Heights
+55077,Inver Grove Heights
+55078,Stacy
+55079,Stacy
+55080,Stanchfield
+55082,Stillwater
+55083,Stillwater
+55084,Taylors Falls
+55085,Vermillion
+55087,Warsaw
+55088,Webster
+55089,Welch
+55090,Willernie
+55092,Wyoming
+55101,Saint Paul
+55102,Saint Paul
+55103,Saint Paul
+55104,Saint Paul
+55105,Saint Paul
+55106,Saint Paul
+55107,Saint Paul
+55108,Saint Paul
+55109,Saint Paul
+55110,Saint Paul
+55111,Saint Paul
+55112,Saint Paul
+55113,Saint Paul
+55114,Saint Paul
+55115,Saint Paul
+55116,Saint Paul
+55117,Saint Paul
+55118,Saint Paul
+55119,Saint Paul
+55120,Saint Paul
+55121,Saint Paul
+55122,Saint Paul
+55123,Saint Paul
+55124,Saint Paul
+55125,Saint Paul
+55126,Saint Paul
+55127,Saint Paul
+55128,Saint Paul
+55129,Saint Paul
+55133,Saint Paul
+55144,Saint Paul
+55145,Saint Paul
+55146,Saint Paul
+55150,Mendota
+55155,Saint Paul
+55161,Saint Paul
+55164,Saint Paul
+55165,Saint Paul
+55166,Saint Paul
+55168,Saint Paul
+55169,Saint Paul
+55170,Saint Paul
+55171,Saint Paul
+55172,Saint Paul
+55175,Saint Paul
+55177,Saint Paul
+55182,Saint Paul
+55187,Saint Paul
+55188,Saint Paul
+55189,Saint Paul
+55190,Saint Paul
+55191,Saint Paul
+55301,Albertville
+55302,Annandale
+55303,Anoka
+55304,Andover
+55305,Hopkins
+55306,Burnsville
+55307,Arlington
+55308,Becker
+55309,Big Lake
+55310,Bird Island
+55311,Osseo
+55312,Brownton
+55313,Buffalo
+55314,Buffalo Lake
+55315,Carver
+55316,Champlin
+55317,Chanhassen
+55318,Chaska
+55319,Clear Lake
+55320,Clearwater
+55321,Cokato
+55322,Cologne
+55323,Crystal Bay
+55324,Darwin
+55325,Dassel
+55327,Dayton
+55328,Delano
+55329,Eden Valley
+55330,Elk River
+55331,Excelsior
+55332,Fairfax
+55333,Franklin
+55334,Gaylord
+55335,Gibbon
+55336,Glencoe
+55337,Burnsville
+55338,Green Isle
+55339,Hamburg
+55340,Hamel
+55341,Hanover
+55342,Hector
+55343,Hopkins
+55344,Eden Prairie
+55345,Minnetonka
+55346,Eden Prairie
+55347,Eden Prairie
+55348,Maple Plain
+55349,Howard Lake
+55350,Hutchinson
+55352,Jordan
+55353,Kimball
+55354,Lester Prairie
+55355,Litchfield
+55356,Long Lake
+55357,Loretto
+55358,Maple Lake
+55359,Maple Plain
+55360,Mayer
+55361,Minnetonka Beach
+55362,Monticello
+55363,Montrose
+55364,Mound
+55365,Monticello
+55366,New Auburn
+55367,New Germany
+55368,Norwood
+55369,Osseo
+55370,Plato
+55371,Princeton
+55372,Prior Lake
+55373,Rockford
+55374,Rogers
+55375,Saint Bonifacius
+55376,Saint Michael
+55377,Santiago
+55378,Savage
+55379,Shakopee
+55380,Silver Creek
+55381,Silver Lake
+55382,South Haven
+55383,Norwood
+55384,Spring Park
+55385,Stewart
+55386,Victoria
+55387,Waconia
+55388,Watertown
+55389,Watkins
+55390,Waverly
+55391,Wayzata
+55392,Navarre
+55393,Maple Plain
+55394,Young America
+55395,Winsted
+55396,Winthrop
+55397,Young America
+55398,Zimmerman
+55399,Young America
+55401,Minneapolis
+55402,Minneapolis
+55403,Minneapolis
+55404,Minneapolis
+55405,Minneapolis
+55406,Minneapolis
+55407,Minneapolis
+55408,Minneapolis
+55409,Minneapolis
+55410,Minneapolis
+55411,Minneapolis
+55412,Minneapolis
+55413,Minneapolis
+55414,Minneapolis
+55415,Minneapolis
+55416,Minneapolis
+55417,Minneapolis
+55418,Minneapolis
+55419,Minneapolis
+55420,Minneapolis
+55421,Minneapolis
+55422,Minneapolis
+55423,Minneapolis
+55424,Minneapolis
+55425,Minneapolis
+55426,Minneapolis
+55427,Minneapolis
+55428,Minneapolis
+55429,Minneapolis
+55430,Minneapolis
+55431,Minneapolis
+55432,Minneapolis
+55433,Minneapolis
+55434,Minneapolis
+55435,Minneapolis
+55436,Minneapolis
+55437,Minneapolis
+55438,Minneapolis
+55439,Minneapolis
+55440,Minneapolis
+55441,Minneapolis
+55442,Minneapolis
+55443,Minneapolis
+55444,Minneapolis
+55445,Minneapolis
+55446,Minneapolis
+55447,Minneapolis
+55448,Minneapolis
+55449,Minneapolis
+55450,Minneapolis
+55454,Minneapolis
+55455,Minneapolis
+55458,Minneapolis
+55459,Minneapolis
+55460,Minneapolis
+55468,Minneapolis
+55470,Minneapolis
+55472,Minneapolis
+55473,Minneapolis
+55474,Minneapolis
+55478,Minneapolis
+55479,Minneapolis
+55480,Minneapolis
+55483,Minneapolis
+55484,Minneapolis
+55485,Minneapolis
+55486,Minneapolis
+55487,Minneapolis
+55488,Minneapolis
+55550,Young America
+55551,Young America
+55552,Young America
+55553,Young America
+55554,Norwood
+55555,Young America
+55556,Young America
+55557,Young America
+55558,Young America
+55559,Young America
+55560,Young America
+55561,Monticello
+55562,Young America
+55563,Monticello
+55564,Young America
+55565,Monticello
+55566,Young America
+55567,Young America
+55568,Young America
+55569,Osseo
+55570,Maple Plain
+55571,Maple Plain
+55572,Maple Plain
+55573,Young America
+55574,Maple Plain
+55575,Howard Lake
+55576,Maple Plain
+55577,Maple Plain
+55578,Maple Plain
+55579,Maple Plain
+55580,Monticello
+55581,Monticello
+55582,Monticello
+55583,Norwood
+55584,Monticello
+55585,Monticello
+55586,Monticello
+55587,Monticello
+55588,Monticello
+55589,Monticello
+55590,Monticello
+55591,Monticello
+55592,Maple Plain
+55593,Maple Plain
+55594,Young America
+55595,Loretto
+55596,Loretto
+55597,Loretto
+55598,Loretto
+55599,Loretto
+55601,Beaver Bay
+55602,Brimson
+55603,Finland
+55604,Grand Marais
+55605,Grand Portage
+55606,Hovland
+55607,Isabella
+55609,Knife River
+55612,Lutsen
+55613,Schroeder
+55614,Silver Bay
+55615,Tofte
+55616,Two Harbors
+55701,Adolph
+55702,Alborn
+55703,Angora
+55704,Askov
+55705,Aurora
+55706,Babbitt
+55707,Barnum
+55708,Biwabik
+55709,Bovey
+55710,Britt
+55711,Brookston
+55712,Bruno
+55713,Buhl
+55716,Calumet
+55717,Canyon
+55718,Carlton
+55719,Chisholm
+55720,Cloquet
+55721,Cohasset
+55722,Coleraine
+55723,Cook
+55724,Cotton
+55725,Crane Lake
+55726,Cromwell
+55730,Grand Rapids
+55731,Ely
+55732,Embarrass
+55733,Esko
+55734,Eveleth
+55735,Finlayson
+55736,Floodwood
+55738,Forbes
+55741,Gilbert
+55742,Goodland
+55744,Grand Rapids
+55745,Grand Rapids
+55746,Hibbing
+55747,Hibbing
+55748,Hill City
+55749,Holyoke
+55750,Hoyt Lakes
+55751,Iron
+55752,Jacobson
+55753,Keewatin
+55756,Kerrick
+55757,Kettle River
+55758,Kinney
+55760,McGregor
+55763,Makinen
+55764,Marble
+55765,Meadowlands
+55766,Melrude
+55767,Moose Lake
+55768,Mountain Iron
+55769,Nashwauk
+55771,Orr
+55772,Nett Lake
+55775,Pengilly
+55777,Virginia
+55779,Saginaw
+55780,Sawyer
+55781,Side Lake
+55782,Soudan
+55783,Sturgeon Lake
+55784,Swan River
+55785,Swatara
+55786,Taconite
+55787,Tamarack
+55790,Tower
+55791,Twig
+55792,Virginia
+55793,Warba
+55795,Willow River
+55796,Winton
+55797,Wrenshall
+55798,Wright
+55801,Duluth
+55802,Duluth
+55803,Duluth
+55804,Duluth
+55805,Duluth
+55806,Duluth
+55807,Duluth
+55808,Duluth
+55810,Duluth
+55811,Duluth
+55812,Duluth
+55814,Duluth
+55815,Duluth
+55816,Duluth
+55901,Rochester
+55902,Rochester
+55903,Rochester
+55904,Rochester
+55905,Rochester
+55906,Rochester
+55909,Adams
+55910,Altura
+55912,Austin
+55917,Blooming Prairie
+55918,Brownsdale
+55919,Brownsville
+55920,Byron
+55921,Caledonia
+55922,Canton
+55923,Chatfield
+55924,Claremont
+55925,Dakota
+55926,Dexter
+55927,Dodge Center
+55929,Dover
+55931,Eitzen
+55932,Elgin
+55933,Elkton
+55934,Eyota
+55935,Fountain
+55936,Grand Meadow
+55939,Harmony
+55940,Hayfield
+55941,Hokah
+55942,Homer
+55943,Houston
+55944,Kasson
+55945,Kellogg
+55946,Kenyon
+55947,La Crescent
+55949,Lanesboro
+55950,Lansing
+55951,Le Roy
+55952,Lewiston
+55953,Lyle
+55954,Mabel
+55955,Mantorville
+55956,Mazeppa
+55957,Millville
+55959,Minnesota City
+55960,Oronoco
+55961,Ostrander
+55962,Peterson
+55963,Pine Island
+55964,Plainview
+55965,Preston
+55967,Racine
+55968,Reads Landing
+55969,Rollingstone
+55970,Rose Creek
+55971,Rushford
+55972,Saint Charles
+55973,Sargeant
+55974,Spring Grove
+55975,Spring Valley
+55976,Stewartville
+55977,Taopi
+55979,Utica
+55981,Wabasha
+55982,Waltham
+55983,Wanamingo
+55985,West Concord
+55987,Winona
+55988,Stockton
+55990,Wykoff
+55991,Zumbro Falls
+55992,Zumbrota
+56001,Mankato
+56002,Mankato
+56003,Mankato
+56006,Mankato
+56007,Albert Lea
+56009,Alden
+56010,Amboy
+56011,Belle Plaine
+56013,Blue Earth
+56014,Bricelyn
+56016,Clarks Grove
+56017,Cleveland
+56019,Comfrey
+56020,Conger
+56021,Courtland
+56022,Darfur
+56023,Delavan
+56024,Eagle Lake
+56025,Easton
+56026,Ellendale
+56027,Elmore
+56028,Elysian
+56029,Emmons
+56030,Essig
+56031,Fairmont
+56032,Freeborn
+56033,Frost
+56034,Garden City
+56035,Geneva
+56036,Glenville
+56037,Good Thunder
+56039,Granada
+56041,Hanska
+56042,Hartland
+56043,Hayward
+56044,Henderson
+56045,Hollandale
+56046,Hope
+56047,Huntley
+56048,Janesville
+56050,Kasota
+56051,Kiester
+56052,Kilkenny
+56054,Lafayette
+56055,Lake Crystal
+56056,La Salle
+56057,Le Center
+56058,Le Sueur
+56060,Lewisville
+56062,Madelia
+56063,Madison Lake
+56064,Manchester
+56065,Mapleton
+56068,Minnesota Lake
+56069,Montgomery
+56071,New Prague
+56072,New Richland
+56073,New Ulm
+56074,Nicollet
+56075,Northrop
+56076,Oakland
+56078,Pemberton
+56080,Saint Clair
+56081,Saint James
+56082,Saint Peter
+56083,Sanborn
+56084,Searles
+56085,Sleepy Eye
+56087,Springfield
+56088,Truman
+56089,Twin Lakes
+56090,Vernon Center
+56091,Waldorf
+56093,Waseca
+56096,Waterville
+56097,Wells
+56098,Winnebago
+56101,Windom
+56110,Adrian
+56111,Alpha
+56113,Arco
+56114,Avoca
+56115,Balaton
+56116,Beaver Creek
+56117,Bigelow
+56118,Bingham Lake
+56119,Brewster
+56120,Butterfield
+56121,Ceylon
+56122,Chandler
+56123,Currie
+56125,Dovray
+56127,Dunnell
+56128,Edgerton
+56129,Ellsworth
+56131,Fulda
+56132,Garvin
+56134,Hardwick
+56136,Hendricks
+56137,Heron Lake
+56138,Hills
+56139,Holland
+56140,Ihlen
+56141,Iona
+56142,Ivanhoe
+56143,Jackson
+56144,Jasper
+56145,Jeffers
+56146,Kanaranzi
+56147,Kenneth
+56149,Lake Benton
+56150,Lakefield
+56151,Lake Wilson
+56152,Lamberton
+56153,Leota
+56155,Lismore
+56156,Luverne
+56157,Lynd
+56158,Magnolia
+56159,Mountain Lake
+56160,Odin
+56161,Okabena
+56162,Ormsby
+56164,Pipestone
+56165,Reading
+56166,Revere
+56167,Round Lake
+56168,Rushmore
+56169,Russell
+56170,Ruthton
+56171,Sherburn
+56172,Slayton
+56173,Steen
+56174,Storden
+56175,Tracy
+56176,Trimont
+56177,Trosky
+56178,Tyler
+56180,Walnut Grove
+56181,Welcome
+56183,Westbrook
+56185,Wilmont
+56186,Woodstock
+56187,Worthington
+56201,Willmar
+56207,Alberta
+56208,Appleton
+56209,Atwater
+56210,Barry
+56211,Beardsley
+56212,Bellingham
+56214,Belview
+56215,Benson
+56216,Blomkest
+56218,Boyd
+56219,Browns Valley
+56220,Canby
+56221,Chokio
+56222,Clara City
+56223,Clarkfield
+56224,Clements
+56225,Clinton
+56226,Clontarf
+56227,Correll
+56228,Cosmos
+56229,Cottonwood
+56230,Danube
+56231,Danvers
+56232,Dawson
+56235,Donnelly
+56236,Dumont
+56237,Echo
+56239,Ghent
+56240,Graceville
+56241,Granite Falls
+56243,Grove City
+56244,Hancock
+56245,Hanley Falls
+56246,Hawick
+56248,Herman
+56249,Holloway
+56251,Kandiyohi
+56252,Kerkhoven
+56253,Lake Lillian
+56255,Lucan
+56256,Madison
+56257,Marietta
+56258,Marshall
+56260,Maynard
+56262,Milan
+56263,Milroy
+56264,Minneota
+56265,Montevideo
+56266,Morgan
+56267,Morris
+56270,Morton
+56271,Murdock
+56273,New London
+56274,Norcross
+56276,Odessa
+56277,Olivia
+56278,Ortonville
+56279,Pennock
+56280,Porter
+56281,Prinsburg
+56282,Raymond
+56283,Redwood Falls
+56284,Renville
+56285,Sacred Heart
+56287,Seaforth
+56288,Spicer
+56289,Sunburg
+56291,Taunton
+56292,Vesta
+56293,Wabasso
+56294,Wanda
+56295,Watson
+56296,Wheaton
+56297,Wood Lake
+56301,Saint Cloud
+56302,Saint Cloud
+56303,Saint Cloud
+56304,Saint Cloud
+56307,Albany
+56308,Alexandria
+56309,Ashby
+56310,Avon
+56311,Barrett
+56312,Belgrade
+56313,Bock
+56314,Bowlus
+56315,Brandon
+56316,Brooten
+56317,Buckman
+56318,Burtrum
+56319,Carlos
+56320,Cold Spring
+56321,Collegeville
+56323,Cyrus
+56324,Dalton
+56325,Elrosa
+56326,Evansville
+56327,Farwell
+56328,Flensburg
+56329,Foley
+56330,Foreston
+56331,Freeport
+56332,Garfield
+56333,Gilman
+56334,Glenwood
+56335,Greenwald
+56336,Grey Eagle
+56338,Hillman
+56339,Hoffman
+56340,Holdingford
+56341,Holmes City
+56342,Isle
+56343,Kensington
+56344,Lastrup
+56345,Little Falls
+56347,Long Prairie
+56349,Lowry
+56350,Mc Grath
+56352,Melrose
+56353,Milaca
+56354,Miltona
+56355,Nelson
+56356,New Munich
+56357,Oak Park
+56358,Ogilvie
+56359,Onamia
+56360,Osakis
+56361,Parkers Prairie
+56362,Paynesville
+56363,Pease
+56364,Pierz
+56367,Rice
+56368,Richmond
+56369,Rockville
+56371,Roscoe
+56372,Saint Cloud
+56373,Royalton
+56374,Saint Joseph
+56375,Saint Stephen
+56376,Saint Martin
+56377,Sartell
+56378,Sauk Centre
+56379,Sauk Rapids
+56381,Starbuck
+56382,Swanville
+56384,Upsala
+56385,Villard
+56386,Wahkon
+56387,Waite Park
+56389,West Union
+56393,Saint Cloud
+56395,Saint Cloud
+56396,Saint Cloud
+56397,Saint Cloud
+56398,Saint Cloud
+56399,Saint Cloud
+56401,Brainerd
+56425,Baxter
+56430,Ah Gwah Ching
+56431,Aitkin
+56433,Akeley
+56434,Aldrich
+56435,Backus
+56436,Benedict
+56437,Bertha
+56438,Browerville
+56440,Clarissa
+56441,Crosby
+56442,Crosslake
+56443,Cushing
+56444,Deerwood
+56446,Eagle Bend
+56447,Emily
+56448,Fifty Lakes
+56449,Fort Ripley
+56450,Garrison
+56452,Hackensack
+56453,Hewitt
+56455,Ironton
+56456,Jenkins
+56458,Lake George
+56459,Lake Hubert
+56461,Laporte
+56464,Menahga
+56465,Merrifield
+56466,Motley
+56467,Nevis
+56468,Nisswa
+56469,Palisade
+56470,Park Rapids
+56472,Pequot Lakes
+56473,Pillager
+56474,Pine River
+56475,Randall
+56477,Sebeka
+56478,Nimrod
+56479,Staples
+56481,Verndale
+56482,Wadena
+56484,Walker
+56501,Detroit Lakes
+56502,Detroit Lakes
+56510,Ada
+56511,Audubon
+56513,Baker
+56514,Barnesville
+56515,Battle Lake
+56516,Bejou
+56517,Beltrami
+56518,Bluffton
+56519,Borup
+56520,Breckenridge
+56521,Callaway
+56522,Campbell
+56523,Climax
+56524,Clitherall
+56525,Comstock
+56527,Deer Creek
+56528,Dent
+56529,Dilworth
+56531,Elbow Lake
+56533,Elizabeth
+56534,Erhard
+56535,Erskine
+56536,Felton
+56537,Fergus Falls
+56538,Fergus Falls
+56540,Fertile
+56541,Flom
+56542,Fosston
+56543,Foxhome
+56544,Frazee
+56545,Gary
+56546,Georgetown
+56547,Glyndon
+56548,Halstad
+56549,Hawley
+56550,Hendrum
+56551,Henning
+56552,Hitterdal
+56553,Kent
+56554,Lake Park
+56556,McIntosh
+56557,Mahnomen
+56560,Moorhead
+56561,Moorhead
+56562,Moorhead
+56563,Moorhead
+56565,Nashua
+56566,Naytahwaush
+56567,New York Mills
+56568,Nielsville
+56569,Ogema
+56570,Osage
+56571,Ottertail
+56572,Pelican Rapids
+56573,Perham
+56574,Perley
+56575,Ponsford
+56576,Richville
+56577,Richwood
+56578,Rochert
+56579,Rothsay
+56580,Sabin
+56581,Shelly
+56583,Tintah
+56584,Twin Valley
+56585,Ulen
+56586,Underwood
+56587,Vergas
+56588,Vining
+56589,Waubun
+56590,Wendell
+56591,White Earth
+56592,Winger
+56593,Wolf Lake
+56594,Wolverton
+56601,Bemidji
+56619,Bemidji
+56621,Bagley
+56623,Baudette
+56626,Bena
+56627,Big Falls
+56628,Bigfork
+56629,Birchdale
+56630,Blackduck
+56631,Bowstring
+56633,Cass Lake
+56634,Clearbrook
+56636,Deer River
+56637,Talmoon
+56639,Effie
+56641,Federal Dam
+56644,Gonvick
+56646,Gully
+56647,Hines
+56649,International Falls
+56650,Kelliher
+56651,Lengby
+56652,Leonard
+56653,Littlefork
+56654,Loman
+56655,Longville
+56657,Marcell
+56658,Margie
+56659,Max
+56660,Mizpah
+56661,Northome
+56662,Outing
+56663,Pennington
+56666,Ponemah
+56667,Puposky
+56668,Ranier
+56669,Ray
+56670,Redby
+56671,Redlake
+56672,Remer
+56673,Roosevelt
+56676,Shevlin
+56678,Solway
+56679,South International Falls
+56680,Spring Lake
+56681,Squaw Lake
+56682,Swift
+56683,Tenstrike
+56684,Trail
+56685,Waskish
+56686,Williams
+56687,Wilton
+56688,Wirt
+56701,Thief River Falls
+56710,Alvarado
+56711,Angle Inlet
+56712,Angus
+56713,Argyle
+56714,Badger
+56715,Brooks
+56716,Crookston
+56720,Donaldson
+56721,East Grand Forks
+56722,Euclid
+56723,Fisher
+56724,Gatzke
+56725,Goodridge
+56726,Greenbush
+56727,Grygla
+56728,Hallock
+56729,Halma
+56731,Humboldt
+56732,Karlstad
+56733,Kennedy
+56734,Lake Bronson
+56735,Lancaster
+56736,Mentor
+56737,Middle River
+56738,Newfolden
+56740,Noyes
+56741,Oak Island
+56742,Oklee
+56744,Oslo
+56748,Plummer
+56750,Red Lake Falls
+56751,Roseau
+56754,Saint Hilaire
+56755,Saint Vincent
+56756,Salol
+56757,Stephen
+56758,Strandquist
+56759,Strathcona
+56760,Viking
+56761,Wannaska
+56762,Warren
+56763,Warroad
+48001,Algonac
+48002,Allenton
+48003,Almont
+48004,Anchorville
+48005,Armada
+48006,Avoca
+48007,Troy
+48009,Birmingham
+48012,Birmingham
+48014,Capac
+48015,Center Line
+48017,Clawson
+48021,Eastpointe
+48022,Emmett
+48023,Fair Haven
+48025,Franklin
+48026,Fraser
+48027,Goodells
+48028,Harsens Island
+48030,Hazel Park
+48032,Jeddo
+48034,Southfield
+48035,Clinton Township
+48036,Clinton Township
+48037,Southfield
+48038,Clinton Township
+48039,Marine City
+48040,Marysville
+48041,Memphis
+48042,Macomb
+48043,Mount Clemens
+48044,Macomb
+48045,Harrison Township
+48046,Mount Clemens
+48047,New Baltimore
+48048,New Haven
+48049,North Street
+48050,New Haven
+48051,New Baltimore
+48054,East China
+48059,Fort Gratiot
+48060,Port Huron
+48061,Port Huron
+48062,Richmond
+48063,Columbus
+48064,Casco
+48065,Romeo
+48066,Roseville
+48067,Royal Oak
+48068,Royal Oak
+48069,Pleasant Ridge
+48070,Huntington Woods
+48071,Madison Heights
+48072,Berkley
+48073,Royal Oak
+48074,Smiths Creek
+48075,Southfield
+48076,Southfield
+48079,Saint Clair
+48080,Saint Clair Shores
+48081,Saint Clair Shores
+48082,Saint Clair Shores
+48083,Troy
+48084,Troy
+48086,Southfield
+48089,Warren
+48090,Warren
+48091,Warren
+48092,Warren
+48093,Warren
+48094,Washington
+48095,Washington
+48096,Ray
+48097,Yale
+48098,Troy
+48099,Troy
+48101,Allen Park
+48103,Ann Arbor
+48104,Ann Arbor
+48105,Ann Arbor
+48106,Ann Arbor
+48107,Ann Arbor
+48108,Ann Arbor
+48109,Ann Arbor
+48110,Azalia
+48111,Belleville
+48112,Belleville
+48113,Ann Arbor
+48114,Brighton
+48115,Bridgewater
+48116,Brighton
+48117,Carleton
+48118,Chelsea
+48120,Dearborn
+48121,Dearborn
+48122,Melvindale
+48123,Dearborn
+48124,Dearborn
+48125,Dearborn Heights
+48126,Dearborn
+48127,Dearborn Heights
+48128,Dearborn
+48130,Dexter
+48131,Dundee
+48133,Erie
+48134,Flat Rock
+48135,Garden City
+48136,Garden City
+48137,Gregory
+48138,Grosse Ile
+48139,Hamburg
+48140,Ida
+48141,Inkster
+48143,Lakeland
+48144,Lambertville
+48145,La Salle
+48146,Lincoln Park
+48150,Livonia
+48151,Livonia
+48152,Livonia
+48153,Livonia
+48154,Livonia
+48157,Luna Pier
+48158,Manchester
+48159,Maybee
+48160,Milan
+48161,Monroe
+48162,Monroe
+48164,New Boston
+48165,New Hudson
+48166,Newport
+48167,Northville
+48169,Pinckney
+48170,Plymouth
+48173,Rockwood
+48174,Romulus
+48175,Salem
+48176,Saline
+48177,Samaria
+48178,South Lyon
+48179,South Rockwood
+48180,Taylor
+48182,Temperance
+48183,Trenton
+48184,Wayne
+48185,Westland
+48186,Westland
+48187,Canton
+48188,Canton
+48189,Whitmore Lake
+48190,Whittaker
+48191,Willis
+48192,Wyandotte
+48195,Southgate
+48197,Ypsilanti
+48198,Ypsilanti
+48201,Detroit
+48202,Detroit
+48203,Highland Park
+48204,Detroit
+48205,Detroit
+48206,Detroit
+48207,Detroit
+48208,Detroit
+48209,Detroit
+48210,Detroit
+48211,Detroit
+48212,Hamtramck
+48213,Detroit
+48214,Detroit
+48215,Detroit
+48216,Detroit
+48217,Detroit
+48218,River Rouge
+48219,Detroit
+48220,Ferndale
+48221,Detroit
+48222,Detroit
+48223,Detroit
+48224,Detroit
+48225,Harper Woods
+48226,Detroit
+48227,Detroit
+48228,Detroit
+48229,Ecorse
+48230,Grosse Pointe
+48231,Detroit
+48232,Detroit
+48233,Detroit
+48234,Detroit
+48235,Detroit
+48236,Grosse Pointe
+48237,Oak Park
+48238,Detroit
+48239,Redford
+48240,Redford
+48242,Detroit
+48243,Detroit
+48244,Detroit
+48254,Detroit
+48255,Detroit
+48258,Detroit
+48260,Detroit
+48264,Detroit
+48265,Detroit
+48266,Detroit
+48267,Detroit
+48268,Detroit
+48269,Detroit
+48272,Detroit
+48274,Detroit
+48275,Detroit
+48277,Detroit
+48278,Detroit
+48279,Detroit
+48288,Detroit
+48295,Detroit
+48297,Detroit
+48299,Detroit
+48301,Bloomfield Hills
+48302,Bloomfield Hills
+48303,Bloomfield Hills
+48304,Bloomfield Hills
+48306,Rochester
+48307,Rochester
+48308,Rochester
+48309,Rochester
+48310,Sterling Heights
+48311,Sterling Heights
+48312,Sterling Heights
+48313,Sterling Heights
+48314,Sterling Heights
+48315,Utica
+48316,Utica
+48317,Utica
+48318,Utica
+48320,Keego Harbor
+48321,Auburn Hills
+48322,West Bloomfield
+48323,West Bloomfield
+48324,West Bloomfield
+48325,West Bloomfield
+48326,Auburn Hills
+48327,Waterford
+48328,Waterford
+48329,Waterford
+48330,Drayton Plains
+48331,Farmington
+48332,Farmington
+48333,Farmington
+48334,Farmington
+48335,Farmington
+48336,Farmington
+48340,Pontiac
+48341,Pontiac
+48342,Pontiac
+48343,Pontiac
+48346,Clarkston
+48347,Clarkston
+48348,Clarkston
+48350,Davisburg
+48353,Hartland
+48356,Highland
+48357,Highland
+48359,Lake Orion
+48360,Lake Orion
+48361,Lake Orion
+48362,Lake Orion
+48363,Oakland
+48366,Lakeville
+48367,Leonard
+48370,Oxford
+48371,Oxford
+48374,Novi
+48375,Novi
+48376,Novi
+48377,Novi
+48380,Milford
+48381,Milford
+48382,Commerce Township
+48383,White Lake
+48386,White Lake
+48387,Union Lake
+48390,Walled Lake
+48391,Walled Lake
+48393,Wixom
+48397,Warren
+48398,Clawson
+48401,Applegate
+48410,Argyle
+48411,Atlas
+48412,Attica
+48413,Bad Axe
+48414,Bancroft
+48415,Birch Run
+48416,Brown City
+48417,Burt
+48418,Byron
+48419,Carsonville
+48420,Clio
+48421,Columbiaville
+48422,Croswell
+48423,Davison
+48426,Decker
+48427,Deckerville
+48428,Dryden
+48429,Durand
+48430,Fenton
+48432,Filion
+48433,Flushing
+48434,Forestville
+48435,Fostoria
+48436,Gaines
+48437,Genesee
+48438,Goodrich
+48439,Grand Blanc
+48440,Hadley
+48441,Harbor Beach
+48442,Holly
+48444,Imlay City
+48445,Kinde
+48446,Lapeer
+48449,Lennon
+48450,Lexington
+48451,Linden
+48453,Marlette
+48454,Melvin
+48455,Metamora
+48456,Minden City
+48457,Montrose
+48458,Mount Morris
+48460,New Lothrop
+48461,North Branch
+48462,Ortonville
+48463,Otisville
+48464,Otter Lake
+48465,Palms
+48466,Peck
+48467,Port Austin
+48468,Port Hope
+48469,Port Sanilac
+48470,Ruth
+48471,Sandusky
+48472,Snover
+48473,Swartz Creek
+48475,Ubly
+48476,Vernon
+48501,Flint
+48502,Flint
+48503,Flint
+48504,Flint
+48505,Flint
+48506,Flint
+48507,Flint
+48509,Burton
+48519,Burton
+48529,Burton
+48531,Flint
+48532,Flint
+48550,Flint
+48551,Flint
+48552,Flint
+48553,Flint
+48554,Flint
+48555,Flint
+48556,Flint
+48557,Flint
+48559,Flint
+48601,Saginaw
+48602,Saginaw
+48603,Saginaw
+48604,Saginaw
+48605,Saginaw
+48606,Saginaw
+48607,Saginaw
+48608,Saginaw
+48609,Saginaw
+48610,Alger
+48611,Auburn
+48612,Beaverton
+48613,Bentley
+48614,Brant
+48615,Breckenridge
+48616,Chesaning
+48617,Clare
+48618,Coleman
+48619,Comins
+48620,Edenville
+48621,Fairview
+48622,Farwell
+48623,Freeland
+48624,Gladwin
+48625,Harrison
+48626,Hemlock
+48627,Higgins Lake
+48628,Hope
+48629,Houghton Lake
+48630,Houghton Lake Heights
+48631,Kawkawlin
+48632,Lake
+48633,Lake George
+48634,Linwood
+48635,Lupton
+48636,Luzerne
+48637,Merrill
+48640,Midland
+48641,Midland
+48642,Midland
+48647,Mio
+48649,Oakley
+48650,Pinconning
+48651,Prudenville
+48652,Rhodes
+48653,Roscommon
+48654,Rose City
+48655,Saint Charles
+48656,Saint Helen
+48657,Sanford
+48658,Standish
+48659,Sterling
+48661,West Branch
+48662,Wheeler
+48663,Saginaw
+48667,Midland
+48670,Midland
+48674,Midland
+48686,Midland
+48701,Akron
+48703,Au Gres
+48705,Barton City
+48706,Bay City
+48707,Bay City
+48708,Bay City
+48710,University Center
+48720,Bay Port
+48721,Black River
+48722,Bridgeport
+48723,Caro
+48724,Carrollton
+48725,Caseville
+48726,Cass City
+48727,Clifford
+48728,Curran
+48729,Deford
+48730,East Tawas
+48731,Elkton
+48732,Essexville
+48733,Fairgrove
+48734,Frankenmuth
+48735,Gagetown
+48736,Gilford
+48737,Glennie
+48738,Greenbush
+48739,Hale
+48740,Harrisville
+48741,Kingston
+48742,Lincoln
+48743,Long Lake
+48744,Mayville
+48745,Mikado
+48746,Millington
+48747,Munger
+48748,National City
+48749,Omer
+48750,Oscoda
+48754,Owendale
+48755,Pigeon
+48756,Prescott
+48757,Reese
+48758,Richville
+48759,Sebewaing
+48760,Silverwood
+48761,South Branch
+48762,Spruce
+48763,Tawas City
+48764,Tawas City
+48765,Turner
+48766,Twining
+48767,Unionville
+48768,Vassar
+48769,Tuscola
+48770,Whittemore
+48787,Frankenmuth
+48801,Alma
+48802,Alma
+48804,Mount Pleasant
+48805,Okemos
+48806,Ashley
+48807,Bannister
+48808,Bath
+48809,Belding
+48811,Carson City
+48812,Cedar Lake
+48813,Charlotte
+48815,Clarksville
+48816,Cohoctah
+48817,Corunna
+48818,Crystal
+48819,Dansville
+48820,Dewitt
+48821,Dimondale
+48822,Eagle
+48823,East Lansing
+48824,East Lansing
+48825,East Lansing
+48826,East Lansing
+48827,Eaton Rapids
+48829,Edmore
+48830,Elm Hall
+48831,Elsie
+48832,Elwell
+48833,Eureka
+48834,Fenwick
+48835,Fowler
+48836,Fowlerville
+48837,Grand Ledge
+48838,Greenville
+48840,Haslett
+48841,Henderson
+48842,Holt
+48843,Howell
+48844,Howell
+48845,Hubbardston
+48846,Ionia
+48847,Ithaca
+48848,Laingsburg
+48849,Lake Odessa
+48850,Lakeview
+48851,Lyons
+48852,McBrides
+48853,Maple Rapids
+48854,Mason
+48856,Middleton
+48857,Morrice
+48858,Mount Pleasant
+48859,Mount Pleasant
+48860,Muir
+48861,Mulliken
+48862,North Star
+48863,Oak Grove
+48864,Okemos
+48865,Orleans
+48866,Ovid
+48867,Owosso
+48870,Palo
+48871,Perrinton
+48872,Perry
+48873,Pewamo
+48874,Pompeii
+48875,Portland
+48876,Potterville
+48877,Riverdale
+48878,Rosebush
+48879,Saint Johns
+48880,Saint Louis
+48881,Saranac
+48882,Shaftsburg
+48883,Shepherd
+48884,Sheridan
+48885,Sidney
+48886,Six Lakes
+48887,Smyrna
+48888,Stanton
+48889,Sumner
+48890,Sunfield
+48891,Vestaburg
+48892,Webberville
+48893,Weidman
+48894,Westphalia
+48895,Williamston
+48896,Winn
+48897,Woodland
+48901,Lansing
+48906,Lansing
+48907,Lansing
+48908,Lansing
+48909,Lansing
+48910,Lansing
+48911,Lansing
+48912,Lansing
+48913,Lansing
+48915,Lansing
+48916,Lansing
+48917,Lansing
+48918,Lansing
+48919,Lansing
+48921,Lansing
+48922,Lansing
+48924,Lansing
+48929,Lansing
+48930,Lansing
+48933,Lansing
+48937,Lansing
+48950,Lansing
+48956,Lansing
+48980,Lansing
+49001,Kalamazoo
+49002,Portage
+49003,Kalamazoo
+49004,Kalamazoo
+49005,Kalamazoo
+49006,Kalamazoo
+49007,Kalamazoo
+49008,Kalamazoo
+49009,Kalamazoo
+49010,Allegan
+49011,Athens
+49012,Augusta
+49013,Bangor
+49014,Battle Creek
+49015,Battle Creek
+49016,Battle Creek
+49017,Battle Creek
+49018,Battle Creek
+49019,Kalamazoo
+49020,Bedford
+49021,Bellevue
+49022,Benton Harbor
+49023,Benton Harbor
+49024,Portage
+49026,Bloomingdale
+49027,Breedsville
+49028,Bronson
+49029,Burlington
+49030,Burr Oak
+49031,Cassopolis
+49032,Centreville
+49033,Ceresco
+49034,Climax
+49035,Cloverdale
+49036,Coldwater
+49038,Coloma
+49039,Hagar Shores
+49040,Colon
+49041,Comstock
+49042,Constantine
+49043,Covert
+49045,Decatur
+49046,Delton
+49047,Dowagiac
+49050,Dowling
+49051,East Leroy
+49052,Fulton
+49053,Galesburg
+49055,Gobles
+49056,Grand Junction
+49057,Hartford
+49058,Hastings
+49060,Hickory Corners
+49061,Jones
+49062,Kendall
+49063,Lacota
+49064,Lawrence
+49065,Lawton
+49066,Leonidas
+49067,Marcellus
+49068,Marshall
+49069,Marshall
+49070,Martin
+49071,Mattawan
+49072,Mendon
+49073,Nashville
+49074,Nazareth
+49075,Nottawa
+49076,Olivet
+49077,Oshtemo
+49078,Otsego
+49079,Paw Paw
+49080,Plainwell
+49081,Portage
+49082,Quincy
+49083,Richland
+49084,Riverside
+49085,Saint Joseph
+49087,Schoolcraft
+49088,Scotts
+49089,Sherwood
+49090,South Haven
+49091,Sturgis
+49092,Tekonsha
+49093,Three Rivers
+49094,Union City
+49095,Vandalia
+49096,Vermontville
+49097,Vicksburg
+49098,Watervliet
+49099,White Pigeon
+49101,Baroda
+49102,Berrien Center
+49103,Berrien Springs
+49104,Berrien Springs
+49106,Bridgman
+49107,Buchanan
+49111,Eau Claire
+49112,Edwardsburg
+49113,Galien
+49115,Harbert
+49116,Lakeside
+49117,New Buffalo
+49119,New Troy
+49120,Niles
+49121,Niles
+49125,Sawyer
+49126,Sodus
+49127,Stevensville
+49128,Three Oaks
+49129,Union Pier
+49130,Union
+49201,Jackson
+49202,Jackson
+49203,Jackson
+49204,Jackson
+49220,Addison
+49221,Adrian
+49224,Albion
+49227,Allen
+49228,Blissfield
+49229,Britton
+49230,Brooklyn
+49232,Camden
+49233,Cement City
+49234,Clarklake
+49235,Clayton
+49236,Clinton
+49237,Concord
+49238,Deerfield
+49239,Frontier
+49240,Grass Lake
+49241,Hanover
+49242,Hillsdale
+49245,Homer
+49246,Horton
+49247,Hudson
+49248,Jasper
+49249,Jerome
+49250,Jonesville
+49251,Leslie
+49252,Litchfield
+49253,Manitou Beach
+49254,Michigan Center
+49255,Montgomery
+49256,Morenci
+49257,Moscow
+49258,Mosherville
+49259,Munith
+49261,Napoleon
+49262,North Adams
+49263,Norvell
+49264,Onondaga
+49265,Onsted
+49266,Osseo
+49267,Ottawa Lake
+49268,Palmyra
+49269,Parma
+49270,Petersburg
+49271,Pittsford
+49272,Pleasant Lake
+49274,Reading
+49275,Ridgeway
+49276,Riga
+49277,Rives Junction
+49278,Rollin
+49279,Sand Creek
+49280,Seneca
+49281,Somerset
+49282,Somerset Center
+49283,Spring Arbor
+49284,Springport
+49285,Stockbridge
+49286,Tecumseh
+49287,Tipton
+49288,Waldron
+49289,Weston
+49301,Ada
+49302,Alto
+49303,Bailey
+49304,Baldwin
+49305,Barryton
+49306,Belmont
+49307,Big Rapids
+49309,Bitely
+49310,Blanchard
+49311,Bradley
+49312,Brohman
+49314,Burnips
+49315,Byron Center
+49316,Caledonia
+49317,Cannonsburg
+49318,Casnovia
+49319,Cedar Springs
+49320,Chippewa Lake
+49321,Comstock Park
+49322,Coral
+49323,Dorr
+49325,Freeport
+49326,Gowen
+49327,Grant
+49328,Hopkins
+49329,Howard City
+49330,Kent City
+49331,Lowell
+49332,Mecosta
+49333,Middleville
+49335,Moline
+49336,Morley
+49337,Newaygo
+49338,Paris
+49339,Pierson
+49340,Remus
+49341,Rockford
+49342,Rodney
+49343,Sand Lake
+49344,Shelbyville
+49345,Sparta
+49346,Stanwood
+49347,Trufant
+49348,Wayland
+49349,White Cloud
+49351,Rockford
+49355,Ada
+49356,Ada
+49357,Ada
+49401,Allendale
+49402,Branch
+49403,Conklin
+49404,Coopersville
+49405,Custer
+49406,Douglas
+49408,Fennville
+49409,Ferrysburg
+49410,Fountain
+49411,Free Soil
+49412,Fremont
+49413,Fremont
+49415,Fruitport
+49416,Glenn
+49417,Grand Haven
+49418,Grandville
+49419,Hamilton
+49420,Hart
+49421,Hesperia
+49422,Holland
+49423,Holland
+49424,Holland
+49425,Holton
+49426,Hudsonville
+49427,Jamestown
+49428,Jenison
+49429,Jenison
+49430,Lamont
+49431,Ludington
+49434,Macatawa
+49435,Marne
+49436,Mears
+49437,Montague
+49440,Muskegon
+49441,Muskegon
+49442,Muskegon
+49443,Muskegon
+49444,Muskegon
+49445,Muskegon
+49446,New Era
+49448,Nunica
+49449,Pentwater
+49450,Pullman
+49451,Ravenna
+49452,Rothbury
+49453,Saugatuck
+49454,Scottville
+49455,Shelby
+49456,Spring Lake
+49457,Twin Lake
+49458,Walhalla
+49459,Walkerville
+49460,West Olive
+49461,Whitehall
+49463,Wabaningo
+49464,Zeeland
+49468,Grandville
+49501,Grand Rapids
+49502,Grand Rapids
+49503,Grand Rapids
+49504,Grand Rapids
+49505,Grand Rapids
+49506,Grand Rapids
+49507,Grand Rapids
+49508,Grand Rapids
+49509,Grand Rapids
+49510,Grand Rapids
+49512,Grand Rapids
+49514,Grand Rapids
+49515,Grand Rapids
+49516,Grand Rapids
+49518,Grand Rapids
+49523,Grand Rapids
+49525,Grand Rapids
+49530,Grand Rapids
+49544,Grand Rapids
+49546,Grand Rapids
+49548,Grand Rapids
+49550,Grand Rapids
+49555,Grand Rapids
+49560,Grand Rapids
+49588,Grand Rapids
+49599,Grand Rapids
+49601,Cadillac
+49610,Acme
+49611,Alba
+49612,Alden
+49613,Arcadia
+49614,Bear Lake
+49615,Bellaire
+49616,Benzonia
+49617,Beulah
+49618,Boon
+49619,Brethren
+49620,Buckley
+49621,Cedar
+49622,Central Lake
+49623,Chase
+49625,Copemish
+49626,Eastlake
+49627,Eastport
+49628,Elberta
+49629,Elk Rapids
+49630,Empire
+49631,Evart
+49632,Falmouth
+49633,Fife Lake
+49634,Filer City
+49635,Frankfort
+49636,Glen Arbor
+49637,Grawn
+49638,Harrietta
+49639,Hersey
+49640,Honor
+49642,Idlewild
+49643,Interlochen
+49644,Irons
+49645,Kaleva
+49646,Kalkaska
+49648,Kewadin
+49649,Kingsley
+49650,Lake Ann
+49651,Lake City
+49653,Lake Leelanau
+49654,Leland
+49655,Leroy
+49656,Luther
+49657,Mc Bain
+49659,Mancelona
+49660,Manistee
+49663,Manton
+49664,Maple City
+49665,Marion
+49666,Mayfield
+49667,Merritt
+49668,Mesick
+49670,Northport
+49673,Old Mission
+49674,Omena
+49675,Onekama
+49676,Rapid City
+49677,Reed City
+49679,Sears
+49680,South Boardman
+49682,Suttons Bay
+49683,Thompsonville
+49684,Traverse City
+49685,Traverse City
+49686,Traverse City
+49688,Tustin
+49689,Wellston
+49690,Williamsburg
+49696,Traverse City
+49701,Mackinaw City
+49705,Afton
+49706,Alanson
+49707,Alpena
+49709,Atlanta
+49710,Barbeau
+49711,Bay Shore
+49712,Boyne City
+49713,Boyne Falls
+49715,Brimley
+49716,Brutus
+49717,Burt Lake
+49718,Carp Lake
+49719,Cedarville
+49720,Charlevoix
+49721,Cheboygan
+49722,Conway
+49723,Cross Village
+49724,Dafter
+49725,De Tour Village
+49726,Drummond Island
+49727,East Jordan
+49728,Eckerman
+49729,Ellsworth
+49730,Elmira
+49733,Frederic
+49734,Gaylord
+49735,Gaylord
+49736,Goetzville
+49737,Good Hart
+49738,Grayling
+49739,Grayling
+49740,Harbor Springs
+49743,Hawks
+49744,Herron
+49745,Hessel
+49746,Hillman
+49747,Hubbard Lake
+49748,Hulbert
+49749,Indian River
+49751,Johannesburg
+49752,Kinross
+49753,Lachine
+49755,Levering
+49756,Lewiston
+49757,Mackinac Island
+49759,Millersburg
+49760,Moran
+49761,Mullett Lake
+49762,Naubinway
+49764,Oden
+49765,Onaway
+49766,Ossineke
+49768,Paradise
+49769,Pellston
+49770,Petoskey
+49774,Pickford
+49775,Pointe Aux Pins
+49776,Posen
+49777,Presque Isle
+49778,Brimley
+49779,Rogers City
+49780,Rudyard
+49781,Saint Ignace
+49782,Beaver Island
+49783,Sault Sainte Marie
+49784,Kincheloe
+49785,Kincheloe
+49786,Kincheloe
+49788,Kincheloe
+49790,Strongs
+49791,Topinabee
+49792,Tower
+49793,Trout Lake
+49795,Vanderbilt
+49796,Walloon Lake
+49797,Waters
+49799,Wolverine
+49801,Iron Mountain
+49802,Kingsford
+49805,Allouez
+49806,Au Train
+49807,Bark River
+49808,Big Bay
+49812,Carney
+49813,Cedar River
+49814,Champion
+49815,Channing
+49816,Chatham
+49817,Cooks
+49818,Cornell
+49819,Arnold
+49820,Curtis
+49821,Daggett
+49822,Deerton
+49825,Eben Junction
+49826,Rumely
+49827,Engadine
+49829,Escanaba
+49831,Felch
+49833,Little Lake
+49834,Foster City
+49835,Garden
+49836,Germfask
+49837,Gladstone
+49838,Gould City
+49839,Grand Marais
+49840,Gulliver
+49841,Gwinn
+49845,Harris
+49847,Hermansville
+49848,Ingalls
+49849,Ishpeming
+49852,Loretto
+49853,Mc Millan
+49854,Manistique
+49855,Marquette
+49858,Menominee
+49861,Michigamme
+49862,Munising
+49863,Nadeau
+49864,Nahma
+49865,National Mine
+49866,Negaunee
+49868,Newberry
+49870,Norway
+49871,Palmer
+49872,Perkins
+49873,Perronville
+49874,Powers
+49876,Quinnesec
+49877,Ralph
+49878,Rapid River
+49879,Republic
+49880,Rock
+49881,Sagola
+49883,Seney
+49884,Shingleton
+49885,Skandia
+49886,Spalding
+49887,Stephenson
+49891,Trenary
+49892,Vulcan
+49893,Wallace
+49894,Wells
+49895,Wetmore
+49896,Wilson
+49901,Ahmeek
+49902,Alpha
+49903,Amasa
+49905,Atlantic Mine
+49908,Baraga
+49910,Bergland
+49911,Bessemer
+49912,Bruce Crossing
+49913,Calumet
+49915,Caspian
+49916,Chassell
+49917,Copper City
+49918,Copper Harbor
+49919,Covington
+49920,Crystal Falls
+49921,Dodgeville
+49922,Dollar Bay
+49925,Ewen
+49927,Gaastra
+49929,Greenland
+49930,Hancock
+49931,Houghton
+49934,Hubbell
+49935,Iron River
+49938,Ironwood
+49942,Kearsarge
+49945,Lake Linden
+49946,Lanse
+49947,Marenisco
+49948,Mass City
+49950,Mohawk
+49952,Nisula
+49953,Ontonagon
+49955,Painesdale
+49958,Pelkie
+49959,Ramsay
+49960,Rockland
+49961,Sidnaw
+49962,Skanee
+49963,South Range
+49964,Stambaugh
+49965,Toivola
+49967,Trout Creek
+49968,Wakefield
+49969,Watersmeet
+49970,Watton
+49971,White Pine
+96960,Majuro
+96970,Ebeye
+03901,Berwick
+03902,Cape Neddick
+03903,Eliot
+03904,Kittery
+03905,Kittery Point
+03906,North Berwick
+03907,Ogunquit
+03908,South Berwick
+03909,York
+03910,York Beach
+03911,York Harbor
+04001,Acton
+04002,Alfred
+04003,Bailey Island
+04004,Bar Mills
+04005,Biddeford
+04006,Biddeford Pool
+04007,Biddeford
+04008,Bowdoinham
+04009,Bridgton
+04010,Brownfield
+04011,Brunswick
+04013,Bustins Island
+04014,Cape Porpoise
+04015,Casco
+04016,Center Lovell
+04017,Chebeague Island
+04019,Cliff Island
+04020,Cornish
+04021,Cumberland Center
+04022,Denmark
+04024,East Baldwin
+04027,Lebanon
+04028,East Parsonfield
+04029,Sebago
+04030,East Waterboro
+04032,Freeport
+04033,Freeport
+04034,Freeport
+04037,Fryeburg
+04038,Gorham
+04039,Gray
+04040,Harrison
+04041,Hiram
+04042,Hollis Center
+04043,Kennebunk
+04046,Kennebunkport
+04047,Parsonsfield
+04048,Limerick
+04049,Limington
+04050,Long Island
+04051,Lovell
+04053,Merepoint
+04054,Moody
+04055,Naples
+04056,Newfield
+04057,North Bridgton
+04061,North Waterboro
+04062,Windham
+04063,Ocean Park
+04064,Old Orchard Beach
+04066,Orrs Island
+04068,Porter
+04069,Pownal
+04070,Scarborough
+04071,Raymond
+04072,Saco
+04073,Sanford
+04074,Scarborough
+04075,Sebago Lake
+04076,Shapleigh
+04077,South Casco
+04078,South Freeport
+04079,Harpswell
+04081,South Waterford
+04082,South Windham
+04083,Springvale
+04084,Standish
+04085,Steep Falls
+04086,Topsham
+04087,Waterboro
+04088,Waterford
+04090,Wells
+04091,West Baldwin
+04092,Westbrook
+04093,Buxton
+04094,West Kennebunk
+04095,West Newfield
+04096,Yarmouth
+04097,North Yarmouth
+04098,Westbrook
+04101,Portland
+04102,Portland
+04103,Portland
+04104,Portland
+04105,Falmouth
+04106,South Portland
+04107,Cape Elizabeth
+04108,Peaks Island
+04109,Portland
+04110,Cumberland Foreside
+04112,Portland
+04116,South Portland
+04122,Portland
+04123,Portland
+04124,Portland
+04210,Auburn
+04211,Auburn
+04212,Auburn
+04216,Andover
+04217,Bethel
+04219,Bryant Pond
+04220,Buckfield
+04221,Canton
+04222,Durham
+04223,Danville
+04224,Dixfield
+04225,Dryden
+04226,East Andover
+04227,East Dixfield
+04228,East Livermore
+04230,East Poland
+04231,Stoneham
+04234,East Wilton
+04236,Greene
+04237,Hanover
+04238,Hebron
+04239,Jay
+04240,Lewiston
+04241,Lewiston
+04243,Lewiston
+04250,Lisbon
+04252,Lisbon Falls
+04253,Livermore
+04254,Livermore Falls
+04255,Greenwood
+04256,Mechanic Falls
+04257,Mexico
+04258,Minot
+04259,Monmouth
+04260,New Gloucester
+04261,Newry
+04262,North Jay
+04263,Leeds
+04265,North Monmouth
+04266,North Turner
+04267,North Waterford
+04268,Norway
+04270,Oxford
+04271,Paris
+04274,Poland
+04275,Roxbury
+04276,Rumford
+04278,Rumford Center
+04280,Sabattus
+04281,South Paris
+04282,Turner
+04283,Turner Center
+04284,Wayne
+04285,Weld
+04286,West Bethel
+04287,Bowdoin
+04288,West Minot
+04289,West Paris
+04290,Peru
+04291,West Poland
+04292,Sumner
+04294,Wilton
+04330,Augusta
+04332,Augusta
+04333,Augusta
+04336,Augusta
+04338,Augusta
+04341,Coopers Mills
+04342,Dresden
+04343,East Winthrop
+04344,Farmingdale
+04345,Gardiner
+04346,Randolph
+04347,Hallowell
+04348,Jefferson
+04349,Kents Hill
+04350,Litchfield
+04351,Manchester
+04352,Mount Vernon
+04353,Whitefield
+04354,Palermo
+04355,Readfield
+04357,Richmond
+04358,South China
+04359,South Gardiner
+04360,Vienna
+04363,Windsor
+04364,Winthrop
+04401,Bangor
+04402,Bangor
+04406,Abbot
+04408,Aurora
+04410,Bradford
+04411,Bradley
+04412,Brewer
+04413,Brookton
+04414,Brownville
+04415,Brownville Junction
+04416,Bucksport
+04417,Burlington
+04418,Greenbush
+04419,Carmel
+04420,Castine
+04421,Castine
+04422,Charleston
+04423,Costigan
+04424,Danforth
+04426,Dover Foxcroft
+04427,Corinth
+04428,Eddington
+04429,Holden
+04430,East Millinocket
+04431,East Orland
+04434,Etna
+04435,Exeter
+04438,Frankfort
+04441,Greenville
+04442,Greenville Junction
+04443,Guilford
+04444,Hampden
+04448,Howland
+04449,Hudson
+04450,Kenduskeag
+04451,Kingman
+04453,Lagrange
+04454,Lambert Lake
+04455,Lee
+04456,Levant
+04457,Lincoln
+04459,Mattawamkeag
+04460,Medway
+04461,Milford
+04462,Millinocket
+04463,Milo
+04464,Monson
+04467,Olamon
+04468,Old Town
+04469,Orono
+04471,Orient
+04472,Orland
+04473,Orono
+04474,Orrington
+04475,Passadumkeag
+04476,Penobscot
+04478,Rockwood
+04479,Sangerville
+04481,Sebec
+04485,Shirley Mills
+04487,Springfield
+04488,Stetson
+04489,Stillwater
+04490,Topsfield
+04491,Vanceboro
+04492,Waite
+04493,West Enfield
+04495,Winn
+04496,Winterport
+04497,Wytopitlock
+04530,Bath
+04535,Alna
+04536,Bayville
+04537,Boothbay
+04538,Boothbay Harbor
+04539,Bristol
+04541,Chamberlain
+04543,Damariscotta
+04544,East Boothbay
+04547,Friendship
+04548,Georgetown
+04549,Isle of Springs
+04551,Bremen
+04552,Newagen
+04553,Newcastle
+04554,New Harbor
+04555,Nobleboro
+04556,Edgecomb
+04558,Pemaquid
+04562,Phippsburg
+04563,Cushing
+04564,Round Pond
+04565,Sebasco Estates
+04567,Small Point
+04568,South Bristol
+04570,Squirrel Island
+04571,Trevett
+04572,Waldoboro
+04573,Walpole
+04574,Washington
+04575,West Boothbay Harbor
+04576,Southport
+04578,Wiscasset
+04579,Woolwich
+04605,Ellsworth
+04606,Addison
+04607,Gouldsboro
+04609,Bar Harbor
+04611,Beals
+04612,Bernard
+04613,Birch Harbor
+04614,Blue Hill
+04615,Blue Hill Falls
+04616,Brooklin
+04617,Brooksville
+04619,Calais
+04622,Cherryfield
+04623,Columbia Falls
+04624,Corea
+04625,Cranberry Isles
+04626,Cutler
+04627,Deer Isle
+04628,Dennysville
+04629,East Blue Hill
+04630,East Machias
+04631,Eastport
+04634,Franklin
+04635,Frenchboro
+04637,Grand Lake Stream
+04640,Hancock
+04642,Harborside
+04643,Harrington
+04644,Hulls Cove
+04645,Isle au Haut
+04646,Islesford
+04648,Jonesboro
+04649,Jonesport
+04650,Little Deer Isle
+04652,Lubec
+04653,Bass Harbor
+04654,Machias
+04655,Machiasport
+04656,Manset
+04657,Meddybemps
+04658,Milbridge
+04660,Mount Desert
+04662,Northeast Harbor
+04664,Sullivan
+04665,Otter Creek
+04666,Pembroke
+04667,Perry
+04668,Princeton
+04669,Prospect Harbor
+04671,Robbinston
+04672,Salsbury Cove
+04673,Sargentville
+04674,Seal Cove
+04675,Seal Harbor
+04676,Sedgwick
+04677,Sorrento
+04679,Southwest Harbor
+04680,Steuben
+04681,Stonington
+04683,Sunset
+04684,Surry
+04685,Swans Island
+04686,Wesley
+04690,West Tremont
+04691,Whiting
+04693,Winter Harbor
+04694,Baileyville
+04730,Houlton
+04732,Ashland
+04733,Benedicta
+04734,Blaine
+04735,Bridgewater
+04736,Caribou
+04737,Clayton Lake
+04738,Crouseville
+04739,Eagle Lake
+04740,Easton
+04741,Estcourt Station
+04742,Fort Fairfield
+04743,Fort Kent
+04744,Fort Kent Mills
+04745,Frenchville
+04746,Grand Isle
+04747,Island Falls
+04750,Limestone
+04751,Limestone
+04756,Madawaska
+04757,Mapleton
+04758,Mars Hill
+04759,Masardis
+04760,Monticello
+04761,New Limerick
+04762,New Sweden
+04763,Oakfield
+04764,Oxbow
+04765,Patten
+04766,Perham
+04768,Portage
+04769,Presque Isle
+04770,Quimby
+04772,Saint Agatha
+04773,Saint David
+04774,Saint Francis
+04775,Sheridan
+04776,Sherman Mills
+04777,Sherman Station
+04779,Sinclair
+04780,Smyrna Mills
+04781,Soldier Pond
+04782,Stacyville
+04783,Stockholm
+04785,Van Buren
+04786,Washburn
+04787,Westfield
+04788,Winterville
+04841,Rockland
+04843,Camden
+04846,Glen Cove
+04847,Hope
+04848,Islesboro
+04849,Lincolnville
+04850,Lincolnville Center
+04851,Matinicus
+04852,Monhegan
+04853,North Haven
+04854,Owls Head
+04855,Port Clyde
+04856,Rockport
+04857,Saint George
+04858,South Thomaston
+04859,Spruce Head
+04860,Tenants Harbor
+04861,Thomaston
+04862,Union
+04863,Vinalhaven
+04864,Warren
+04865,West Rockport
+04901,Waterville
+04903,Waterville
+04910,Albion
+04911,Anson
+04912,Athens
+04915,Belfast
+04917,Belgrade
+04918,Belgrade Lakes
+04920,Bingham
+04921,Brooks
+04922,Burnham
+04923,Cambridge
+04924,Canaan
+04925,Caratunk
+04926,China
+04927,Clinton
+04928,Corinna
+04929,Detroit
+04930,Dexter
+04932,Dixmont
+04933,East Newport
+04935,East Vassalboro
+04936,Eustis
+04937,Fairfield
+04938,Farmington
+04939,Garland
+04940,Farmington Falls
+04941,Freedom
+04942,Harmony
+04943,Hartland
+04944,Hinckley
+04945,Jackman
+04947,Kingfield
+04949,Liberty
+04950,Madison
+04951,Monroe
+04952,Morrill
+04953,Newport
+04954,New Portland
+04955,New Sharon
+04956,New Vineyard
+04957,Norridgewock
+04958,North Anson
+04961,North New Portland
+04962,North Vassalboro
+04963,Oakland
+04964,Oquossoc
+04965,Palmyra
+04966,Phillips
+04967,Pittsfield
+04969,Plymouth
+04970,Rangeley
+04971,Saint Albans
+04972,Sandy Point
+04973,Searsmont
+04974,Searsport
+04975,Shawmut
+04976,Skowhegan
+04978,Smithfield
+04979,Solon
+04981,Stockton Springs
+04982,Stratton
+04983,Strong
+04984,Temple
+04985,West Forks
+04986,Thorndike
+04987,Troy
+04988,Unity
+04989,Vassalboro
+04992,West Farmington
+20601,Waldorf
+20602,Waldorf
+20603,Waldorf
+20604,Waldorf
+20606,Abell
+20607,Accokeek
+20608,Aquasco
+20609,Avenue
+20610,Barstow
+20611,Bel Alton
+20612,Benedict
+20613,Brandywine
+20615,Broomes Island
+20616,Bryans Road
+20617,Bryantown
+20618,Bushwood
+20619,California
+20620,Callaway
+20621,Chaptico
+20622,Charlotte Hall
+20623,Cheltenham
+20624,Clements
+20625,Cobb Island
+20626,Coltons Point
+20627,Compton
+20628,Dameron
+20629,Dowell
+20630,Drayden
+20632,Faulkner
+20634,Great Mills
+20635,Helen
+20636,Hollywood
+20637,Hughesville
+20639,Huntingtown
+20640,Indian Head
+20643,Ironsides
+20645,Issue
+20646,La Plata
+20650,Leonardtown
+20653,Lexington Park
+20656,Loveville
+20657,Lusby
+20658,Marbury
+20659,Mechanicsville
+20660,Morganza
+20661,Mount Victoria
+20662,Nanjemoy
+20664,Newburg
+20667,Park Hall
+20670,Patuxent River
+20674,Piney Point
+20675,Pomfret
+20676,Port Republic
+20677,Port Tobacco
+20678,Prince Frederick
+20680,Ridge
+20682,Rock Point
+20684,Saint Inigoes
+20685,Saint Leonard
+20686,Saint Marys City
+20687,Scotland
+20688,Solomons
+20689,Sunderland
+20690,Tall Timbers
+20692,Valley Lee
+20693,Welcome
+20695,White Plains
+20697,Southern MD Facility
+20701,Annapolis Junction
+20703,Lanham
+20704,Beltsville
+20705,Beltsville
+20706,Lanham
+20707,Laurel
+20708,Laurel
+20709,Laurel
+20710,Bladensburg
+20711,Lothian
+20712,Mount Rainier
+20714,North Beach
+20715,Bowie
+20716,Bowie
+20717,Bowie
+20718,Bowie
+20719,Bowie
+20720,Bowie
+20721,Bowie
+20722,Brentwood
+20723,Laurel
+20724,Laurel
+20725,Laurel
+20726,Laurel
+20731,Capitol Heights
+20732,Chesapeake Beach
+20733,Churchton
+20735,Clinton
+20736,Owings
+20737,Riverdale
+20738,Riverdale
+20740,College Park
+20741,College Park
+20742,College Park
+20743,Capitol Heights
+20744,Fort Washington
+20745,Oxon Hill
+20746,Suitland
+20747,District Heights
+20748,Temple Hills
+20749,Fort Washington
+20750,Oxon Hill
+20751,Deale
+20752,Suitland
+20753,District Heights
+20754,Dunkirk
+20755,Fort George G Meade
+20757,Temple Hills
+20758,Friendship
+20759,Fulton
+20762,Andrews Air Force Base
+20763,Savage
+20764,Shady Side
+20765,Galesville
+20768,Greenbelt
+20769,Glenn Dale
+20770,Greenbelt
+20771,Greenbelt
+20772,Upper Marlboro
+20773,Upper Marlboro
+20774,Upper Marlboro
+20775,Upper Marlboro
+20776,Harwood
+20777,Highland
+20778,West River
+20779,Tracys Landing
+20781,Hyattsville
+20782,Hyattsville
+20783,Hyattsville
+20784,Hyattsville
+20785,Hyattsville
+20787,Hyattsville
+20788,Hyattsville
+20790,Capitol Heights
+20791,Capitol Heights
+20794,Jessup
+20797,Southern MD Facility
+20799,Capitol Heights
+20812,Glen Echo
+20813,Bethesda
+20814,Bethesda
+20815,Chevy Chase
+20816,Bethesda
+20817,Bethesda
+20818,Cabin John
+20824,Bethesda
+20825,Chevy Chase
+20827,Bethesda
+20830,Olney
+20832,Olney
+20833,Brookeville
+20837,Poolesville
+20838,Barnesville
+20839,Beallsville
+20841,Boyds
+20842,Dickerson
+20847,Rockville
+20848,Rockville
+20849,Rockville
+20850,Rockville
+20851,Rockville
+20852,Rockville
+20853,Rockville
+20854,Potomac
+20855,Derwood
+20857,Rockville
+20859,Potomac
+20860,Sandy Spring
+20861,Ashton
+20862,Brinklow
+20866,Burtonsville
+20868,Spencerville
+20871,Clarksburg
+20872,Damascus
+20874,Germantown
+20875,Germantown
+20876,Germantown
+20877,Gaithersburg
+20878,Gaithersburg
+20879,Gaithersburg
+20880,Washington Grove
+20882,Gaithersburg
+20884,Gaithersburg
+20885,Gaithersburg
+20886,Montgomery Village
+20889,Bethesda
+20891,Kensington
+20892,Bethesda
+20894,Bethesda
+20895,Kensington
+20896,Garrett Park
+20897,Suburb Maryland Fac
+20898,Gaithersburg
+20899,Gaithersburg
+20901,Silver Spring
+20902,Silver Spring
+20903,Silver Spring
+20904,Silver Spring
+20905,Silver Spring
+20906,Silver Spring
+20907,Silver Spring
+20908,Silver Spring
+20910,Silver Spring
+20911,Silver Spring
+20912,Takoma Park
+20913,Takoma Park
+20914,Silver Spring
+20915,Silver Spring
+20916,Silver Spring
+20918,Silver Spring
+20997,Silver Spring
+21001,Aberdeen
+21005,Aberdeen Proving Ground
+21009,Abingdon
+21010,Gunpowder
+21012,Arnold
+21013,Baldwin
+21014,Bel Air
+21015,Bel Air
+21017,Belcamp
+21018,Benson
+21020,Boring
+21022,Brooklandville
+21023,Butler
+21027,Chase
+21028,Churchville
+21029,Clarksville
+21030,Cockeysville
+21031,Hunt Valley
+21032,Crownsville
+21034,Darlington
+21035,Davidsonville
+21036,Dayton
+21037,Edgewater
+21040,Edgewood
+21041,Ellicott City
+21042,Ellicott City
+21043,Ellicott City
+21044,Columbia
+21045,Columbia
+21046,Columbia
+21047,Fallston
+21048,Finksburg
+21050,Forest Hill
+21051,Fork
+21052,Fort Howard
+21053,Freeland
+21054,Gambrills
+21055,Garrison
+21056,Gibson Island
+21057,Glen Arm
+21060,Glen Burnie
+21061,Glen Burnie
+21062,Glen Burnie
+21071,Glyndon
+21074,Hampstead
+21075,Elkridge
+21076,Hanover
+21077,Harmans
+21078,Havre de Grace
+21080,Henryton
+21082,Hydes
+21084,Jarrettsville
+21085,Joppa
+21087,Kingsville
+21088,Lineboro
+21090,Linthicum Heights
+21092,Long Green
+21093,Lutherville Timonium
+21094,Lutherville Timonium
+21098,Hanover
+21102,Manchester
+21104,Marriottsville
+21105,Maryland Line
+21106,Mayo
+21108,Millersville
+21111,Monkton
+21113,Odenton
+21114,Crofton
+21117,Owings Mills
+21120,Parkton
+21122,Pasadena
+21123,Pasadena
+21128,Perry Hall
+21130,Perryman
+21131,Phoenix
+21132,Pylesville
+21133,Randallstown
+21136,Reisterstown
+21139,Riderwood
+21140,Riva
+21144,Severn
+21146,Severna Park
+21150,Simpsonville
+21152,Sparks Glencoe
+21153,Stevenson
+21154,Street
+21155,Upperco
+21156,Upper Falls
+21157,Westminster
+21158,Westminster
+21160,Whiteford
+21161,White Hall
+21162,White Marsh
+21163,Woodstock
+21201,Baltimore
+21202,Baltimore
+21203,Baltimore
+21204,Towson
+21205,Baltimore
+21206,Baltimore
+21207,Gwynn Oak
+21208,Pikesville
+21209,Baltimore
+21210,Baltimore
+21211,Baltimore
+21212,Baltimore
+21213,Baltimore
+21214,Baltimore
+21215,Baltimore
+21216,Baltimore
+21217,Baltimore
+21218,Baltimore
+21219,Sparrows Point
+21220,Middle River
+21221,Essex
+21222,Dundalk
+21223,Baltimore
+21224,Baltimore
+21225,Brooklyn
+21226,Curtis Bay
+21227,Halethorpe
+21228,Catonsville
+21229,Baltimore
+21230,Baltimore
+21231,Baltimore
+21233,Baltimore
+21234,Parkville
+21235,Baltimore
+21236,Nottingham
+21237,Rosedale
+21239,Baltimore
+21240,Baltimore
+21241,Baltimore
+21244,Windsor Mill
+21250,Baltimore
+21251,Baltimore
+21252,Baltimore
+21260,Baltimore
+21263,Baltimore
+21264,Baltimore
+21265,Baltimore
+21268,Baltimore
+21270,Baltimore
+21273,Baltimore
+21274,Baltimore
+21275,Baltimore
+21278,Baltimore
+21279,Baltimore
+21280,Baltimore
+21281,Baltimore
+21282,Baltimore
+21283,Baltimore
+21284,Baltimore
+21285,Baltimore
+21286,Towson
+21287,Baltimore
+21288,Baltimore
+21289,Baltimore
+21290,Baltimore
+21297,Baltimore
+21298,Baltimore
+21401,Annapolis
+21402,Annapolis
+21403,Annapolis
+21404,Annapolis
+21405,Annapolis
+21411,Annapolis
+21412,Annapolis
+21501,Cumberland
+21502,Cumberland
+21503,Cumberland
+21504,Cumberland
+21505,Cumberland
+21520,Accident
+21521,Barton
+21522,Bittinger
+21523,Bloomington
+21524,Corriganville
+21528,Eckhart Mines
+21529,Ellerslie
+21530,Flintstone
+21531,Friendsville
+21532,Frostburg
+21536,Grantsville
+21538,Kitzmiller
+21539,Lonaconing
+21540,Luke
+21541,Mc Henry
+21542,Midland
+21543,Midlothian
+21545,Mount Savage
+21550,Oakland
+21555,Oldtown
+21556,Pinto
+21557,Rawlings
+21560,Spring Gap
+21561,Swanton
+21562,Westernport
+21601,Easton
+21606,Easton
+21607,Barclay
+21609,Bethlehem
+21610,Betterton
+21612,Bozman
+21613,Cambridge
+21617,Centreville
+21619,Chester
+21620,Chestertown
+21622,Church Creek
+21623,Church Hill
+21624,Claiborne
+21625,Cordova
+21626,Crapo
+21627,Crocheron
+21628,Crumpton
+21629,Denton
+21631,East New Market
+21632,Federalsburg
+21634,Fishing Creek
+21635,Galena
+21636,Goldsboro
+21638,Grasonville
+21639,Greensboro
+21640,Henderson
+21641,Hillsboro
+21643,Hurlock
+21644,Ingleside
+21645,Kennedyville
+21647,McDaniel
+21648,Madison
+21649,Marydel
+21650,Massey
+21651,Millington
+21652,Neavitt
+21653,Newcomb
+21654,Oxford
+21655,Preston
+21656,Price
+21657,Queen Anne
+21658,Queenstown
+21659,Rhodesdale
+21660,Ridgely
+21661,Rock Hall
+21662,Royal Oak
+21663,Saint Michaels
+21664,Secretary
+21665,Sherwood
+21666,Stevensville
+21667,Still Pond
+21668,Sudlersville
+21669,Taylors Island
+21670,Templeville
+21671,Tilghman
+21672,Toddville
+21673,Trappe
+21675,Wingate
+21676,Wittman
+21677,Woolford
+21678,Worton
+21679,Wye Mills
+21681,Ridgely
+21682,Ridgely
+21683,Ridgely
+21684,Ridgely
+21685,Ridgely
+21686,Ridgely
+21687,Ridgely
+21688,Ridgely
+21690,Chestertown
+21701,Frederick
+21702,Frederick
+21703,Frederick
+21704,Frederick
+21705,Frederick
+21709,Frederick
+21710,Adamstown
+21711,Big Pool
+21713,Boonsboro
+21714,Braddock Heights
+21715,Brownsville
+21716,Brunswick
+21717,Buckeystown
+21718,Burkittsville
+21719,Cascade
+21720,Cavetown
+21721,Chewsville
+21722,Clear Spring
+21723,Cooksville
+21727,Emmitsburg
+21733,Fairplay
+21734,Funkstown
+21736,Gapland
+21737,Glenelg
+21738,Glenwood
+21740,Hagerstown
+21741,Hagerstown
+21742,Hagerstown
+21746,Hagerstown
+21747,Hagerstown
+21748,Hagerstown
+21749,Hagerstown
+21750,Hancock
+21754,Ijamsville
+21755,Jefferson
+21756,Keedysville
+21757,Keymar
+21758,Knoxville
+21759,Ladiesburg
+21762,Libertytown
+21764,Linwood
+21765,Lisbon
+21766,Little Orleans
+21767,Maugansville
+21769,Middletown
+21770,Monrovia
+21771,Mount Airy
+21773,Myersville
+21774,New Market
+21775,New Midway
+21776,New Windsor
+21777,Point of Rocks
+21778,Rocky Ridge
+21779,Rohrersville
+21780,Sabillasville
+21781,Saint James
+21782,Sharpsburg
+21783,Smithsburg
+21784,Sykesville
+21787,Taneytown
+21788,Thurmont
+21790,Tuscarora
+21791,Union Bridge
+21792,Unionville
+21793,Walkersville
+21794,West Friendship
+21795,Williamsport
+21797,Woodbine
+21798,Woodsboro
+21801,Salisbury
+21802,Salisbury
+21803,Salisbury
+21804,Salisbury
+21810,Allen
+21811,Berlin
+21813,Bishopville
+21814,Bivalve
+21817,Crisfield
+21821,Deal Island
+21822,Eden
+21824,Ewell
+21826,Fruitland
+21829,Girdletree
+21830,Hebron
+21835,Linkwood
+21836,Manokin
+21837,Mardela Springs
+21838,Marion Station
+21840,Nanticoke
+21841,Newark
+21842,Ocean City
+21843,Ocean City
+21849,Parsonsburg
+21850,Pittsville
+21851,Pocomoke City
+21852,Powellville
+21853,Princess Anne
+21856,Quantico
+21857,Rehobeth
+21861,Sharptown
+21862,Showell
+21863,Snow Hill
+21864,Stockton
+21865,Tyaskin
+21866,Tylerton
+21867,Upper Fairmount
+21869,Vienna
+21870,Wenona
+21871,Westover
+21872,Whaleyville
+21874,Willards
+21875,Delmar
+21890,Westover
+21901,North East
+21902,Perry Point
+21903,Perryville
+21904,Port Deposit
+21911,Rising Sun
+21912,Warwick
+21913,Cecilton
+21914,Charlestown
+21915,Chesapeake City
+21916,Childs
+21917,Colora
+21918,Conowingo
+21919,Earleville
+21920,Elk Mills
+21921,Elkton
+21922,Elkton
+21930,Georgetown
+01001,Agawam
+01002,Amherst
+01003,Amherst
+01004,Amherst
+01005,Barre
+01007,Belchertown
+01008,Blandford
+01009,Bondsville
+01010,Brimfield
+01011,Chester
+01012,Chesterfield
+01013,Chicopee
+01014,Chicopee
+01020,Chicopee
+01021,Chicopee
+01022,Chicopee
+01026,Cummington
+01027,Easthampton
+01028,East Longmeadow
+01029,East Otis
+01030,Feeding Hills
+01031,Gilbertville
+01032,Goshen
+01033,Granby
+01034,Granville
+01035,Hadley
+01036,Hampden
+01037,Hardwick
+01038,Hatfield
+01039,Haydenville
+01040,Holyoke
+01041,Holyoke
+01050,Huntington
+01053,Leeds
+01054,Leverett
+01056,Ludlow
+01057,Monson
+01059,North Amherst
+01060,Northampton
+01061,Northampton
+01062,Florence
+01063,Northampton
+01066,North Hatfield
+01068,Oakham
+01069,Palmer
+01070,Plainfield
+01071,Russell
+01072,Shutesbury
+01073,Southampton
+01074,South Barre
+01075,South Hadley
+01077,Southwick
+01079,Thorndike
+01080,Three Rivers
+01081,Wales
+01082,Ware
+01083,Warren
+01084,West Chesterfield
+01085,Westfield
+01086,Westfield
+01088,West Hatfield
+01089,West Springfield
+01090,West Springfield
+01092,West Warren
+01093,Whately
+01094,Wheelwright
+01095,Wilbraham
+01096,Williamsburg
+01097,Woronoco
+01098,Worthington
+01101,Springfield
+01102,Springfield
+01103,Springfield
+01104,Springfield
+01105,Springfield
+01106,Longmeadow
+01107,Springfield
+01108,Springfield
+01109,Springfield
+01111,Springfield
+01114,Springfield
+01115,Springfield
+01116,Longmeadow
+01118,Springfield
+01119,Springfield
+01128,Springfield
+01129,Springfield
+01133,Springfield
+01138,Springfield
+01139,Springfield
+01144,Springfield
+01151,Indian Orchard
+01152,Springfield
+01199,Springfield
+01201,Pittsfield
+01202,Pittsfield
+01203,Pittsfield
+01220,Adams
+01222,Ashley Falls
+01223,Becket
+01224,Berkshire
+01225,Cheshire
+01226,Dalton
+01227,Dalton
+01229,Glendale
+01230,Great Barrington
+01235,Hinsdale
+01236,Housatonic
+01237,Lanesboro
+01238,Lee
+01240,Lenox
+01242,Lenox Dale
+01243,Middlefield
+01244,Mill River
+01245,Monterey
+01247,North Adams
+01252,North Egremont
+01253,Otis
+01254,Richmond
+01255,Sandisfield
+01256,Savoy
+01257,Sheffield
+01258,South Egremont
+01259,Southfield
+01260,South Lee
+01262,Stockbridge
+01263,Stockbridge
+01264,Tyringham
+01266,West Stockbridge
+01267,Williamstown
+01270,Windsor
+01301,Greenfield
+01302,Greenfield
+01330,Ashfield
+01331,Athol
+01337,Bernardston
+01338,Buckland
+01339,Charlemont
+01340,Colrain
+01341,Conway
+01342,Deerfield
+01343,Drury
+01344,Erving
+01346,Heath
+01347,Lake Pleasant
+01349,Turners Falls
+01350,Monroe Bridge
+01351,Montague
+01354,Northfield
+01355,New Salem
+01360,Northfield
+01364,Orange
+01366,Petersham
+01367,Rowe
+01368,Royalston
+01369,Shattuckville
+01370,Shelburne Falls
+01373,South Deerfield
+01375,Sunderland
+01376,Turners Falls
+01378,Warwick
+01379,Wendell
+01380,Wendell Depot
+01420,Fitchburg
+01430,Ashburnham
+01431,Ashby
+01432,Ayer
+01436,Baldwinville
+01438,East Templeton
+01440,Gardner
+01441,Gardner
+01450,Groton
+01451,Harvard
+01452,Hubbardston
+01453,Leominster
+01460,Littleton
+01462,Lunenburg
+01463,Pepperell
+01464,Shirley
+01467,Still River
+01468,Templeton
+01469,Townsend
+01470,Groton
+01471,Groton
+01472,West Groton
+01473,Westminster
+01474,West Townsend
+01475,Winchendon
+01477,Winchendon Springs
+01501,Auburn
+01503,Berlin
+01504,Blackstone
+01505,Boylston
+01506,Brookfield
+01507,Charlton
+01508,Charlton City
+01509,Charlton Depot
+01510,Clinton
+01515,East Brookfield
+01516,Douglas
+01517,East Princeton
+01518,Fiskdale
+01519,Grafton
+01520,Holden
+01521,Holland
+01522,Jefferson
+01523,Lancaster
+01524,Leicester
+01525,Linwood
+01526,Manchaug
+01527,Millbury
+01529,Millville
+01531,New Braintree
+01532,Northborough
+01534,Northbridge
+01535,North Brookfield
+01536,North Grafton
+01537,North Oxford
+01538,North Uxbridge
+01540,Oxford
+01541,Princeton
+01542,Rochdale
+01543,Rutland
+01545,Shrewsbury
+01546,Shrewsbury
+01550,Southbridge
+01560,South Grafton
+01561,South Lancaster
+01562,Spencer
+01564,Sterling
+01566,Sturbridge
+01568,Upton
+01569,Uxbridge
+01570,Webster
+01571,Dudley
+01580,Westborough
+01581,Westborough
+01582,Westborough
+01583,West Boylston
+01585,West Brookfield
+01586,West Millbury
+01588,Whitinsville
+01590,Sutton
+01601,Worcester
+01602,Worcester
+01603,Worcester
+01604,Worcester
+01605,Worcester
+01606,Worcester
+01607,Worcester
+01608,Worcester
+01609,Worcester
+01610,Worcester
+01611,Cherry Valley
+01612,Paxton
+01613,Worcester
+01614,Worcester
+01615,Worcester
+01653,Worcester
+01654,Worcester
+01655,Worcester
+01701,Framingham
+01702,Framingham
+01703,Framingham
+01704,Framingham
+01705,Framingham
+01718,Village of Nagog Woods
+01719,Boxborough
+01720,Acton
+01721,Ashland
+01730,Bedford
+01731,Hanscom AFB
+01740,Bolton
+01741,Carlisle
+01742,Concord
+01745,Fayville
+01746,Holliston
+01747,Hopedale
+01748,Hopkinton
+01749,Hudson
+01752,Marlborough
+01754,Maynard
+01756,Mendon
+01757,Milford
+01760,Natick
+01770,Sherborn
+01772,Southborough
+01773,Lincoln
+01775,Stow
+01776,Sudbury
+01778,Wayland
+01784,Woodville
+01801,Woburn
+01803,Burlington
+01805,Burlington
+01806,Woburn
+01807,Woburn
+01808,Woburn
+01810,Andover
+01812,Andover
+01813,Woburn
+01815,Woburn
+01821,Billerica
+01822,Billerica
+01824,Chelmsford
+01826,Dracut
+01827,Dunstable
+01830,Haverhill
+01831,Haverhill
+01832,Haverhill
+01833,Georgetown
+01834,Groveland
+01835,Haverhill
+01840,Lawrence
+01841,Lawrence
+01842,Lawrence
+01843,Lawrence
+01844,Methuen
+01845,North Andover
+01850,Lowell
+01851,Lowell
+01852,Lowell
+01853,Lowell
+01854,Lowell
+01860,Merrimac
+01862,North Billerica
+01863,North Chelmsford
+01864,North Reading
+01865,Nutting Lake
+01866,Pinehurst
+01867,Reading
+01876,Tewksbury
+01879,Tyngsboro
+01880,Wakefield
+01885,West Boxford
+01886,Westford
+01887,Wilmington
+01888,Woburn
+01889,North Reading
+01890,Winchester
+01899,Andover
+01901,Lynn
+01902,Lynn
+01903,Lynn
+01904,Lynn
+01905,Lynn
+01906,Saugus
+01907,Swampscott
+01908,Nahant
+01910,Lynn
+01913,Amesbury
+01915,Beverly
+01921,Boxford
+01922,Byfield
+01923,Danvers
+01929,Essex
+01930,Gloucester
+01931,Gloucester
+01936,Hamilton
+01937,Hathorne
+01938,Ipswich
+01940,Lynnfield
+01944,Manchester
+01945,Marblehead
+01947,Salem
+01949,Middleton
+01950,Newburyport
+01951,Newbury
+01952,Salisbury
+01960,Peabody
+01961,Peabody
+01965,Prides Crossing
+01966,Rockport
+01969,Rowley
+01970,Salem
+01971,Salem
+01982,South Hamilton
+01983,Topsfield
+01984,Wenham
+01985,West Newbury
+02018,Accord
+02019,Bellingham
+02020,Brant Rock
+02021,Canton
+02025,Cohasset
+02026,Dedham
+02027,Dedham
+02030,Dover
+02031,East Mansfield
+02032,East Walpole
+02035,Foxboro
+02038,Franklin
+02040,Greenbush
+02041,Green Harbor
+02043,Hingham
+02044,Hingham
+02045,Hull
+02047,Humarock
+02048,Mansfield
+02050,Marshfield
+02051,Marshfield Hills
+02052,Medfield
+02053,Medway
+02054,Millis
+02055,Minot
+02056,Norfolk
+02059,North Marshfield
+02060,North Scituate
+02061,Norwell
+02062,Norwood
+02065,Ocean Bluff
+02066,Scituate
+02067,Sharon
+02070,Sheldonville
+02071,South Walpole
+02072,Stoughton
+02081,Walpole
+02090,Westwood
+02093,Wrentham
+02101,Boston
+02102,Boston
+02103,Boston
+02104,Boston
+02105,Boston
+02106,Boston
+02107,Boston
+02108,Boston
+02109,Boston
+02110,Boston
+02111,Boston
+02112,Boston
+02113,Boston
+02114,Boston
+02115,Boston
+02116,Boston
+02117,Boston
+02118,Boston
+02119,Boston
+02120,Boston
+02121,Boston
+02122,Boston
+02123,Boston
+02124,Boston
+02125,Boston
+02126,Mattapan
+02127,Boston
+02128,Boston
+02129,Charlestown
+02130,Jamaica Plain
+02131,Roslindale
+02132,West Roxbury
+02133,Boston
+02134,Allston
+02135,Brighton
+02136,Hyde Park
+02137,Readville
+02138,Cambridge
+02139,Cambridge
+02140,Cambridge
+02141,Cambridge
+02142,Cambridge
+02143,Somerville
+02144,Somerville
+02145,Somerville
+02146,Brookline
+02147,Brookline Village
+02148,Malden
+02149,Everett
+02150,Chelsea
+02151,Revere
+02152,Winthrop
+02153,Medford
+02154,Waltham
+02155,Medford
+02156,West Medford
+02157,Babson Park
+02158,Newton
+02159,Newton
+02160,Newton
+02161,Newton
+02162,Newton
+02163,Boston
+02164,Newton
+02165,Newton
+02166,Auburndale
+02167,Chestnut Hill
+02168,Waban
+02169,Quincy
+02170,Quincy
+02171,Quincy
+02172,Watertown
+02173,Lexington
+02174,Arlington
+02175,Arlington Heights
+02176,Melrose
+02177,Melrose
+02178,Belmont
+02179,Waverley
+02180,Stoneham
+02181,Wellesley
+02184,Braintree
+02185,Braintree
+02186,Milton
+02187,Milton Village
+02188,Weymouth
+02189,Weymouth
+02190,Weymouth
+02191,Weymouth
+02192,Needham
+02193,Weston
+02194,Needham
+02195,Newton
+02196,Boston
+02199,Boston
+02201,Boston
+02202,Boston
+02203,Boston
+02204,Boston
+02205,Boston
+02206,Boston
+02207,Boston
+02208,Boston
+02209,Boston
+02210,Boston
+02211,Boston
+02212,Boston
+02215,Boston
+02216,Boston
+02217,Boston
+02222,Boston
+02238,Cambridge
+02239,Cambridge
+02241,Boston
+02254,Waltham
+02258,Newton
+02266,Boston
+02269,Quincy
+02272,Watertown
+02277,Watertown
+02283,Boston
+02284,Boston
+02293,Boston
+02295,Boston
+02297,Boston
+02301,Brockton
+02302,Brockton
+02303,Brockton
+02304,Brockton
+02305,Brockton
+02322,Avon
+02324,Bridgewater
+02325,Bridgewater
+02327,Bryantville
+02330,Carver
+02331,Duxbury
+02332,Duxbury
+02333,East Bridgewater
+02334,Easton
+02337,Elmwood
+02338,Halifax
+02339,Hanover
+02341,Hanson
+02343,Holbrook
+02344,Middleboro
+02345,Manomet
+02346,Middleboro
+02347,Lakeville
+02348,Middleboro
+02349,Middleboro
+02350,Monponsett
+02351,Abington
+02355,North Carver
+02356,North Easton
+02357,North Easton
+02358,North Pembroke
+02359,Pembroke
+02360,Plymouth
+02361,Plymouth
+02362,Plymouth
+02364,Kingston
+02366,South Carver
+02367,Plympton
+02368,Randolph
+02370,Rockland
+02375,South Easton
+02379,West Bridgewater
+02381,White Horse Beach
+02382,Whitman
+02401,Brockton
+02402,Brockton
+02403,Brockton
+02404,Brockton
+02405,Brockton
+02420,Lexington
+02421,Lexington
+02445,Brookline
+02446,Brookline
+02447,Brookline Village
+02451,Waltham
+02452,Waltham
+02453,Waltham
+02454,Waltham
+02456,New Town
+02457,Babson Park
+02458,Newton
+02459,Newton Center
+02460,Newtonville
+02461,Newton Highlands
+02462,Newton Lower Falls
+02464,Newton Upper Falls
+02465,West Newton
+02466,Auburndale
+02467,Chestnut Hill
+02468,Waban
+02471,Watertown
+02472,Watertown
+02474,Arlington
+02475,Arlington Heights
+02476,Arlington
+02477,Watertown
+02478,Belmont
+02479,Waverley
+02481,Wellesley Hills
+02482,Wellesley
+02492,Needham
+02493,Weston
+02494,Needham Heights
+02495,Nonantum
+02532,Buzzards Bay
+02534,Cataumet
+02535,Chilmark
+02536,East Falmouth
+02537,East Sandwich
+02538,East Wareham
+02539,Edgartown
+02540,Falmouth
+02541,Falmouth
+02542,Buzzards Bay
+02543,Woods Hole
+02552,Menemsha
+02553,Monument Beach
+02554,Nantucket
+02556,North Falmouth
+02557,Oak Bluffs
+02558,Onset
+02559,Pocasset
+02561,Sagamore
+02562,Sagamore Beach
+02563,Sandwich
+02564,Siasconset
+02565,Silver Beach
+02568,Vineyard Haven
+02571,Wareham
+02573,Vineyard Haven
+02574,West Falmouth
+02575,West Tisbury
+02576,West Wareham
+02584,Nantucket
+02601,Hyannis
+02630,Barnstable
+02631,Brewster
+02632,Centerville
+02633,Chatham
+02634,Centerville
+02635,Cotuit
+02636,Centerville
+02637,Cummaquid
+02638,Dennis
+02639,Dennis Port
+02641,East Dennis
+02642,Eastham
+02643,East Orleans
+02644,Forestdale
+02645,Harwich
+02646,Harwich Port
+02647,Hyannis Port
+02648,Marstons Mills
+02649,Mashpee
+02650,North Chatham
+02651,North Eastham
+02652,North Truro
+02653,Orleans
+02655,Osterville
+02657,Provincetown
+02659,South Chatham
+02660,South Dennis
+02661,South Harwich
+02662,South Orleans
+02663,South Wellfleet
+02664,South Yarmouth
+02666,Truro
+02667,Wellfleet
+02668,West Barnstable
+02669,West Chatham
+02670,West Dennis
+02671,West Harwich
+02672,West Hyannisport
+02673,West Yarmouth
+02675,Yarmouth Port
+02702,Assonet
+02703,Attleboro
+02712,Chartley
+02713,Cuttyhunk
+02714,Dartmouth
+02715,Dighton
+02717,East Freetown
+02718,East Taunton
+02719,Fairhaven
+02720,Fall River
+02721,Fall River
+02722,Fall River
+02723,Fall River
+02724,Fall River
+02725,Somerset
+02726,Somerset
+02738,Marion
+02739,Mattapoisett
+02740,New Bedford
+02741,New Bedford
+02742,New Bedford
+02743,Acushnet
+02744,New Bedford
+02745,New Bedford
+02746,New Bedford
+02747,North Dartmouth
+02748,South Dartmouth
+02760,North Attleboro
+02761,North Attleboro
+02762,Plainville
+02763,Attleboro Falls
+02764,North Dighton
+02766,Norton
+02767,Raynham
+02768,Raynham Center
+02769,Rehoboth
+02770,Rochester
+02771,Seekonk
+02777,Swansea
+02779,Berkley
+02780,Taunton
+02783,Taunton
+02790,Westport
+02791,Westport Point
+05501,Andover
+05544,Andover
+70001,Metairie
+70002,Metairie
+70003,Metairie
+70004,Metairie
+70005,Metairie
+70006,Metairie
+70009,Metairie
+70010,Metairie
+70011,Metairie
+70030,Des Allemands
+70031,Ama
+70032,Arabi
+70033,Metairie
+70036,Barataria
+70037,Belle Chasse
+70038,Boothville
+70039,Boutte
+70040,Braithwaite
+70041,Buras
+70042,Carlisle
+70043,Chalmette
+70044,Chalmette
+70046,Davant
+70047,Destrehan
+70049,Edgard
+70050,Empire
+70051,Garyville
+70052,Gramercy
+70053,Gretna
+70054,Gretna
+70055,Metairie
+70056,Gretna
+70057,Hahnville
+70058,Harvey
+70059,Harvey
+70060,Metairie
+70062,Kenner
+70063,Kenner
+70064,Kenner
+70065,Kenner
+70066,Killona
+70067,Lafitte
+70068,La Place
+70069,La Place
+70070,Luling
+70071,Lutcher
+70072,Marrero
+70073,Marrero
+70075,Meraux
+70076,Mount Airy
+70078,New Sarpy
+70079,Norco
+70080,Paradis
+70081,Pilottown
+70082,Pointe A la Hache
+70083,Port Sulphur
+70084,Reserve
+70085,Saint Bernard
+70086,Saint James
+70087,Saint Rose
+70090,Vacherie
+70091,Venice
+70092,Violet
+70094,Westwego
+70096,Westwego
+70112,New Orleans
+70113,New Orleans
+70114,New Orleans
+70115,New Orleans
+70116,New Orleans
+70117,New Orleans
+70118,New Orleans
+70119,New Orleans
+70121,New Orleans
+70122,New Orleans
+70123,New Orleans
+70124,New Orleans
+70125,New Orleans
+70126,New Orleans
+70127,New Orleans
+70128,New Orleans
+70129,New Orleans
+70130,New Orleans
+70131,New Orleans
+70139,New Orleans
+70140,New Orleans
+70141,New Orleans
+70142,New Orleans
+70143,New Orleans
+70145,New Orleans
+70146,New Orleans
+70148,New Orleans
+70149,New Orleans
+70150,New Orleans
+70151,New Orleans
+70152,New Orleans
+70153,New Orleans
+70154,New Orleans
+70156,New Orleans
+70157,New Orleans
+70158,New Orleans
+70159,New Orleans
+70160,New Orleans
+70161,New Orleans
+70162,New Orleans
+70163,New Orleans
+70164,New Orleans
+70165,New Orleans
+70166,New Orleans
+70167,New Orleans
+70170,New Orleans
+70172,New Orleans
+70174,New Orleans
+70175,New Orleans
+70176,New Orleans
+70177,New Orleans
+70178,New Orleans
+70179,New Orleans
+70181,New Orleans
+70182,New Orleans
+70183,New Orleans
+70184,New Orleans
+70185,New Orleans
+70186,New Orleans
+70187,New Orleans
+70189,New Orleans
+70190,New Orleans
+70195,New Orleans
+70301,Thibodaux
+70302,Thibodaux
+70310,Thibodaux
+70339,Pierre Part
+70340,Amelia
+70341,Belle Rose
+70342,Berwick
+70343,Bourg
+70344,Chauvin
+70345,Cut Off
+70346,Donaldsonville
+70352,Donner
+70353,Dulac
+70354,Galliano
+70355,Gheens
+70356,Gibson
+70357,Golden Meadow
+70358,Grand Isle
+70359,Gray
+70360,Houma
+70361,Houma
+70363,Houma
+70364,Houma
+70371,Kraemer
+70372,Labadieville
+70373,Larose
+70374,Lockport
+70375,Mathews
+70376,Modeste
+70377,Montegut
+70380,Morgan City
+70381,Morgan City
+70390,Napoleonville
+70391,Paincourtville
+70392,Patterson
+70393,Plattenville
+70394,Raceland
+70395,Schriever
+70397,Theriot
+70401,Hammond
+70402,Hammond
+70403,Hammond
+70404,Hammond
+70420,Abita Springs
+70421,Akers
+70422,Amite
+70426,Angie
+70427,Bogalusa
+70429,Bogalusa
+70431,Bush
+70433,Covington
+70434,Covington
+70435,Covington
+70436,Fluker
+70437,Folsom
+70438,Franklinton
+70441,Greensburg
+70442,Husser
+70443,Independence
+70444,Kentwood
+70445,Lacombe
+70446,Loranger
+70447,Madisonville
+70448,Mandeville
+70449,Maurepas
+70450,Mount Hermon
+70451,Natalbany
+70452,Pearl River
+70453,Pine Grove
+70454,Ponchatoula
+70455,Robert
+70456,Roseland
+70457,Saint Benedict
+70458,Slidell
+70459,Slidell
+70460,Slidell
+70461,Slidell
+70462,Springfield
+70463,Sun
+70464,Talisheek
+70465,Tangipahoa
+70466,Tickfaw
+70467,Angie
+70469,Slidell
+70470,Mandeville
+70471,Mandeville
+70501,Lafayette
+70502,Lafayette
+70503,Lafayette
+70504,Lafayette
+70505,Lafayette
+70506,Lafayette
+70507,Lafayette
+70508,Lafayette
+70509,Lafayette
+70510,Abbeville
+70511,Abbeville
+70512,Arnaudville
+70513,Avery Island
+70514,Baldwin
+70515,Basile
+70516,Branch
+70517,Breaux Bridge
+70518,Broussard
+70519,Cade
+70520,Carencro
+70521,Cecilia
+70522,Centerville
+70523,Charenton
+70524,Chataignier
+70525,Church Point
+70526,Crowley
+70527,Crowley
+70528,Delcambre
+70529,Duson
+70531,Egan
+70532,Elton
+70533,Erath
+70534,Estherwood
+70535,Eunice
+70537,Evangeline
+70538,Franklin
+70540,Garden City
+70541,Grand Coteau
+70542,Gueydan
+70543,Iota
+70544,Jeanerette
+70546,Jennings
+70548,Kaplan
+70549,Lake Arthur
+70550,Lawtell
+70551,Leonville
+70552,Loreauville
+70554,Mamou
+70555,Maurice
+70556,Mermentau
+70558,Milton
+70559,Morse
+70560,New Iberia
+70562,New Iberia
+70563,New Iberia
+70569,Lydia
+70570,Opelousas
+70571,Opelousas
+70575,Perry
+70576,Pine Prairie
+70577,Port Barre
+70578,Rayne
+70580,Reddell
+70581,Roanoke
+70582,Saint Martinville
+70583,Scott
+70584,Sunset
+70585,Turkey Creek
+70586,Ville Platte
+70589,Washington
+70591,Welsh
+70592,Youngsville
+70593,Lafayette
+70596,Lafayette
+70598,Lafayette
+70601,Lake Charles
+70602,Lake Charles
+70605,Lake Charles
+70606,Lake Charles
+70607,Lake Charles
+70609,Lake Charles
+70611,Lake Charles
+70612,Lake Charles
+70615,Lake Charles
+70616,Lake Charles
+70629,Lake Charles
+70630,Bell City
+70631,Cameron
+70632,Creole
+70633,Dequincy
+70634,Deridder
+70637,Dry Creek
+70638,Elizabeth
+70639,Evans
+70640,Fenton
+70642,Fullerton
+70643,Grand Chenier
+70644,Grant
+70645,Hackberry
+70646,Hayes
+70647,Iowa
+70648,Kinder
+70650,Lacassine
+70651,Leblanc
+70652,Longville
+70653,Merryville
+70654,Mittie
+70655,Oberlin
+70656,Pitkin
+70657,Ragley
+70658,Reeves
+70659,Rosepine
+70660,Singer
+70661,Starks
+70662,Sugartown
+70663,Sulphur
+70664,Sulphur
+70665,Sulphur
+70668,Vinton
+70669,Westlake
+70704,Baker
+70706,Denham Springs
+70707,Gonzales
+70710,Addis
+70711,Albany
+70712,Angola
+70714,Baker
+70715,Batchelor
+70716,Bayou Goula
+70717,Blanks
+70718,Brittany
+70719,Brusly
+70720,Bueche
+70721,Carville
+70722,Clinton
+70723,Convent
+70725,Darrow
+70726,Denham Springs
+70727,Denham Springs
+70728,Duplessis
+70729,Erwinville
+70730,Ethel
+70732,Fordoche
+70733,French Settlement
+70734,Geismar
+70736,Glynn
+70737,Gonzales
+70738,Burnside
+70739,Greenwell Springs
+70740,Grosse Tete
+70743,Hester
+70744,Holden
+70747,Innis
+70748,Jackson
+70749,Jarreau
+70750,Krotz Springs
+70751,Labarre
+70752,Lakeland
+70753,Lettsworth
+70754,Livingston
+70755,Livonia
+70756,Lottie
+70757,Maringouin
+70759,Morganza
+70760,New Roads
+70761,Norwood
+70762,Oscar
+70763,Paulina
+70764,Plaquemine
+70765,Plaquemine
+70767,Port Allen
+70769,Prairieville
+70770,Pride
+70772,Rosedale
+70773,Rougon
+70774,Saint Amant
+70775,Saint Francisville
+70776,Saint Gabriel
+70777,Slaughter
+70778,Sorrento
+70780,Sunshine
+70781,Torbert
+70782,Tunica
+70783,Ventress
+70784,Wakefield
+70785,Walker
+70786,Watson
+70787,Weyanoke
+70788,White Castle
+70789,Wilson
+70791,Zachary
+70792,Uncle Sam
+70801,Baton Rouge
+70802,Baton Rouge
+70803,Baton Rouge
+70804,Baton Rouge
+70805,Baton Rouge
+70806,Baton Rouge
+70807,Baton Rouge
+70808,Baton Rouge
+70809,Baton Rouge
+70810,Baton Rouge
+70811,Baton Rouge
+70812,Baton Rouge
+70813,Baton Rouge
+70814,Baton Rouge
+70815,Baton Rouge
+70816,Baton Rouge
+70817,Baton Rouge
+70818,Baton Rouge
+70819,Baton Rouge
+70820,Baton Rouge
+70821,Baton Rouge
+70822,Baton Rouge
+70823,Baton Rouge
+70825,Baton Rouge
+70826,Baton Rouge
+70827,Baton Rouge
+70831,Baton Rouge
+70833,Baton Rouge
+70835,Baton Rouge
+70836,Baton Rouge
+70837,Baton Rouge
+70874,Baton Rouge
+70879,Baton Rouge
+70883,Baton Rouge
+70884,Baton Rouge
+70892,Baton Rouge
+70893,Baton Rouge
+70894,Baton Rouge
+70895,Baton Rouge
+70896,Baton Rouge
+70898,Baton Rouge
+71001,Arcadia
+71002,Ashland
+71003,Athens
+71004,Belcher
+71006,Benton
+71007,Bethany
+71008,Bienville
+71009,Blanchard
+71016,Castor
+71018,Cotton Valley
+71019,Coushatta
+71021,Cullen
+71023,Doyline
+71024,Dubberly
+71025,East Point
+71027,Frierson
+71028,Gibsland
+71029,Gilliam
+71030,Gloster
+71031,Goldonna
+71032,Grand Cane
+71033,Greenwood
+71034,Hall Summit
+71036,Harmon
+71037,Haughton
+71038,Haynesville
+71039,Heflin
+71040,Homer
+71043,Hosston
+71044,Ida
+71045,Jamestown
+71046,Keatchie
+71047,Keithville
+71048,Lisbon
+71049,Logansport
+71050,Longstreet
+71051,Elm Grove
+71052,Mansfield
+71055,Minden
+71058,Minden
+71060,Mooringsport
+71061,Oil City
+71063,Pelican
+71064,Plain Dealing
+71065,Pleasant Hill
+71066,Powhatan
+71067,Princeton
+71068,Ringgold
+71069,Rodessa
+71070,Saline
+71071,Sarepta
+71072,Shongaloo
+71073,Sibley
+71075,Springhill
+71078,Stonewall
+71079,Summerfield
+71080,Taylor
+71082,Vivian
+71101,Shreveport
+71102,Shreveport
+71103,Shreveport
+71104,Shreveport
+71105,Shreveport
+71106,Shreveport
+71107,Shreveport
+71108,Shreveport
+71109,Shreveport
+71110,Barksdale AFB
+71111,Bossier City
+71112,Bossier City
+71113,Bossier City
+71115,Shreveport
+71118,Shreveport
+71119,Shreveport
+71120,Shreveport
+71129,Shreveport
+71130,Shreveport
+71133,Shreveport
+71134,Shreveport
+71135,Shreveport
+71136,Shreveport
+71137,Shreveport
+71138,Shreveport
+71148,Shreveport
+71149,Shreveport
+71151,Shreveport
+71152,Shreveport
+71153,Shreveport
+71154,Shreveport
+71156,Shreveport
+71161,Shreveport
+71162,Shreveport
+71163,Shreveport
+71164,Shreveport
+71165,Shreveport
+71166,Shreveport
+71171,Bossier City
+71172,Bossier City
+71201,Monroe
+71202,Monroe
+71203,Monroe
+71207,Monroe
+71208,Monroe
+71209,Monroe
+71210,Monroe
+71211,Monroe
+71212,Monroe
+71213,Monroe
+71218,Archibald
+71219,Baskin
+71220,Bastrop
+71221,Bastrop
+71222,Bernice
+71223,Bonita
+71225,Calhoun
+71226,Chatham
+71227,Choudrant
+71229,Collinston
+71230,Crowville
+71232,Delhi
+71233,Delta
+71234,Downsville
+71235,Dubach
+71237,Epps
+71238,Eros
+71240,Fairbanks
+71241,Farmerville
+71242,Forest
+71243,Fort Necessity
+71245,Grambling
+71247,Hodge
+71249,Jigger
+71250,Jones
+71251,Jonesboro
+71253,Kilbourne
+71254,Lake Providence
+71256,Lillie
+71259,Mangham
+71260,Marion
+71261,Mer Rouge
+71263,Oak Grove
+71264,Oak Ridge
+71266,Pioneer
+71268,Quitman
+71269,Rayville
+71270,Ruston
+71272,Ruston
+71273,Ruston
+71275,Simsboro
+71276,Sondheimer
+71277,Spearsville
+71279,Start
+71280,Sterlington
+71281,Swartz
+71282,Tallulah
+71284,Tallulah
+71286,Transylvania
+71291,West Monroe
+71292,West Monroe
+71294,West Monroe
+71295,Winnsboro
+71301,Alexandria
+71302,Alexandria
+71303,Alexandria
+71306,Alexandria
+71307,Alexandria
+71309,Alexandria
+71315,Alexandria
+71316,Acme
+71320,Bordelonville
+71322,Bunkie
+71323,Center Point
+71324,Chase
+71325,Cheneyville
+71326,Clayton
+71327,Cottonport
+71328,Deville
+71329,Dupont
+71330,Echo
+71331,Effie
+71333,Evergreen
+71334,Ferriday
+71336,Gilbert
+71339,Hamburg
+71340,Harrisonburg
+71341,Hessmer
+71342,Jena
+71343,Jonesville
+71345,Lebeau
+71346,Lecompte
+71348,Libuse
+71350,Mansura
+71351,Marksville
+71353,Melville
+71354,Monterey
+71355,Moreauville
+71356,Morrow
+71357,Newellton
+71358,Palmetto
+71359,Pineville
+71360,Pineville
+71361,Pineville
+71362,Plaucheville
+71363,Rhinehart
+71365,Ruby
+71366,Saint Joseph
+71367,Saint Landry
+71368,Sicily Island
+71369,Simmesport
+71371,Trout
+71373,Vidalia
+71375,Waterproof
+71377,Wildsville
+71378,Wisner
+71401,Aimwell
+71403,Anacoco
+71404,Atlanta
+71405,Ball
+71406,Belmont
+71407,Bentley
+71409,Boyce
+71410,Calvin
+71411,Campti
+71414,Clarence
+71415,Clarks
+71416,Cloutierville
+71417,Colfax
+71418,Columbia
+71419,Converse
+71422,Dodson
+71423,Dry Prong
+71424,Elmer
+71425,Enterprise
+71426,Fisher
+71427,Flatwoods
+71428,Flora
+71429,Florien
+71430,Forest Hill
+71431,Gardner
+71432,Georgetown
+71433,Glenmora
+71434,Gorum
+71435,Grayson
+71438,Hineston
+71439,Hornbeck
+71440,Joyce
+71441,Kelly
+71443,Kurthwood
+71444,Lacamp
+71446,Leesville
+71447,Lena
+71448,Longleaf
+71449,Many
+71450,Marthaville
+71452,Melrose
+71454,Montgomery
+71455,Mora
+71456,Natchez
+71457,Natchitoches
+71458,Natchitoches
+71459,Leesville
+71460,Negreet
+71461,Newllano
+71462,Noble
+71463,Oakdale
+71465,Olla
+71466,Otis
+71467,Pollock
+71468,Provencal
+71469,Robeline
+71471,Saint Maurice
+71472,Sieper
+71473,Sikes
+71474,Simpson
+71475,Slagle
+71477,Tioga
+71479,Tullos
+71480,Urania
+71481,Verda
+71483,Winnfield
+71485,Woodworth
+71486,Zwolle
+71496,Leesville
+71497,Natchitoches
+40003,Bagdad
+40004,Bardstown
+40006,Bedford
+40007,Bethlehem
+40008,Bloomfield
+40009,Bradfordsville
+40010,Buckner
+40011,Campbellsburg
+40012,Chaplin
+40013,Coxs Creek
+40014,Crestwood
+40018,Eastwood
+40019,Eminence
+40020,Fairfield
+40022,Finchville
+40023,Fisherville
+40025,Glenview
+40026,Goshen
+40027,Harrods Creek
+40031,La Grange
+40032,La Grange
+40033,Lebanon
+40036,Lockport
+40037,Loretto
+40040,Mackville
+40041,Masonic Home
+40045,Milton
+40046,Mount Eden
+40047,Mount Washington
+40048,Nazareth
+40049,Nerinx
+40050,New Castle
+40051,New Haven
+40052,New Hope
+40055,Pendleton
+40056,Pewee Valley
+40057,Pleasureville
+40058,Port Royal
+40059,Prospect
+40060,Raywick
+40061,Saint Catharine
+40062,Saint Francis
+40063,Saint Mary
+40065,Shelbyville
+40066,Shelbyville
+40067,Simpsonville
+40068,Smithfield
+40069,Springfield
+40070,Sulphur
+40071,Taylorsville
+40075,Turners Station
+40076,Waddy
+40077,Westport
+40078,Willisburg
+40104,Battletown
+40107,Boston
+40108,Brandenburg
+40109,Brooks
+40110,Clermont
+40111,Cloverport
+40115,Custer
+40117,Ekron
+40118,Fairdale
+40119,Falls of Rough
+40121,Fort Knox
+40140,Garfield
+40142,Guston
+40143,Hardinsburg
+40144,Harned
+40145,Hudson
+40146,Irvington
+40150,Lebanon Junction
+40152,Mc Daniels
+40153,Mc Quady
+40155,Muldraugh
+40157,Payneville
+40159,Radcliff
+40160,Radcliff
+40161,Rhodelia
+40162,Rineyville
+40164,SE Ree
+40165,Shepherdsville
+40170,Stephensport
+40171,Union Star
+40175,Vine Grove
+40176,Webster
+40177,West Point
+40178,Westview
+40201,Louisville
+40202,Louisville
+40203,Louisville
+40204,Louisville
+40205,Louisville
+40206,Louisville
+40207,Louisville
+40208,Louisville
+40209,Louisville
+40210,Louisville
+40211,Louisville
+40212,Louisville
+40213,Louisville
+40214,Louisville
+40215,Louisville
+40216,Louisville
+40217,Louisville
+40218,Louisville
+40219,Louisville
+40220,Louisville
+40221,Louisville
+40222,Louisville
+40223,Louisville
+40224,Louisville
+40225,Louisville
+40228,Louisville
+40229,Louisville
+40231,Louisville
+40232,Louisville
+40233,Louisville
+40241,Louisville
+40242,Louisville
+40243,Louisville
+40245,Louisville
+40250,Louisville
+40251,Louisville
+40252,Louisville
+40253,Louisville
+40255,Louisville
+40256,Louisville
+40257,Louisville
+40258,Louisville
+40259,Louisville
+40261,Louisville
+40266,Louisville
+40268,Louisville
+40269,Louisville
+40270,Louisville
+40272,Louisville
+40280,Louisville
+40281,Louisville
+40282,Louisville
+40283,Louisville
+40285,Louisville
+40287,Louisville
+40289,Louisville
+40290,Louisville
+40291,Louisville
+40292,Louisville
+40293,Louisville
+40294,Louisville
+40295,Louisville
+40296,Louisville
+40297,Louisville
+40298,Louisville
+40299,Louisville
+40310,Burgin
+40311,Carlisle
+40312,Clay City
+40313,Clearfield
+40316,Denniston
+40317,Elliottville
+40319,Farmers
+40320,Ford
+40322,Frenchburg
+40324,Georgetown
+40328,Gravel Switch
+40329,Haldeman
+40330,Harrodsburg
+40334,Hope
+40336,Irvine
+40337,Jeffersonville
+40339,Keene
+40340,Nicholasville
+40342,Lawrenceburg
+40346,Means
+40347,Midway
+40348,Millersburg
+40350,Moorefield
+40351,Morehead
+40353,Mount Sterling
+40355,New Liberty
+40356,Nicholasville
+40357,North Middletown
+40358,Olympia
+40359,Owenton
+40360,Owingsville
+40361,Paris
+40362,Paris
+40363,Perry Park
+40366,Preston
+40370,Sadieville
+40371,Salt Lick
+40372,Salvisa
+40374,Sharpsburg
+40376,Slade
+40379,Stamping Ground
+40380,Stanton
+40383,Versailles
+40384,Versailles
+40385,Waco
+40386,Versailles
+40387,Wellington
+40390,Wilmore
+40391,Winchester
+40392,Winchester
+40402,Annville
+40403,Berea
+40404,Berea
+40405,Bighill
+40409,Brodhead
+40410,Bryantsville
+40419,Crab Orchard
+40421,Dabolt
+40422,Danville
+40423,Danville
+40434,Gray Hawk
+40437,Hustonville
+40440,Junction City
+40442,Kings Mountain
+40444,Lancaster
+40445,Livingston
+40446,Lancaster
+40447,Mc Kee
+40448,Mc Kinney
+40452,Mitchellsburg
+40456,Mount Vernon
+40460,Orlando
+40461,Paint Lick
+40464,Parksville
+40467,Peoples
+40468,Perryville
+40472,Ravenna
+40473,Renfro Valley
+40475,Richmond
+40476,Richmond
+40481,Sandgap
+40484,Stanford
+40486,Tyner
+40488,Waneta
+40489,Waynesburg
+40492,Wildie
+40495,Winston
+40501,Lexington
+40502,Lexington
+40503,Lexington
+40504,Lexington
+40505,Lexington
+40506,Lexington
+40507,Lexington
+40508,Lexington
+40509,Lexington
+40510,Lexington
+40511,Lexington
+40512,Lexington
+40513,Lexington
+40514,Lexington
+40515,Lexington
+40516,Lexington
+40517,Lexington
+40522,Lexington
+40523,Lexington
+40524,Lexington
+40526,Lexington
+40533,Lexington
+40536,Lexington
+40544,Lexington
+40546,Lexington
+40550,Lexington
+40555,Lexington
+40574,Lexington
+40575,Lexington
+40576,Lexington
+40577,Lexington
+40578,Lexington
+40579,Lexington
+40580,Lexington
+40581,Lexington
+40582,Lexington
+40583,Lexington
+40584,Lexington
+40585,Lexington
+40586,Lexington
+40587,Lexington
+40588,Lexington
+40589,Lexington
+40590,Lexington
+40591,Lexington
+40592,Lexington
+40593,Lexington
+40594,Lexington
+40595,Lexington
+40596,Lexington
+40601,Frankfort
+40602,Frankfort
+40603,Frankfort
+40604,Frankfort
+40618,Frankfort
+40619,Frankfort
+40620,Frankfort
+40621,Frankfort
+40622,Frankfort
+40701,Corbin
+40702,Corbin
+40724,Bush
+40729,East Bernstadt
+40730,Emlyn
+40734,Gray
+40737,Keavy
+40740,Lily
+40741,London
+40742,London
+40743,London
+40744,London
+40745,London
+40746,London
+40747,London
+40748,London
+40751,Marydell
+40754,Nevisdale
+40755,Pittsburg
+40759,Rockholds
+40763,Siler
+40769,Williamsburg
+40771,Woodbine
+40801,Ages Brookside
+40803,Asher
+40806,Baxter
+40807,Benham
+40808,Big Laurel
+40810,Bledsoe
+40813,Calvin
+40815,Cawood
+40816,Chappell
+40818,Coalgood
+40819,Coldiron
+40820,Cranks
+40823,Cumberland
+40824,Dayhoit
+40826,Eolia
+40827,Essie
+40828,Evarts
+40829,Grays Knob
+40830,Gulston
+40831,Harlan
+40840,Helton
+40843,Holmes Mill
+40844,Hoskinston
+40845,Hulen
+40847,Kenvir
+40849,Lejunior
+40854,Loyall
+40855,Lynch
+40856,Miracle
+40858,Mozelle
+40862,Partridge
+40863,Pathfork
+40865,Putney
+40867,Smith
+40868,Stinnett
+40870,Totz
+40873,Wallins Creek
+40874,Warbranch
+40902,Arjay
+40903,Artemus
+40906,Barbourville
+40913,Beverly
+40914,Big Creek
+40915,Bimble
+40921,Bryants Store
+40923,Cannon
+40927,Closplint
+40930,Dewitt
+40931,Eriline
+40932,Fall Rock
+40935,Flat Lick
+40939,Fourmile
+40940,Frakes
+40941,Garrard
+40943,Girdler
+40944,Goose Rock
+40946,Green Road
+40949,Heidrick
+40951,Hima
+40953,Hinkle
+40955,Ingram
+40958,Kettle Island
+40962,Manchester
+40964,Mary Alice
+40965,Middlesboro
+40972,Oneida
+40977,Pineville
+40979,Roark
+40981,Saul
+40982,Scalf
+40983,Sextons Creek
+40988,Stoney Fork
+40995,Trosper
+40997,Walker
+40999,Woollum
+41001,Alexandria
+41002,Augusta
+41003,Berry
+41004,Brooksville
+41005,Burlington
+41006,Butler
+41007,California
+41008,Carrollton
+41010,Corinth
+41011,Covington
+41012,Covington
+41014,Covington
+41015,Latonia
+41016,Covington
+41017,Ft Mitchell
+41018,Erlanger
+41019,Covington
+41022,Florence
+41030,Crittenden
+41031,Cynthiana
+41033,De Mossville
+41034,Dover
+41035,Dry Ridge
+41037,Elizaville
+41039,Ewing
+41040,Falmouth
+41041,Flemingsburg
+41042,Florence
+41043,Foster
+41044,Germantown
+41045,Ghent
+41046,Glencoe
+41048,Hebron
+41049,Hillsboro
+41051,Independence
+41052,Jonesville
+41053,Kenton
+41054,Mason
+41055,Mayslick
+41056,Maysville
+41059,Melbourne
+41061,Milford
+41062,Minerva
+41063,Morning View
+41064,Mount Olivet
+41065,Muses Mills
+41071,Newport
+41072,Newport
+41073,Bellevue
+41074,Dayton
+41075,Fort Thomas
+41076,Newport
+41080,Petersburg
+41081,Plummers Landing
+41083,Sanders
+41085,Silver Grove
+41086,Sparta
+41091,Union
+41092,Verona
+41093,Wallingford
+41094,Walton
+41095,Warsaw
+41096,Washington
+41097,Williamstown
+41098,Worthville
+41099,Newport
+41101,Ashland
+41102,Ashland
+41105,Ashland
+41114,Ashland
+41121,Argillite
+41124,Blaine
+41127,Camp Dix
+41128,Carter
+41129,Catlettsburg
+41132,Denton
+41135,Emerson
+41137,Firebrick
+41139,Flatwoods
+41141,Garrison
+41142,Grahn
+41143,Grayson
+41144,Greenup
+41146,Hitchins
+41149,Isonville
+41150,Jacobs
+41156,Lloyd
+41159,Martha
+41160,Mazie
+41164,Olive Hill
+41166,Quincy
+41168,Rush
+41169,Russell
+41170,Saint Paul
+41171,Sandy Hook
+41173,Soldier
+41174,South Portsmouth
+41175,South Shore
+41179,Vanceburg
+41180,Webbville
+41181,Willard
+41183,Worthington
+41189,Tollesboro
+41201,Adams
+41203,Beauty
+41204,Boons Camp
+41214,Debord
+41215,Denver
+41216,East Point
+41219,Flatgap
+41222,Hagerhill
+41224,Inez
+41226,Keaton
+41228,Leander
+41230,Louisa
+41231,Lovely
+41232,Lowmansville
+41234,Meally
+41238,Oil Springs
+41240,Paintsville
+41250,Pilgrim
+41254,River
+41255,Sitka
+41256,Staffordsville
+41257,Stambaugh
+41260,Thelma
+41262,Tomahawk
+41263,Tutor Key
+41264,Ulysses
+41265,Van Lear
+41267,Warfield
+41268,West Van Lear
+41271,Williamsport
+41274,Wittensville
+41301,Campton
+41307,Athol
+41310,Bays
+41311,Beattyville
+41313,Bethany
+41314,Booneville
+41317,Clayhole
+41332,Hazel Green
+41333,Heidelberg
+41338,Island City
+41339,Jackson
+41342,Lee City
+41344,Lerose
+41347,Lone
+41348,Lost Creek
+41351,Mistletoe
+41352,Mize
+41360,Pine Ridge
+41362,Primrose
+41364,Ricetown
+41365,Rogers
+41366,Rousseau
+41367,Rowdy
+41368,Saint Helens
+41377,Talbert
+41385,Vancleve
+41386,Vincent
+41390,Whick
+41397,Zoe
+41408,Cannel City
+41410,Cisco
+41413,Crockett
+41419,Edna
+41421,Elkfork
+41422,Elsie
+41425,Ezel
+41426,Falcon
+41433,Gapville
+41444,Ivyton
+41451,Malone
+41459,Ophir
+41464,Royalton
+41465,Salyersville
+41472,West Liberty
+41477,Wrigley
+41501,Pikeville
+41502,Pikeville
+41503,South Williamson
+41512,Ashcamp
+41513,Belcher
+41514,Belfry
+41517,Burdine
+41519,Canada
+41520,Dorton
+41522,Elkhorn City
+41524,Fedscreek
+41526,Fords Branch
+41527,Forest Hills
+41528,Freeburn
+41531,Hardy
+41534,Hellier
+41535,Huddy
+41537,Jenkins
+41538,Jonancy
+41539,Kimper
+41540,Lick Creek
+41542,Lookout
+41543,Mc Andrews
+41544,Mc Carr
+41546,Mc Veigh
+41547,Majestic
+41548,Mouthcard
+41549,Myra
+41553,Phelps
+41554,Phyllis
+41555,Pinsonfork
+41557,Raccoon
+41558,Ransom
+41559,Regina
+41560,Robinson Creek
+41561,Rockhouse
+41562,Shelbiana
+41563,Shelby Gap
+41564,Sidney
+41566,Steele
+41567,Stone
+41568,Stopover
+41569,Toler
+41571,Varney
+41572,Virgie
+41601,Allen
+41602,Auxier
+41603,Banner
+41604,Beaver
+41605,Betsy Layne
+41606,Bevinsville
+41607,Blue River
+41612,Bypro
+41615,Dana
+41616,David
+41619,Drift
+41621,Dwale
+41622,Eastern
+41630,Garrett
+41631,Grethel
+41632,Gunlock
+41635,Harold
+41636,Hi Hat
+41640,Hueysville
+41642,Ivel
+41643,Lackey
+41645,Langley
+41647,Mc Dowell
+41649,Martin
+41650,Melvin
+41651,Minnie
+41653,Prestonsburg
+41655,Printer
+41659,Stanville
+41660,Teaberry
+41663,Tram
+41666,Wayland
+41667,Weeksbury
+41668,West Prestonsburg
+41669,Wheelwright
+41701,Hazard
+41702,Hazard
+41712,Ary
+41713,Avawam
+41714,Bear Branch
+41719,Bonnyman
+41721,Buckhorn
+41722,Bulan
+41723,Busy
+41725,Carrie
+41727,Chavies
+41729,Combs
+41730,Confluence
+41731,Cornettsville
+41735,Delphia
+41736,Dice
+41739,Dwarf
+41740,Emmalena
+41743,Fisty
+41745,Gays Creek
+41746,Happy
+41747,Hardburly
+41749,Hyden
+41751,Jeff
+41754,Krypton
+41759,Sassafras
+41760,Scuddy
+41762,Sizerock
+41763,Slemp
+41764,Smilax
+41766,Thousandsticks
+41772,Vest
+41773,Vicco
+41774,Viper
+41775,Wendover
+41776,Wooton
+41777,Yeaddiss
+41778,Yerkes
+41804,Blackey
+41810,Cromona
+41812,Deane
+41815,Ermine
+41817,Garner
+41819,Gordon
+41821,Hallie
+41822,Hindman
+41824,Isom
+41825,Jackhorn
+41826,Jeremiah
+41828,Kite
+41831,Leburn
+41832,Letcher
+41833,Linefork
+41834,Littcarr
+41835,Mc Roberts
+41836,Mallie
+41837,Mayking
+41838,Millstone
+41839,Mousie
+41840,Neon
+41843,Pine Top
+41844,Pippa Passes
+41845,Premium
+41847,Redfox
+41848,Roxana
+41849,Seco
+41855,Thornton
+41858,Whitesburg
+41859,Dema
+41861,Raven
+41862,Topmost
+42001,Paducah
+42002,Paducah
+42003,Paducah
+42020,Almo
+42021,Arlington
+42022,Bandana
+42023,Bardwell
+42024,Barlow
+42025,Benton
+42027,Boaz
+42028,Burna
+42029,Calvert City
+42031,Clinton
+42032,Columbus
+42033,Crayne
+42035,Cunningham
+42036,Dexter
+42037,Dycusburg
+42038,Eddyville
+42039,Fancy Farm
+42040,Farmington
+42041,Fulton
+42044,Gilbertsville
+42045,Grand Rivers
+42046,Hamlin
+42047,Hampton
+42048,Hardin
+42049,Hazel
+42050,Hickman
+42051,Hickory
+42053,Kevil
+42054,Kirksey
+42055,Kuttawa
+42056,La Center
+42058,Ledbetter
+42060,Lovelaceville
+42061,Lowes
+42063,Lynnville
+42064,Marion
+42066,Mayfield
+42069,Melber
+42070,Milburn
+42071,Murray
+42076,New Concord
+42078,Salem
+42079,Sedalia
+42081,Smithland
+42082,Symsonia
+42083,Tiline
+42084,Tolu
+42085,Water Valley
+42086,West Paducah
+42087,Wickliffe
+42088,Wingo
+42101,Bowling Green
+42102,Bowling Green
+42103,Bowling Green
+42104,Bowling Green
+42120,Adolphus
+42122,Alvaton
+42123,Austin
+42124,Beaumont
+42127,Cave City
+42128,Drake
+42129,Edmonton
+42130,Eighty Eight
+42131,Etoile
+42133,Fountain Run
+42134,Franklin
+42135,Franklin
+42140,Gamaliel
+42141,Glasgow
+42142,Glasgow
+42150,Halfway
+42151,Hestand
+42152,Hiseville
+42153,Holland
+42154,Knob Lick
+42156,Lucas
+42157,Mount Hermon
+42159,Oakland
+42160,Park City
+42163,Rocky Hill
+42164,Scottsville
+42166,Summer Shade
+42167,Tompkinsville
+42170,Woodburn
+42171,Smiths Grove
+42201,Aberdeen
+42202,Adairville
+42203,Allegre
+42204,Allensville
+42206,Auburn
+42207,Bee Spring
+42209,Brooklyn
+42210,Brownsville
+42211,Cadiz
+42214,Center
+42215,Cerulean
+42216,Clifty
+42217,Crofton
+42219,Dunbar
+42220,Elkton
+42221,Fairview
+42223,Fort Campbell
+42232,Gracey
+42234,Guthrie
+42235,Hadley
+42236,Herndon
+42240,Hopkinsville
+42241,Hopkinsville
+42251,Huntsville
+42252,Jetson
+42254,La Fayette
+42256,Lewisburg
+42257,Lindseyville
+42259,Mammoth Cave
+42261,Morgantown
+42262,Oak Grove
+42265,Olmstead
+42266,Pembroke
+42267,Provo
+42270,Richardsville
+42273,Rochester
+42274,Rockfield
+42275,Roundhill
+42276,Russellville
+42280,Sharon Grove
+42283,South Union
+42285,Sweeden
+42286,Trenton
+42287,Welchs Creek
+42288,Woodbury
+42301,Owensboro
+42302,Owensboro
+42303,Owensboro
+42304,Owensboro
+42320,Beaver Dam
+42321,Beech Creek
+42322,Beech Grove
+42323,Beechmont
+42324,Belton
+42325,Bremen
+42326,Browder
+42327,Calhoun
+42328,Centertown
+42330,Central City
+42332,Cleaton
+42333,Cromwell
+42334,Curdsville
+42337,Drakesboro
+42338,Dundee
+42339,Dunmor
+42343,Fordsville
+42344,Graham
+42345,Greenville
+42347,Hartford
+42348,Hawesville
+42349,Horse Branch
+42350,Island
+42351,Lewisport
+42352,Livermore
+42354,Mc Henry
+42355,Maceo
+42356,Maple Mount
+42361,Olaton
+42364,Pellville
+42365,Penrod
+42366,Philpot
+42367,Powderly
+42368,Reynolds Station
+42369,Rockport
+42370,Rosine
+42371,Rumsey
+42372,Sacramento
+42374,South Carrollton
+42375,Stanley
+42376,Utica
+42377,West Louisville
+42378,Whitesville
+42402,Baskett
+42403,Blackford
+42404,Clay
+42406,Corydon
+42408,Dawson Springs
+42409,Dixon
+42410,Earlington
+42411,Fredonia
+42413,Hanson
+42419,Henderson
+42420,Henderson
+42431,Madisonville
+42436,Manitou
+42437,Morganfield
+42440,Mortons Gap
+42441,Nebo
+42442,Nortonville
+42444,Poole
+42445,Princeton
+42450,Providence
+42451,Reed
+42452,Robards
+42453,Saint Charles
+42455,Sebree
+42456,Slaughters
+42457,Smith Mills
+42458,Spottsville
+42459,Sturgis
+42460,Sullivan
+42461,Uniontown
+42462,Waverly
+42463,Wheatcroft
+42464,White Plains
+42501,Somerset
+42502,Somerset
+42503,Somerset
+42516,Bethelridge
+42518,Bronston
+42519,Burnside
+42528,Dunnville
+42533,Ferguson
+42539,Liberty
+42541,Middleburg
+42544,Nancy
+42553,Science Hill
+42558,Tateville
+42564,West Somerset
+42565,Windsor
+42566,Yosemite
+42567,Eubank
+42602,Albany
+42603,Alpha
+42629,Jamestown
+42631,Marshes Siding
+42632,Mill Springs
+42633,Monticello
+42634,Parkers Lake
+42635,Pine Knot
+42638,Revelo
+42642,Russell Springs
+42647,Stearns
+42649,Strunk
+42653,Whitley City
+42701,Elizabethtown
+42702,Elizabethtown
+42711,Bakerton
+42712,Big Clifty
+42713,Bonnieville
+42715,Breeding
+42716,Buffalo
+42717,Burkesville
+42718,Campbellsville
+42719,Campbellsville
+42720,Cane Valley
+42721,Caneyville
+42722,Canmer
+42724,Cecilia
+42726,Clarkson
+42728,Columbia
+42729,Cub Run
+42731,Dubre
+42732,Eastview
+42733,Elk Horn
+42735,Fairplay
+42740,Glendale
+42741,Glens Fork
+42742,Gradyville
+42743,Greensburg
+42746,Hardyville
+42748,Hodgenville
+42749,Horse Cave
+42753,Knifley
+42754,Leitchfield
+42755,Leitchfield
+42757,Magnolia
+42758,Mannsville
+42759,Marrowbone
+42761,Milltown
+42762,Millwood
+42764,Mount Sherman
+42765,Munfordville
+42776,Sonora
+42782,Summersville
+42783,Summit
+42784,Upton
+42786,Waterview
+42788,White Mills
+66002,Atchison
+66006,Baldwin City
+66007,Basehor
+66008,Bendena
+66010,Blue Mound
+66012,Bonner Springs
+66013,Bucyrus
+66014,Centerville
+66015,Colony
+66016,Cummings
+66017,Denton
+66018,De Soto
+66019,Clearview City
+66020,Easton
+66021,Edgerton
+66023,Effingham
+66024,Elwood
+66025,Eudora
+66026,Fontana
+66027,Fort Leavenworth
+66030,Gardner
+66031,New Century
+66032,Garnett
+66033,Greeley
+66035,Highland
+66036,Hillsdale
+66039,Kincaid
+66040,La Cygne
+66041,Lancaster
+66042,Lane
+66043,Lansing
+66044,Lawrence
+66045,Lawrence
+66046,Lawrence
+66047,Lawrence
+66048,Leavenworth
+66049,Lawrence
+66050,Lecompton
+66051,Olathe
+66052,Linwood
+66053,Louisburg
+66054,Mc Louth
+66056,Mound City
+66058,Muscotah
+66060,Nortonville
+66061,Olathe
+66062,Olathe
+66063,Olathe
+66064,Osawatomie
+66066,Oskaloosa
+66067,Ottawa
+66070,Ozawkie
+66071,Paola
+66072,Parker
+66073,Perry
+66075,Pleasanton
+66076,Pomona
+66077,Potter
+66078,Princeton
+66079,Rantoul
+66080,Richmond
+66083,Spring Hill
+66085,Stilwell
+66086,Tonganoxie
+66087,Troy
+66088,Valley Falls
+66090,Wathena
+66091,Welda
+66092,Wellsville
+66093,Westphalia
+66094,White Cloud
+66095,Williamsburg
+66097,Winchester
+66101,Kansas City
+66102,Kansas City
+66103,Kansas City
+66104,Kansas City
+66105,Kansas City
+66106,Kansas City
+66109,Kansas City
+66110,Kansas City
+66111,Kansas City
+66112,Kansas City
+66113,Edwardsville
+66115,Kansas City
+66117,Kansas City
+66118,Kansas City
+66119,Kansas City
+66160,Kansas City
+66201,Shawnee Mission
+66202,Shawnee Mission
+66203,Shawnee Mission
+66204,Shawnee Mission
+66205,Shawnee Mission
+66206,Shawnee Mission
+66207,Shawnee Mission
+66208,Shawnee Mission
+66209,Shawnee Mission
+66210,Shawnee Mission
+66211,Shawnee Mission
+66212,Shawnee Mission
+66213,Shawnee Mission
+66214,Shawnee Mission
+66215,Shawnee Mission
+66216,Shawnee Mission
+66217,Shawnee Mission
+66218,Shawnee Mission
+66219,Shawnee Mission
+66220,Shawnee Mission
+66221,Shawnee Mission
+66222,Shawnee Mission
+66223,Shawnee Mission
+66224,Shawnee Mission
+66225,Shawnee Mission
+66226,Shawnee Mission
+66227,Shawnee Mission
+66250,Shawnee Mission
+66251,Shawnee Mission
+66276,Shawnee Mission
+66279,Shawnee Mission
+66282,Shawnee Mission
+66283,Shawnee Mission
+66285,Shawnee Mission
+66286,Shawnee Mission
+66401,Alma
+66402,Auburn
+66403,Axtell
+66404,Baileyville
+66406,Beattie
+66407,Belvue
+66408,Bern
+66409,Berryton
+66411,Blue Rapids
+66412,Bremen
+66413,Burlingame
+66414,Carbondale
+66415,Centralia
+66416,Circleville
+66417,Corning
+66418,Delia
+66419,Denison
+66420,Dover
+66422,Emmett
+66423,Eskridge
+66424,Everest
+66425,Fairview
+66426,Fostoria
+66427,Frankfort
+66428,Goff
+66429,Grantville
+66431,Harveyville
+66432,Havensville
+66434,Hiawatha
+66436,Holton
+66438,Home
+66439,Horton
+66440,Hoyt
+66441,Junction City
+66442,Fort Riley
+66449,Leonardville
+66450,Louisville
+66451,Lyndon
+66501,Mc Farland
+66502,Manhattan
+66503,Manhattan
+66505,Manhattan
+66506,Manhattan
+66507,Maple Hill
+66508,Marysville
+66509,Mayetta
+66510,Melvern
+66512,Meriden
+66514,Milford
+66515,Morrill
+66516,Netawaka
+66517,Ogden
+66518,Oketo
+66520,Olsburg
+66521,Onaga
+66522,Oneida
+66523,Osage City
+66524,Overbrook
+66526,Paxico
+66527,Powhattan
+66528,Quenemo
+66531,Riley
+66532,Robinson
+66533,Rossville
+66534,Sabetha
+66535,Saint George
+66536,Saint Marys
+66537,Scranton
+66538,Seneca
+66539,Silver Lake
+66540,Soldier
+66541,Summerfield
+66542,Tecumseh
+66543,Vassar
+66544,Vermillion
+66546,Wakarusa
+66547,Wamego
+66548,Waterville
+66549,Westmoreland
+66550,Wetmore
+66551,Wheaton
+66552,Whiting
+66554,Randolph
+66555,Marysville
+66601,Topeka
+66603,Topeka
+66604,Topeka
+66605,Topeka
+66606,Topeka
+66607,Topeka
+66608,Topeka
+66609,Topeka
+66610,Topeka
+66611,Topeka
+66612,Topeka
+66614,Topeka
+66615,Topeka
+66616,Topeka
+66617,Topeka
+66618,Topeka
+66619,Topeka
+66620,Topeka
+66621,Topeka
+66622,Topeka
+66624,Topeka
+66625,Topeka
+66626,Topeka
+66628,Topeka
+66629,Topeka
+66634,Topeka
+66636,Topeka
+66637,Topeka
+66638,Topeka
+66642,Topeka
+66647,Topeka
+66652,Topeka
+66653,Topeka
+66658,Topeka
+66667,Topeka
+66675,Topeka
+66683,Topeka
+66686,Topeka
+66692,Topeka
+66699,Topeka
+66701,Fort Scott
+66710,Altoona
+66711,Arcadia
+66712,Arma
+66713,Baxter Springs
+66714,Benedict
+66716,Bronson
+66717,Buffalo
+66720,Chanute
+66724,Cherokee
+66725,Columbus
+66727,Coyville
+66728,Crestline
+66732,Elsmore
+66733,Erie
+66734,Farlington
+66735,Franklin
+66736,Fredonia
+66738,Fulton
+66739,Galena
+66740,Galesburg
+66741,Garland
+66742,Gas
+66743,Girard
+66746,Hepler
+66748,Humboldt
+66749,Iola
+66751,La Harpe
+66753,Mc Cune
+66754,Mapleton
+66755,Moran
+66756,Mulberry
+66757,Neodesha
+66758,Neosho Falls
+66759,New Albany
+66760,Opolis
+66761,Piqua
+66762,Pittsburg
+66763,Frontenac
+66767,Prescott
+66769,Redfield
+66770,Riverton
+66771,Saint Paul
+66772,Savonburg
+66773,Scammon
+66775,Stark
+66776,Thayer
+66777,Toronto
+66778,Treece
+66779,Uniontown
+66780,Walnut
+66781,Weir
+66782,West Mineral
+66783,Yates Center
+66801,Emporia
+66830,Admire
+66833,Allen
+66834,Alta Vista
+66835,Americus
+66838,Burdick
+66839,Burlington
+66840,Burns
+66842,Cassoday
+66843,Cedar Point
+66845,Cottonwood Falls
+66846,Council Grove
+66849,Dwight
+66850,Elmdale
+66851,Florence
+66852,Gridley
+66853,Hamilton
+66854,Hartford
+66855,Lamont
+66856,Lebo
+66857,Le Roy
+66858,Lincolnville
+66859,Lost Springs
+66860,Madison
+66861,Marion
+66862,Matfield Green
+66863,Neal
+66864,Neosho Rapids
+66865,Olpe
+66866,Peabody
+66868,Reading
+66869,Strong City
+66870,Virgil
+66871,Waverly
+66872,White City
+66873,Wilsey
+66901,Concordia
+66930,Agenda
+66932,Athol
+66933,Barnes
+66935,Belleville
+66936,Burr Oak
+66937,Clifton
+66938,Clyde
+66939,Courtland
+66940,Cuba
+66941,Esbon
+66942,Formoso
+66943,Greenleaf
+66944,Haddam
+66945,Hanover
+66946,Hollenberg
+66948,Jamestown
+66949,Jewell
+66951,Kensington
+66952,Lebanon
+66953,Linn
+66955,Mahaska
+66956,Mankato
+66958,Morrowville
+66959,Munden
+66960,Narka
+66961,Norway
+66962,Palmer
+66963,Randall
+66964,Republic
+66966,Scandia
+66967,Smith Center
+66968,Washington
+66970,Webber
+67001,Andale
+67002,Andover
+67003,Anthony
+67004,Argonia
+67005,Arkansas City
+67008,Atlanta
+67009,Attica
+67010,Augusta
+67012,Beaumont
+67013,Belle Plaine
+67016,Bentley
+67017,Benton
+67018,Bluff City
+67019,Burden
+67020,Burrton
+67021,Byers
+67022,Caldwell
+67023,Cambridge
+67024,Cedar Vale
+67025,Cheney
+67026,Clearwater
+67028,Coats
+67029,Coldwater
+67030,Colwich
+67031,Conway Springs
+67035,Cunningham
+67036,Danville
+67037,Derby
+67038,Dexter
+67039,Douglass
+67041,Elbing
+67042,El Dorado
+67045,Eureka
+67047,Fall River
+67049,Freeport
+67050,Garden Plain
+67051,Geuda Springs
+67052,Goddard
+67053,Goessel
+67054,Greensburg
+67055,Greenwich
+67056,Halstead
+67057,Hardtner
+67058,Harper
+67059,Haviland
+67060,Haysville
+67061,Hazelton
+67062,Hesston
+67063,Hillsboro
+67065,Isabel
+67066,Iuka
+67067,Kechi
+67068,Kingman
+67070,Kiowa
+67071,Lake City
+67072,Latham
+67073,Lehigh
+67074,Leon
+67101,Maize
+67102,Maple City
+67103,Mayfield
+67104,Medicine Lodge
+67105,Milan
+67106,Milton
+67107,Moundridge
+67108,Mount Hope
+67109,Mullinville
+67110,Mulvane
+67111,Murdock
+67112,Nashville
+67114,Newton
+67117,North Newton
+67118,Norwich
+67119,Oxford
+67120,Peck
+67122,Piedmont
+67123,Potwin
+67124,Pratt
+67127,Protection
+67128,Rago
+67131,Rock
+67132,Rosalia
+67133,Rose Hill
+67134,Sawyer
+67135,Sedgwick
+67137,Severy
+67138,Sharon
+67140,South Haven
+67142,Spivey
+67143,Sun City
+67144,Towanda
+67146,Udall
+67147,Valley Center
+67149,Viola
+67150,Waldron
+67151,Walton
+67152,Wellington
+67154,Whitewater
+67155,Wilmore
+67156,Winfield
+67159,Zenda
+67201,Wichita
+67202,Wichita
+67203,Wichita
+67204,Wichita
+67205,Wichita
+67206,Wichita
+67207,Wichita
+67208,Wichita
+67209,Wichita
+67210,Wichita
+67211,Wichita
+67212,Wichita
+67213,Wichita
+67214,Wichita
+67215,Wichita
+67216,Wichita
+67217,Wichita
+67218,Wichita
+67219,Wichita
+67220,Wichita
+67221,Mc Connell A F B
+67223,Wichita
+67226,Wichita
+67227,Wichita
+67228,Wichita
+67230,Wichita
+67231,Wichita
+67232,Wichita
+67233,Wichita
+67235,Wichita
+67236,Wichita
+67251,Wichita
+67256,Wichita
+67257,Wichita
+67259,Wichita
+67260,Wichita
+67275,Wichita
+67276,Wichita
+67277,Wichita
+67278,Wichita
+67301,Independence
+67330,Altamont
+67332,Bartlett
+67333,Caney
+67334,Chautauqua
+67335,Cherryvale
+67336,Chetopa
+67337,Coffeyville
+67340,Dearing
+67341,Dennis
+67342,Edna
+67344,Elk City
+67345,Elk Falls
+67346,Grenola
+67347,Havana
+67349,Howard
+67351,Liberty
+67352,Longton
+67353,Moline
+67354,Mound Valley
+67355,Niotaze
+67356,Oswego
+67357,Parsons
+67360,Peru
+67361,Sedan
+67363,Sycamore
+67364,Tyro
+67401,Salina
+67402,Salina
+67410,Abilene
+67416,Assaria
+67417,Aurora
+67418,Barnard
+67420,Beloit
+67422,Bennington
+67423,Beverly
+67425,Brookville
+67427,Bushton
+67428,Canton
+67430,Cawker City
+67431,Chapman
+67432,Clay Center
+67436,Delphos
+67437,Downs
+67438,Durham
+67439,Ellsworth
+67441,Enterprise
+67442,Falun
+67443,Galva
+67444,Geneseo
+67445,Glasco
+67446,Glen Elder
+67447,Green
+67448,Gypsum
+67449,Herington
+67450,Holyrood
+67451,Hope
+67452,Hunter
+67454,Kanopolis
+67455,Lincoln
+67456,Lindsborg
+67457,Little River
+67458,Longford
+67459,Lorraine
+67460,McPherson
+67464,Marquette
+67466,Miltonvale
+67467,Minneapolis
+67468,Morganville
+67470,New Cambria
+67473,Osborne
+67474,Portis
+67475,Ramona
+67476,Roxbury
+67478,Simpson
+67480,Solomon
+67481,Sylvan Grove
+67482,Talmage
+67483,Tampa
+67484,Tescott
+67485,Tipton
+67487,Wakefield
+67490,Wilson
+67491,Windom
+67492,Woodbine
+67501,Hutchinson
+67502,Hutchinson
+67504,Hutchinson
+67505,South Hutchinson
+67510,Abbyville
+67511,Albert
+67512,Alden
+67513,Alexander
+67514,Arlington
+67515,Arnold
+67516,Bazine
+67518,Beeler
+67519,Belpre
+67520,Bison
+67521,Brownell
+67522,Buhler
+67523,Burdett
+67524,Chase
+67525,Claflin
+67526,Ellinwood
+67529,Garfield
+67530,Great Bend
+67543,Haven
+67544,Hoisington
+67545,Hudson
+67546,Inman
+67547,Kinsley
+67548,La Crosse
+67550,Larned
+67552,Lewis
+67553,Liebenthal
+67554,Lyons
+67556,Mc Cracken
+67557,Macksville
+67559,Nekoma
+67560,Ness City
+67561,Nickerson
+67563,Offerle
+67564,Olmitz
+67565,Otis
+67566,Partridge
+67567,Pawnee Rock
+67568,Plevna
+67570,Pretty Prairie
+67572,Ransom
+67573,Raymond
+67574,Rozel
+67575,Rush Center
+67576,Saint John
+67578,Stafford
+67579,Sterling
+67581,Sylvia
+67583,Turon
+67584,Utica
+67585,Yoder
+67601,Hays
+67621,Agra
+67622,Almena
+67623,Alton
+67625,Bogue
+67626,Bunker Hill
+67627,Catharine
+67628,Cedar
+67629,Clayton
+67631,Collyer
+67632,Damar
+67634,Dorrance
+67635,Dresden
+67637,Ellis
+67638,Gaylord
+67639,Glade
+67640,Gorham
+67642,Hill City
+67643,Jennings
+67644,Kirwin
+67645,Lenora
+67646,Logan
+67647,Long Island
+67648,Lucas
+67649,Luray
+67650,Morland
+67651,Natoma
+67653,Norcatur
+67654,Norton
+67656,Ogallah
+67657,Palco
+67658,Paradise
+67659,Penokee
+67660,Pfeifer
+67661,Phillipsburg
+67663,Plainville
+67664,Prairie View
+67665,Russell
+67667,Schoenchen
+67669,Stockton
+67670,Stuttgart
+67671,Victoria
+67672,Wa Keeney
+67673,Waldo
+67674,Walker
+67675,Woodston
+67701,Colby
+67730,Atwood
+67731,Bird City
+67732,Brewster
+67733,Edson
+67734,Gem
+67735,Goodland
+67736,Gove
+67737,Grainfield
+67738,Grinnell
+67739,Herndon
+67740,Hoxie
+67741,Kanorado
+67743,Levant
+67744,Ludell
+67745,Mc Donald
+67747,Monument
+67748,Oakley
+67749,Oberlin
+67751,Park
+67752,Quinter
+67753,Rexford
+67756,Saint Francis
+67757,Selden
+67758,Sharon Springs
+67761,Wallace
+67762,Weskan
+67764,Winona
+67801,Dodge City
+67831,Ashland
+67834,Bucklin
+67835,Cimarron
+67836,Coolidge
+67837,Copeland
+67838,Deerfield
+67839,Dighton
+67840,Englewood
+67841,Ensign
+67842,Ford
+67844,Fowler
+67846,Garden City
+67849,Hanston
+67850,Healy
+67851,Holcomb
+67853,Ingalls
+67854,Jetmore
+67855,Johnson
+67857,Kendall
+67859,Kismet
+67860,Lakin
+67861,Leoti
+67862,Manter
+67863,Marienthal
+67864,Meade
+67865,Minneola
+67867,Montezuma
+67868,Pierceville
+67869,Plains
+67870,Satanta
+67871,Scott City
+67876,Spearville
+67877,Sublette
+67878,Syracuse
+67879,Tribune
+67880,Ulysses
+67882,Wright
+67901,Liberal
+67905,Liberal
+67950,Elkhart
+67951,Hugoton
+67952,Moscow
+67953,Richfield
+67954,Rolla
+46001,Alexandria
+46011,Anderson
+46012,Anderson
+46013,Anderson
+46014,Anderson
+46015,Anderson
+46016,Anderson
+46017,Anderson
+46018,Anderson
+46030,Arcadia
+46031,Atlanta
+46032,Carmel
+46033,Carmel
+46034,Cicero
+46035,Colfax
+46036,Elwood
+46038,Fishers
+46039,Forest
+46040,Fortville
+46041,Frankfort
+46044,Frankton
+46045,Goldsmith
+46046,Hillisburg
+46047,Hobbs
+46048,Ingalls
+46049,Kempton
+46050,Kirklin
+46051,Lapel
+46052,Lebanon
+46055,Mc Cordsville
+46056,Markleville
+46057,Michigantown
+46058,Mulberry
+46060,Noblesville
+46061,Noblesville
+46063,Orestes
+46064,Pendleton
+46065,Rossville
+46067,Sedalia
+46068,Sharpsville
+46069,Sheridan
+46070,Summitville
+46071,Thorntown
+46072,Tipton
+46074,Westfield
+46075,Whitestown
+46076,Windfall
+46077,Zionsville
+46082,Carmel
+46102,Advance
+46103,Amo
+46104,Arlington
+46105,Bainbridge
+46106,Bargersville
+46107,Beech Grove
+46110,Boggstown
+46111,Brooklyn
+46112,Brownsburg
+46113,Camby
+46114,Cartersburg
+46115,Carthage
+46117,Charlottesville
+46118,Clayton
+46120,Cloverdale
+46121,Coatesville
+46122,Danville
+46123,Avon
+46124,Edinburgh
+46125,Eminence
+46126,Fairland
+46127,Falmouth
+46128,Fillmore
+46129,Finly
+46130,Fountaintown
+46131,Franklin
+46133,Glenwood
+46135,Greencastle
+46140,Greenfield
+46142,Greenwood
+46143,Greenwood
+46144,Gwynneville
+46146,Homer
+46147,Jamestown
+46148,Knightstown
+46149,Lizton
+46150,Manilla
+46151,Martinsville
+46154,Maxwell
+46155,Mays
+46156,Milroy
+46157,Monrovia
+46158,Mooresville
+46160,Morgantown
+46161,Morristown
+46162,Needham
+46163,New Palestine
+46164,Nineveh
+46165,North Salem
+46166,Paragon
+46167,Pittsboro
+46168,Plainfield
+46170,Putnamville
+46171,Reelsville
+46172,Roachdale
+46173,Rushville
+46175,Russellville
+46176,Shelbyville
+46180,Stilesville
+46181,Trafalgar
+46182,Waldron
+46183,West Newton
+46184,Whiteland
+46186,Wilkinson
+46201,Indianapolis
+46202,Indianapolis
+46203,Indianapolis
+46204,Indianapolis
+46205,Indianapolis
+46206,Indianapolis
+46207,Indianapolis
+46208,Indianapolis
+46209,Indianapolis
+46211,Indianapolis
+46214,Indianapolis
+46216,Indianapolis
+46217,Indianapolis
+46218,Indianapolis
+46219,Indianapolis
+46220,Indianapolis
+46221,Indianapolis
+46222,Indianapolis
+46223,Indianapolis
+46224,Indianapolis
+46225,Indianapolis
+46226,Indianapolis
+46227,Indianapolis
+46228,Indianapolis
+46229,Indianapolis
+46230,Indianapolis
+46231,Indianapolis
+46234,Indianapolis
+46235,Indianapolis
+46236,Indianapolis
+46237,Indianapolis
+46239,Indianapolis
+46240,Indianapolis
+46241,Indianapolis
+46242,Indianapolis
+46244,Indianapolis
+46247,Indianapolis
+46249,Indianapolis
+46250,Indianapolis
+46251,Indianapolis
+46253,Indianapolis
+46254,Indianapolis
+46255,Indianapolis
+46256,Indianapolis
+46259,Indianapolis
+46260,Indianapolis
+46266,Indianapolis
+46268,Indianapolis
+46274,Indianapolis
+46275,Indianapolis
+46277,Indianapolis
+46278,Indianapolis
+46280,Indianapolis
+46282,Indianapolis
+46283,Indianapolis
+46285,Indianapolis
+46290,Indianapolis
+46291,Indianapolis
+46295,Indianapolis
+46298,Indianapolis
+46301,Beverly Shores
+46302,Boone Grove
+46303,Cedar Lake
+46304,Chesterton
+46307,Crown Point
+46308,Crown Point
+46310,Demotte
+46311,Dyer
+46312,East Chicago
+46319,Griffith
+46320,Hammond
+46321,Munster
+46322,Highland
+46323,Hammond
+46324,Hammond
+46325,Hammond
+46327,Hammond
+46340,Hanna
+46341,Hebron
+46342,Hobart
+46345,Kingsbury
+46346,Kingsford Heights
+46347,Kouts
+46348,La Crosse
+46349,Lake Village
+46350,La Porte
+46352,La Porte
+46355,Leroy
+46356,Lowell
+46360,Michigan City
+46361,Michigan City
+46365,Mill Creek
+46366,North Judson
+46368,Portage
+46371,Rolling Prairie
+46372,Roselawn
+46373,Saint John
+46374,San Pierre
+46375,Schererville
+46376,Schneider
+46377,Shelby
+46379,Sumava Resorts
+46380,Tefft
+46381,Thayer
+46382,Union Mills
+46383,Valparaiso
+46384,Valparaiso
+46385,Valparaiso
+46390,Wanatah
+46391,Westville
+46392,Wheatfield
+46393,Wheeler
+46394,Whiting
+46401,Gary
+46402,Gary
+46403,Gary
+46404,Gary
+46405,Lake Station
+46406,Gary
+46407,Gary
+46408,Gary
+46409,Gary
+46410,Merrillville
+46411,Merrillville
+46501,Argos
+46502,Atwood
+46504,Bourbon
+46506,Bremen
+46507,Bristol
+46508,Burket
+46510,Claypool
+46511,Culver
+46513,Donaldson
+46514,Elkhart
+46515,Elkhart
+46516,Elkhart
+46517,Elkhart
+46524,Etna Green
+46526,Goshen
+46527,Goshen
+46528,Goshen
+46530,Granger
+46531,Grovertown
+46532,Hamlet
+46534,Knox
+46536,Lakeville
+46537,Lapaz
+46538,Leesburg
+46539,Mentone
+46540,Middlebury
+46542,Milford
+46543,Millersburg
+46544,Mishawaka
+46545,Mishawaka
+46546,Mishawaka
+46550,Nappanee
+46552,New Carlisle
+46553,New Paris
+46554,North Liberty
+46555,North Webster
+46556,Notre Dame
+46561,Osceola
+46562,Pierceton
+46563,Plymouth
+46565,Shipshewana
+46566,Sidney
+46567,Syracuse
+46570,Tippecanoe
+46571,Topeka
+46572,Tyner
+46573,Wakarusa
+46574,Walkerton
+46580,Warsaw
+46581,Warsaw
+46582,Warsaw
+46590,Winona Lake
+46595,Wyatt
+46601,South Bend
+46604,South Bend
+46612,South Bend
+46613,South Bend
+46614,South Bend
+46615,South Bend
+46616,South Bend
+46617,South Bend
+46619,South Bend
+46620,South Bend
+46624,South Bend
+46626,South Bend
+46628,South Bend
+46629,South Bend
+46634,South Bend
+46635,South Bend
+46637,South Bend
+46660,South Bend
+46680,South Bend
+46699,South Bend
+46701,Albion
+46702,Andrews
+46703,Angola
+46704,Arcola
+46705,Ashley
+46706,Auburn
+46710,Avilla
+46711,Berne
+46713,Bippus
+46714,Bluffton
+46720,Brimfield
+46721,Butler
+46723,Churubusco
+46725,Columbia City
+46730,Corunna
+46731,Craigville
+46732,Cromwell
+46733,Decatur
+46737,Fremont
+46738,Garrett
+46740,Geneva
+46741,Grabill
+46742,Hamilton
+46743,Harlan
+46745,Hoagland
+46746,Howe
+46747,Hudson
+46748,Huntertown
+46750,Huntington
+46755,Kendallville
+46759,Keystone
+46760,Kimmell
+46761,Lagrange
+46763,Laotto
+46764,Larwill
+46765,Leo
+46766,Liberty Center
+46767,Ligonier
+46769,Linn Grove
+46770,Markle
+46771,Mongo
+46772,Monroe
+46773,Monroeville
+46774,New Haven
+46776,Orland
+46777,Ossian
+46778,Petroleum
+46779,Pleasant Lake
+46780,Pleasant Mills
+46781,Poneto
+46782,Preble
+46783,Roanoke
+46784,Rome City
+46785,Saint Joe
+46786,South Milford
+46787,South Whitley
+46788,Spencerville
+46789,Stroh
+46791,Uniondale
+46792,Warren
+46793,Waterloo
+46794,Wawaka
+46795,Wolcottville
+46796,Wolflake
+46797,Woodburn
+46798,Yoder
+46799,Zanesville
+46801,Fort Wayne
+46802,Fort Wayne
+46803,Fort Wayne
+46804,Fort Wayne
+46805,Fort Wayne
+46806,Fort Wayne
+46807,Fort Wayne
+46808,Fort Wayne
+46809,Fort Wayne
+46814,Fort Wayne
+46815,Fort Wayne
+46816,Fort Wayne
+46818,Fort Wayne
+46819,Fort Wayne
+46825,Fort Wayne
+46835,Fort Wayne
+46845,Fort Wayne
+46850,Fort Wayne
+46851,Fort Wayne
+46852,Fort Wayne
+46853,Fort Wayne
+46854,Fort Wayne
+46855,Fort Wayne
+46856,Fort Wayne
+46857,Fort Wayne
+46858,Fort Wayne
+46859,Fort Wayne
+46860,Fort Wayne
+46861,Fort Wayne
+46862,Fort Wayne
+46863,Fort Wayne
+46864,Fort Wayne
+46865,Fort Wayne
+46866,Fort Wayne
+46867,Fort Wayne
+46868,Fort Wayne
+46869,Fort Wayne
+46885,Fort Wayne
+46895,Fort Wayne
+46896,Fort Wayne
+46897,Fort Wayne
+46898,Fort Wayne
+46899,Fort Wayne
+46901,Kokomo
+46902,Kokomo
+46903,Kokomo
+46904,Kokomo
+46910,Akron
+46911,Amboy
+46912,Athens
+46913,Bringhurst
+46914,Bunker Hill
+46915,Burlington
+46916,Burrows
+46917,Camden
+46919,Converse
+46920,Cutler
+46921,Deedsville
+46922,Delong
+46923,Delphi
+46926,Denver
+46928,Fairmount
+46929,Flora
+46930,Fowlerton
+46931,Fulton
+46932,Galveston
+46933,Gas City
+46935,Grass Creek
+46936,Greentown
+46937,Hemlock
+46938,Jonesboro
+46939,Kewanna
+46940,La Fontaine
+46941,Lagro
+46942,Lake Cicott
+46943,Laketon
+46945,Leiters Ford
+46946,Liberty Mills
+46947,Logansport
+46950,Lucerne
+46951,Macy
+46952,Marion
+46953,Marion
+46957,Matthews
+46958,Mexico
+46959,Miami
+46960,Monterey
+46961,New Waverly
+46962,North Manchester
+46965,Oakford
+46967,Onward
+46968,Ora
+46970,Peru
+46971,Grissom AFB
+46974,Roann
+46975,Rochester
+46977,Rockfield
+46978,Royal Center
+46979,Russiaville
+46980,Servia
+46982,Silver Lake
+46984,Somerset
+46985,Star City
+46986,Swayzee
+46987,Sweetser
+46988,Twelve Mile
+46989,Upland
+46990,Urbana
+46991,Van Buren
+46992,Wabash
+46994,Walton
+46995,West Middleton
+46996,Winamac
+46998,Young America
+47001,Aurora
+47003,West College Corner
+47006,Batesville
+47010,Bath
+47011,Bennington
+47012,Brookville
+47016,Cedar Grove
+47017,Cross Plains
+47018,Dillsboro
+47019,East Enterprise
+47020,Florence
+47021,Friendship
+47022,Guilford
+47023,Holton
+47024,Laurel
+47025,Lawrenceburg
+47030,Metamora
+47031,Milan
+47032,Moores Hill
+47033,Morris
+47034,Napoleon
+47035,New Trenton
+47036,Oldenburg
+47037,Osgood
+47038,Patriot
+47039,Pierceville
+47040,Rising Sun
+47041,Sunman
+47042,Versailles
+47043,Vevay
+47060,West Harrison
+47102,Austin
+47104,Bethlehem
+47106,Borden
+47107,Bradford
+47108,Campbellsburg
+47110,Central
+47111,Charlestown
+47112,Corydon
+47114,Crandall
+47115,Depauw
+47116,Eckerty
+47117,Elizabeth
+47118,English
+47119,Floyds Knobs
+47120,Fredericksburg
+47122,Georgetown
+47123,Grantsburg
+47124,Greenville
+47125,Hardinsburg
+47126,Henryville
+47129,Clarksville
+47130,Jeffersonville
+47131,Jeffersonville
+47132,Jeffersonville
+47133,Jeffersonville
+47134,Jeffersonville
+47135,Laconia
+47136,Lanesville
+47137,Leavenworth
+47138,Lexington
+47139,Little York
+47140,Marengo
+47141,Marysville
+47142,Mauckport
+47143,Memphis
+47144,Jeffersonville
+47145,Milltown
+47146,Mount Saint Francis
+47147,Nabb
+47150,New Albany
+47151,New Albany
+47160,New Middletown
+47161,New Salisbury
+47162,New Washington
+47163,Otisco
+47164,Palmyra
+47165,Pekin
+47166,Ramsey
+47167,Salem
+47170,Scottsburg
+47172,Sellersburg
+47174,Sulphur
+47175,Taswell
+47177,Underwood
+47190,Jeffersonville
+47199,Jeffersonville
+47201,Columbus
+47202,Columbus
+47203,Columbus
+47220,Brownstown
+47223,Butlerville
+47224,Canaan
+47225,Clarksburg
+47226,Clifford
+47227,Commiskey
+47228,Cortland
+47229,Crothersville
+47230,Deputy
+47231,Dupont
+47232,Elizabethtown
+47234,Flat Rock
+47235,Freetown
+47236,Grammer
+47240,Greensburg
+47243,Hanover
+47244,Hartsville
+47245,Hayden
+47246,Hope
+47247,Jonesville
+47249,Kurtz
+47250,Madison
+47260,Medora
+47261,Millhousen
+47262,Nebraska
+47263,New Point
+47264,Norman
+47265,North Vernon
+47270,Paris Crossing
+47272,Saint Paul
+47273,Scipio
+47274,Seymour
+47280,Taylorsville
+47281,Vallonia
+47282,Vernon
+47283,Westport
+47302,Muncie
+47303,Muncie
+47304,Muncie
+47305,Muncie
+47306,Muncie
+47307,Muncie
+47308,Muncie
+47320,Albany
+47322,Bentonville
+47324,Boston
+47325,Brownsville
+47326,Bryant
+47327,Cambridge City
+47330,Centerville
+47331,Connersville
+47334,Daleville
+47335,Dublin
+47336,Dunkirk
+47337,Dunreith
+47338,Eaton
+47339,Economy
+47340,Farmland
+47341,Fountain City
+47342,Gaston
+47344,Greensboro
+47345,Greens Fork
+47346,Hagerstown
+47348,Hartford City
+47351,Kennard
+47352,Lewisville
+47353,Liberty
+47354,Losantville
+47355,Lynn
+47356,Middletown
+47357,Milton
+47358,Modoc
+47359,Montpelier
+47360,Mooreland
+47361,Mount Summit
+47362,New Castle
+47366,New Lisbon
+47367,Oakville
+47368,Parker City
+47369,Pennville
+47370,Pershing
+47371,Portland
+47373,Redkey
+47374,Richmond
+47375,Richmond
+47380,Ridgeville
+47381,Salamonia
+47382,Saratoga
+47383,Selma
+47384,Shirley
+47385,Spiceland
+47386,Springport
+47387,Straughn
+47388,Sulphur Springs
+47390,Union City
+47392,Webster
+47393,Williamsburg
+47394,Winchester
+47396,Yorktown
+47401,Bloomington
+47402,Bloomington
+47403,Bloomington
+47404,Bloomington
+47405,Bloomington
+47406,Bloomington
+47407,Bloomington
+47408,Bloomington
+47420,Avoca
+47421,Bedford
+47424,Bloomfield
+47426,Clear Creek
+47427,Coal City
+47429,Ellettsville
+47430,Fort Ritner
+47431,Freedom
+47432,French Lick
+47433,Gosport
+47434,Harrodsburg
+47435,Helmsburg
+47436,Heltonville
+47437,Huron
+47438,Jasonville
+47439,Koleen
+47441,Linton
+47443,Lyons
+47445,Midland
+47446,Mitchell
+47448,Nashville
+47449,Newberry
+47451,Oolitic
+47452,Orleans
+47453,Owensburg
+47454,Paoli
+47455,Patricksburg
+47456,Quincy
+47457,Scotland
+47458,Smithville
+47459,Solsberry
+47460,Spencer
+47462,Springville
+47463,Stanford
+47464,Stinesville
+47465,Switz City
+47467,Tunnelton
+47468,Unionville
+47469,West Baden Springs
+47470,Williams
+47471,Worthington
+47490,Bloomington
+47501,Washington
+47512,Bicknell
+47513,Birdseye
+47514,Branchville
+47515,Bristow
+47516,Bruceville
+47519,Cannelburg
+47520,Cannelton
+47521,Celestine
+47522,Crane
+47523,Dale
+47524,Decker
+47525,Derby
+47527,Dubois
+47528,Edwardsport
+47529,Elnora
+47531,Evanston
+47532,Ferdinand
+47535,Freelandville
+47536,Fulda
+47537,Gentryville
+47541,Holland
+47542,Huntingburg
+47545,Ireland
+47546,Jasper
+47547,Jasper
+47549,Jasper
+47550,Lamar
+47551,Leopold
+47552,Lincoln City
+47553,Loogootee
+47556,Mariah Hill
+47557,Monroe City
+47558,Montgomery
+47561,Oaktown
+47562,Odon
+47564,Otwell
+47567,Petersburg
+47568,Plainville
+47573,Ragsdale
+47574,Rome
+47575,Saint Anthony
+47576,Saint Croix
+47577,Saint Meinrad
+47578,Sandborn
+47579,Santa Claus
+47580,Schnellville
+47581,Shoals
+47584,Spurgeon
+47585,Stendal
+47586,Tell City
+47588,Troy
+47590,Velpen
+47591,Vincennes
+47596,Westphalia
+47597,Wheatland
+47598,Winslow
+47601,Boonville
+47610,Chandler
+47611,Chrisney
+47612,Cynthiana
+47613,Elberfeld
+47614,Folsomville
+47615,Grandview
+47616,Griffin
+47617,Hatfield
+47618,Inglefield
+47619,Lynnville
+47620,Mount Vernon
+47629,Newburgh
+47630,Newburgh
+47631,New Harmony
+47633,Poseyville
+47634,Richland
+47635,Rockport
+47637,Tennyson
+47638,Wadesville
+47639,Haubstadt
+47640,Hazleton
+47647,Buckskin
+47648,Fort Branch
+47649,Francisco
+47654,Mackey
+47660,Oakland City
+47665,Owensville
+47666,Patoka
+47670,Princeton
+47683,Somerville
+47701,Evansville
+47702,Evansville
+47703,Evansville
+47704,Evansville
+47705,Evansville
+47706,Evansville
+47708,Evansville
+47710,Evansville
+47711,Evansville
+47712,Evansville
+47713,Evansville
+47714,Evansville
+47715,Evansville
+47716,Evansville
+47719,Evansville
+47720,Evansville
+47721,Evansville
+47722,Evansville
+47724,Evansville
+47725,Evansville
+47727,Evansville
+47728,Evansville
+47730,Evansville
+47731,Evansville
+47732,Evansville
+47733,Evansville
+47734,Evansville
+47735,Evansville
+47736,Evansville
+47737,Evansville
+47739,Evansville
+47740,Evansville
+47741,Evansville
+47744,Evansville
+47747,Evansville
+47750,Evansville
+47801,Terre Haute
+47802,Terre Haute
+47803,Terre Haute
+47804,Terre Haute
+47805,Terre Haute
+47807,Terre Haute
+47808,Terre Haute
+47809,Terre Haute
+47811,Terre Haute
+47812,Terre Haute
+47813,Terre Haute
+47814,Terre Haute
+47830,Bellmore
+47831,Blanford
+47832,Bloomingdale
+47833,Bowling Green
+47834,Brazil
+47836,Bridgeton
+47837,Carbon
+47838,Carlisle
+47840,Centerpoint
+47841,Clay City
+47842,Clinton
+47845,Coalmont
+47846,Cory
+47847,Dana
+47848,Dugger
+47849,Fairbanks
+47850,Farmersburg
+47851,Fontanet
+47852,Graysville
+47853,Harmony
+47854,Hillsdale
+47855,Hymera
+47856,Judson
+47857,Knightsville
+47858,Lewis
+47859,Marshall
+47860,Mecca
+47861,Merom
+47862,Montezuma
+47863,New Goshen
+47864,New Lebanon
+47865,Paxton
+47866,Pimento
+47868,Poland
+47869,Prairie Creek
+47870,Prairieton
+47871,Riley
+47872,Rockville
+47874,Rosedale
+47875,Saint Bernice
+47876,Saint Mary of the Woods
+47878,Seelyville
+47879,Shelburn
+47880,Shepardsville
+47881,Staunton
+47882,Sullivan
+47884,Universal
+47885,West Terre Haute
+47901,Lafayette
+47902,Lafayette
+47903,Lafayette
+47904,Lafayette
+47905,Lafayette
+47906,West Lafayette
+47907,West Lafayette
+47909,Lafayette
+47916,Alamo
+47917,Ambia
+47918,Attica
+47920,Battle Ground
+47921,Boswell
+47922,Brook
+47923,Brookston
+47924,Buck Creek
+47925,Buffalo
+47926,Burnettsville
+47928,Cayuga
+47929,Chalmers
+47930,Clarks Hill
+47932,Covington
+47933,Crawfordsville
+47934,Crawfordsville
+47935,Crawfordsville
+47936,Crawfordsville
+47937,Crawfordsville
+47938,Crawfordsville
+47939,Crawfordsville
+47940,Darlington
+47941,Dayton
+47942,Earl Park
+47943,Fair Oaks
+47944,Fowler
+47946,Francesville
+47948,Goodland
+47949,Hillsboro
+47950,Idaville
+47951,Kentland
+47952,Kingman
+47954,Ladoga
+47955,Linden
+47957,Medaryville
+47958,Mellott
+47959,Monon
+47960,Monticello
+47962,Montmorenci
+47963,Morocco
+47964,Mount Ayr
+47965,New Market
+47966,Newport
+47967,New Richmond
+47968,New Ross
+47969,Newtown
+47970,Otterbein
+47971,Oxford
+47974,Perrysville
+47975,Pine Village
+47976,Earl Park
+47977,Remington
+47978,Rensselaer
+47980,Reynolds
+47981,Romney
+47982,State Line
+47983,Stockwell
+47984,Talbot
+47986,Templeton
+47987,Veedersburg
+47988,Wallace
+47989,Waveland
+47990,Waynetown
+47991,West Lebanon
+47992,Westpoint
+47993,Williamsport
+47994,Wingate
+47995,Wolcott
+47996,West Lafayette
+47997,Yeoman
+60001,Alden
+60002,Antioch
+60004,Arlington Heights
+60005,Arlington Heights
+60006,Arlington Heights
+60007,Elk Grove Village
+60008,Rolling Meadows
+60009,Elk Grove Village
+60010,Barrington
+60011,Barrington
+60012,Crystal Lake
+60013,Cary
+60014,Crystal Lake
+60015,Deerfield
+60016,Des Plaines
+60017,Des Plaines
+60018,Des Plaines
+60019,Des Plaines
+60020,Fox Lake
+60021,Fox River Grove
+60022,Glencoe
+60025,Glenview
+60026,Glenview NAS
+60029,Golf
+60030,Grayslake
+60031,Gurnee
+60033,Harvard
+60034,Hebron
+60035,Highland Park
+60037,Fort Sheridan
+60038,Palatine
+60039,Crystal Lake
+60040,Highwood
+60041,Ingleside
+60042,Island Lake
+60043,Kenilworth
+60044,Lake Bluff
+60045,Lake Forest
+60046,Lake Villa
+60047,Lake Zurich
+60048,Libertyville
+60049,Long Grove
+60050,McHenry
+60051,McHenry
+60053,Morton Grove
+60055,Palatine
+60056,Mount Prospect
+60060,Mundelein
+60061,Vernon Hills
+60062,Northbrook
+60064,North Chicago
+60065,Northbrook
+60067,Palatine
+60068,Park Ridge
+60069,Lincolnshire
+60070,Prospect Heights
+60071,Richmond
+60072,Ringwood
+60073,Round Lake
+60074,Palatine
+60075,Russell
+60076,Skokie
+60077,Skokie
+60078,Palatine
+60079,Waukegan
+60080,Solon Mills
+60081,Spring Grove
+60082,Techny
+60083,Wadsworth
+60084,Wauconda
+60085,Waukegan
+60086,North Chicago
+60087,Waukegan
+60088,Great Lakes
+60089,Buffalo Grove
+60090,Wheeling
+60091,Wilmette
+60092,Libertyville
+60093,Winnetka
+60094,Palatine
+60095,Palatine
+60096,Winthrop Harbor
+60097,Wonder Lake
+60098,Woodstock
+60099,Zion
+60101,Addison
+60102,Algonquin
+60103,Bartlett
+60104,Bellwood
+60105,Bensenville
+60106,Bensenville
+60107,Streamwood
+60108,Bloomingdale
+60109,Burlington
+60110,Carpentersville
+60111,Clare
+60112,Cortland
+60113,Creston
+60115,Dekalb
+60116,Carol Stream
+60117,Bloomingdale
+60118,Dundee
+60119,Elburn
+60120,Elgin
+60121,Elgin
+60122,Elgin
+60123,Elgin
+60125,Carol Stream
+60126,Elmhurst
+60128,Carol Stream
+60129,Esmond
+60130,Forest Park
+60131,Franklin Park
+60132,Carol Stream
+60134,Geneva
+60135,Genoa
+60136,Gilberts
+60137,Glen Ellyn
+60138,Glen Ellyn
+60139,Glendale Heights
+60140,Hampshire
+60141,Hines
+60142,Huntley
+60143,Itasca
+60144,Kaneville
+60145,Kingston
+60146,Kirkland
+60147,Lafox
+60148,Lombard
+60150,Malta
+60151,Maple Park
+60152,Marengo
+60153,Maywood
+60154,Westchester
+60155,Broadview
+60157,Medinah
+60159,Schaumburg
+60160,Melrose Park
+60161,Melrose Park
+60162,Hillside
+60163,Berkeley
+60164,Melrose Park
+60165,Stone Park
+60168,Schaumburg
+60170,Plato Center
+60171,River Grove
+60172,Roselle
+60173,Schaumburg
+60174,Saint Charles
+60175,Saint Charles
+60176,Schiller Park
+60177,South Elgin
+60178,Sycamore
+60179,Hoffman Estates
+60180,Union
+60181,Villa Park
+60182,Virgil
+60183,Wasco
+60184,Wayne
+60185,West Chicago
+60186,West Chicago
+60187,Wheaton
+60188,Carol Stream
+60189,Wheaton
+60190,Winfield
+60191,Wood Dale
+60192,Schaumburg
+60193,Schaumburg
+60194,Schaumburg
+60195,Schaumburg
+60196,Schaumburg
+60197,Carol Stream
+60198,Carol Stream
+60199,Carol Stream
+60201,Evanston
+60202,Evanston
+60203,Evanston
+60204,Evanston
+60208,Evanston
+60209,Evanston
+60301,Oak Park
+60302,Oak Park
+60303,Oak Park
+60304,Oak Park
+60305,River Forest
+60401,Beecher
+60402,Berwyn
+60406,Blue Island
+60407,Braceville
+60408,Braidwood
+60409,Calumet City
+60410,Channahon
+60411,Chicago Heights
+60412,Chicago Heights
+60415,Chicago Ridge
+60416,Coal City
+60417,Crete
+60419,Dolton
+60420,Dwight
+60421,Elwood
+60422,Flossmoor
+60423,Frankfort
+60424,Gardner
+60425,Glenwood
+60426,Harvey
+60429,Hazel Crest
+60430,Homewood
+60431,Joliet
+60432,Joliet
+60433,Joliet
+60434,Joliet
+60435,Joliet
+60436,Joliet
+60437,Kinsman
+60438,Lansing
+60439,Lemont
+60440,Bolingbrook
+60441,Lockport
+60442,Manhattan
+60443,Matteson
+60444,Mazon
+60445,Midlothian
+60446,Romeoville
+60447,Minooka
+60448,Mokena
+60449,Monee
+60450,Morris
+60451,New Lenox
+60452,Oak Forest
+60453,Oak Lawn
+60454,Oak Lawn
+60455,Bridgeview
+60456,Hometown
+60457,Hickory Hills
+60458,Justice
+60459,Burbank
+60460,Odell
+60461,Olympia Fields
+60462,Orland Park
+60463,Palos Heights
+60464,Palos Park
+60465,Palos Hills
+60466,Park Forest
+60467,Orland Park
+60468,Peotone
+60469,Posen
+60470,Ransom
+60471,Richton Park
+60472,Robbins
+60473,South Holland
+60474,South Wilmington
+60475,Steger
+60476,Thornton
+60477,Tinley Park
+60478,Country Club Hills
+60479,Verona
+60480,Willow Springs
+60481,Wilmington
+60482,Worth
+60490,Bolingbrook
+60499,Bedford Park
+60501,Summit Argo
+60504,Aurora
+60505,Aurora
+60506,Aurora
+60507,Aurora
+60510,Batavia
+60511,Big Rock
+60512,Bristol
+60513,Brookfield
+60514,Clarendon Hills
+60515,Downers Grove
+60516,Downers Grove
+60517,Woodridge
+60518,Earlville
+60519,Eola
+60520,Hinckley
+60521,Hinsdale
+60522,Hinsdale
+60523,Oak Brook
+60525,La Grange
+60526,La Grange Park
+60527,Hinsdale
+60530,Lee
+60531,Leland
+60532,Lisle
+60534,Lyons
+60536,Millbrook
+60537,Millington
+60538,Montgomery
+60539,Mooseheart
+60540,Naperville
+60541,Newark
+60542,North Aurora
+60543,Oswego
+60544,Plainfield
+60545,Plano
+60546,Riverside
+60548,Sandwich
+60549,Serena
+60550,Shabbona
+60551,Sheridan
+60552,Somonauk
+60553,Steward
+60554,Sugar Grove
+60555,Warrenville
+60556,Waterman
+60557,Wedron
+60558,Western Springs
+60559,Westmont
+60560,Yorkville
+60561,Darien
+60563,Naperville
+60564,Naperville
+60565,Naperville
+60566,Naperville
+60567,Naperville
+60568,Aurora
+60570,Hinsdale
+60572,Aurora
+60597,Fox Valley
+60598,Aurora
+60599,Fox Valley
+60601,Chicago
+60602,Chicago
+60603,Chicago
+60604,Chicago
+60605,Chicago
+60606,Chicago
+60607,Chicago
+60608,Chicago
+60609,Chicago
+60610,Chicago
+60611,Chicago
+60612,Chicago
+60613,Chicago
+60614,Chicago
+60615,Chicago
+60616,Chicago
+60617,Chicago
+60618,Chicago
+60619,Chicago
+60620,Chicago
+60621,Chicago
+60622,Chicago
+60623,Chicago
+60624,Chicago
+60625,Chicago
+60626,Chicago
+60628,Chicago
+60629,Chicago
+60630,Chicago
+60631,Chicago
+60632,Chicago
+60633,Chicago
+60634,Chicago
+60636,Chicago
+60637,Chicago
+60638,Chicago
+60639,Chicago
+60640,Chicago
+60641,Chicago
+60643,Chicago
+60644,Chicago
+60645,Chicago
+60646,Chicago
+60647,Chicago
+60649,Chicago
+60651,Chicago
+60652,Chicago
+60653,Chicago
+60654,Chicago
+60655,Chicago
+60656,Chicago
+60657,Chicago
+60659,Chicago
+60660,Chicago
+60661,Chicago
+60663,Chicago
+60664,Chicago
+60665,Chicago
+60666,AMF O'Hare
+60667,Chicago
+60668,Chicago
+60669,Chicago
+60670,Chicago
+60671,Chicago
+60672,Chicago
+60673,Chicago
+60674,Chicago
+60675,Chicago
+60677,Chicago
+60678,Chicago
+60679,Chicago
+60680,Chicago
+60681,Chicago
+60683,Chicago
+60684,Chicago
+60685,Chicago
+60687,Chicago
+60688,Chicago
+60690,Chicago
+60691,Chicago
+60693,Chicago
+60694,Chicago
+60697,Chicago
+60699,Chicago
+60701,Chicago
+60706,Harwood Heights
+60707,Elmwood Park
+60712,Lincolnwood
+60714,Niles
+60803,Alsip
+60804,Cicero
+60805,Evergreen Park
+60827,Riverdale
+60901,Kankakee
+60902,Kankakee
+60910,Aroma Park
+60911,Ashkum
+60912,Beaverville
+60913,Bonfield
+60914,Bourbonnais
+60915,Bradley
+60917,Buckingham
+60918,Buckley
+60919,Cabery
+60920,Campus
+60921,Chatsworth
+60922,Chebanse
+60924,Cissna Park
+60926,Claytonville
+60927,Clifton
+60928,Crescent City
+60929,Cullom
+60930,Danforth
+60931,Donovan
+60932,East Lynn
+60933,Elliott
+60934,Emington
+60935,Essex
+60936,Gibson City
+60938,Gilman
+60939,Goodwine
+60940,Grant Park
+60941,Herscher
+60942,Hoopeston
+60944,Hopkins Park
+60945,Iroquois
+60946,Kempton
+60948,Loda
+60949,Ludlow
+60950,Manteno
+60951,Martinton
+60952,Melvin
+60953,Milford
+60954,Momence
+60955,Onarga
+60956,Papineau
+60957,Paxton
+60959,Piper City
+60960,Rankin
+60961,Reddick
+60962,Roberts
+60963,Rossville
+60964,Saint Anne
+60966,Sheldon
+60967,Stockland
+60968,Thawville
+60969,Union Hill
+60970,Watseka
+60973,Wellington
+60974,Woodland
+61001,Apple River
+61006,Ashton
+61007,Baileyville
+61008,Belvidere
+61010,Byron
+61011,Caledonia
+61012,Capron
+61013,Cedarville
+61014,Chadwick
+61015,Chana
+61016,Cherry Valley
+61017,Coleta
+61018,Dakota
+61019,Davis
+61020,Davis Junction
+61021,Dixon
+61024,Durand
+61025,East Dubuque
+61027,Eleroy
+61028,Elizabeth
+61030,Forreston
+61031,Franklin Grove
+61032,Freeport
+61036,Galena
+61037,Galt
+61038,Garden Prairie
+61039,German Valley
+61041,Hanover
+61042,Harmon
+61043,Holcomb
+61044,Kent
+61046,Lanark
+61047,Leaf River
+61048,Lena
+61049,Lindenwood
+61050,Mc Connell
+61051,Milledgeville
+61052,Monroe Center
+61053,Mount Carroll
+61054,Mount Morris
+61057,Nachusa
+61058,Nelson
+61059,Nora
+61060,Orangeville
+61061,Oregon
+61062,Pearl City
+61063,Pecatonica
+61064,Polo
+61065,Poplar Grove
+61067,Ridott
+61068,Rochelle
+61070,Rock City
+61071,Rock Falls
+61072,Rockton
+61073,Roscoe
+61074,Savanna
+61075,Scales Mound
+61076,Scioto Mills
+61077,Seward
+61078,Shannon
+61079,Shirland
+61080,South Beloit
+61081,Sterling
+61084,Stillman Valley
+61085,Stockton
+61087,Warren
+61088,Winnebago
+61089,Winslow
+61091,Woosung
+61101,Rockford
+61102,Rockford
+61103,Rockford
+61104,Rockford
+61105,Rockford
+61106,Rockford
+61107,Rockford
+61108,Rockford
+61109,Rockford
+61110,Rockford
+61111,Loves Park
+61112,Rockford
+61114,Rockford
+61115,Machesney Park
+61125,Rockford
+61126,Rockford
+61130,Loves Park
+61131,Loves Park
+61132,Loves Park
+61201,Rock Island
+61204,Rock Island
+61206,Rock Island
+61230,Albany
+61231,Aledo
+61232,Andalusia
+61233,Andover
+61234,Annawan
+61235,Atkinson
+61236,Barstow
+61237,Buffalo Prairie
+61238,Cambridge
+61239,Carbon Cliff
+61240,Coal Valley
+61241,Colona
+61242,Cordova
+61243,Deer Grove
+61244,East Moline
+61250,Erie
+61251,Fenton
+61252,Fulton
+61254,Geneseo
+61256,Hampton
+61257,Hillsdale
+61258,Hooppole
+61259,Illinois City
+61260,Joy
+61261,Lyndon
+61262,Lynn Center
+61263,Matherville
+61264,Milan
+61265,Moline
+61266,Moline
+61270,Morrison
+61272,New Boston
+61273,Orion
+61274,Osco
+61275,Port Byron
+61276,Preemption
+61277,Prophetstown
+61278,Rapids City
+61279,Reynolds
+61281,Sherrard
+61282,Silvis
+61283,Tampico
+61284,Taylor Ridge
+61285,Thomson
+61299,Rock Island
+61301,La Salle
+61310,Amboy
+61311,Ancona
+61312,Arlington
+61313,Blackstone
+61314,Buda
+61315,Bureau
+61316,Cedar Point
+61317,Cherry
+61318,Compton
+61319,Cornell
+61320,Dalzell
+61321,Dana
+61322,Depue
+61323,Dover
+61324,Eldena
+61325,Grand Ridge
+61326,Granville
+61327,Hennepin
+61328,Kasbeer
+61329,Ladd
+61330,La Moille
+61331,Lee Center
+61332,Leonore
+61333,Long Point
+61334,Lostant
+61335,Mc Nabb
+61336,Magnolia
+61337,Malden
+61338,Manlius
+61340,Mark
+61341,Marseilles
+61342,Mendota
+61344,Mineral
+61345,Neponset
+61346,New Bedford
+61348,Oglesby
+61349,Ohio
+61350,Ottawa
+61353,Paw Paw
+61354,Peru
+61356,Princeton
+61358,Rutland
+61359,Seatonville
+61360,Seneca
+61361,Sheffield
+61362,Spring Valley
+61363,Standard
+61364,Streator
+61367,Sublette
+61368,Tiskilwa
+61369,Toluca
+61370,Tonica
+61371,Triumph
+61372,Troy Grove
+61373,Utica
+61374,Van Orin
+61375,Varna
+61376,Walnut
+61377,Wenona
+61378,West Brooklyn
+61379,Wyanet
+61401,Galesburg
+61402,Galesburg
+61410,Abingdon
+61411,Adair
+61412,Alexis
+61413,Alpha
+61414,Altona
+61415,Avon
+61416,Bardolph
+61417,Berwick
+61418,Biggsville
+61419,Bishop Hill
+61420,Blandinsville
+61421,Bradford
+61422,Bushnell
+61423,Cameron
+61424,Camp Grove
+61425,Carman
+61426,Castleton
+61427,Cuba
+61428,Dahinda
+61430,East Galesburg
+61431,Ellisville
+61432,Fairview
+61433,Fiatt
+61434,Galva
+61435,Gerlaw
+61436,Gilson
+61437,Gladstone
+61438,Good Hope
+61439,Henderson
+61440,Industry
+61441,Ipava
+61442,Keithsburg
+61443,Kewanee
+61447,Kirkwood
+61448,Knoxville
+61449,La Fayette
+61450,La Harpe
+61451,Laura
+61452,Littleton
+61453,Little York
+61454,Lomax
+61455,Macomb
+61458,Maquon
+61459,Marietta
+61460,Media
+61462,Monmouth
+61465,New Windsor
+61466,North Henderson
+61467,Oneida
+61468,Opheim
+61469,Oquawka
+61470,Prairie City
+61471,Raritan
+61472,Rio
+61473,Roseville
+61474,Saint Augustine
+61475,Sciota
+61476,Seaton
+61477,Smithfield
+61478,Smithshire
+61479,Speer
+61480,Stronghurst
+61482,Table Grove
+61483,Toulon
+61484,Vermont
+61485,Victoria
+61486,Viola
+61488,Wataga
+61489,Williamsfield
+61490,Woodhull
+61491,Wyoming
+61501,Astoria
+61516,Benson
+61517,Brimfield
+61518,Brimfield
+61519,Bryant
+61520,Canton
+61523,Chillicothe
+61524,Dunfermline
+61525,Dunlap
+61526,Edelstein
+61528,Edwards
+61529,Elmwood
+61530,Eureka
+61531,Farmington
+61532,Forest City
+61533,Glasford
+61534,Green Valley
+61535,Groveland
+61536,Hanna City
+61537,Henry
+61539,Kingston Mines
+61540,Lacon
+61541,La Rose
+61542,Lewistown
+61543,Liverpool
+61544,London Mills
+61545,Lowpoint
+61546,Manito
+61547,Mapleton
+61548,Metamora
+61550,Morton
+61552,Mossville
+61553,Norris
+61554,Pekin
+61555,Pekin
+61558,Pekin
+61559,Princeville
+61560,Putnam
+61561,Roanoke
+61562,Rome
+61563,Saint David
+61564,South Pekin
+61565,Sparland
+61567,Topeka
+61568,Tremont
+61569,Trivoli
+61570,Washburn
+61571,Washington
+61572,Yates City
+61601,Peoria
+61602,Peoria
+61603,Peoria
+61604,Peoria
+61605,Peoria
+61606,Peoria
+61607,Peoria
+61610,Creve Coeur
+61611,East Peoria
+61612,Peoria
+61613,Peoria
+61614,Peoria
+61615,Peoria
+61625,Peoria
+61628,Peoria
+61629,Peoria
+61630,Peoria
+61632,Peoria
+61633,Peoria
+61634,Peoria
+61635,Peoria
+61636,Peoria
+61637,Peoria
+61638,Peoria
+61639,Peoria
+61640,Peoria
+61641,Peoria
+61643,Peoria
+61644,Peoria
+61650,Peoria
+61651,Peoria
+61652,Peoria
+61653,Peoria
+61654,Peoria
+61655,Peoria
+61656,Peoria
+61701,Bloomington
+61702,Bloomington
+61704,Bloomington
+61709,Bloomington
+61710,Bloomington
+61720,Anchor
+61721,Armington
+61722,Arrowsmith
+61723,Atlanta
+61724,Bellflower
+61725,Carlock
+61726,Chenoa
+61727,Clinton
+61728,Colfax
+61729,Congerville
+61730,Cooksville
+61731,Cropsey
+61732,Danvers
+61733,Deer Creek
+61734,Delavan
+61735,Dewitt
+61736,Downs
+61737,Ellsworth
+61738,El Paso
+61739,Fairbury
+61740,Flanagan
+61741,Forrest
+61742,Goodfield
+61743,Graymont
+61744,Gridley
+61745,Heyworth
+61747,Hopedale
+61748,Hudson
+61749,Kenney
+61750,Lane
+61751,Lawndale
+61752,Le Roy
+61753,Lexington
+61754,Mc Lean
+61755,Mackinaw
+61756,Maroa
+61758,Merna
+61759,Minier
+61760,Minonk
+61761,Normal
+61764,Pontiac
+61769,Saunemin
+61770,Saybrook
+61771,Secor
+61772,Shirley
+61773,Sibley
+61774,Stanford
+61775,Strawn
+61776,Towanda
+61777,Wapella
+61778,Waynesville
+61790,Normal
+61791,Bloomington
+61799,Bloomington
+61801,Urbana
+61802,Urbana
+61803,Urbana
+61810,Allerton
+61811,Alvin
+61812,Armstrong
+61813,Bement
+61814,Bismarck
+61815,Bondville
+61816,Broadlands
+61817,Catlin
+61818,Cerro Gordo
+61820,Champaign
+61821,Champaign
+61822,Champaign
+61824,Champaign
+61825,Champaign
+61826,Champaign
+61830,Cisco
+61831,Collison
+61832,Danville
+61833,Tilton
+61834,Danville
+61839,De Land
+61840,Dewey
+61841,Fairmount
+61842,Farmer City
+61843,Fisher
+61844,Fithian
+61845,Foosland
+61846,Georgetown
+61847,Gifford
+61848,Henning
+61849,Homer
+61850,Indianola
+61851,Ivesdale
+61852,Longview
+61853,Mahomet
+61854,Mansfield
+61855,Milmine
+61856,Monticello
+61857,Muncie
+61858,Oakwood
+61859,Ogden
+61862,Penfield
+61863,Pesotum
+61864,Philo
+61865,Potomac
+61866,Rantoul
+61870,Ridge Farm
+61871,Royal
+61872,Sadorus
+61873,Saint Joseph
+61874,Savoy
+61875,Seymour
+61876,Sidell
+61877,Sidney
+61878,Thomasboro
+61880,Tolono
+61882,Weldon
+61883,Westville
+61884,White Heath
+61910,Arcola
+61911,Arthur
+61912,Ashmore
+61913,Atwood
+61914,Bethany
+61917,Brocton
+61919,Camargo
+61920,Charleston
+61924,Chrisman
+61925,Dalton City
+61928,Gays
+61929,Hammond
+61930,Hindsboro
+61931,Humboldt
+61932,Hume
+61933,Kansas
+61936,La Place
+61937,Lovington
+61938,Mattoon
+61940,Metcalf
+61941,Murdock
+61942,Newman
+61943,Oakland
+61944,Paris
+61949,Redmon
+61951,Sullivan
+61953,Tuscola
+61955,Vermilion
+61956,Villa Grove
+61957,Windsor
+62001,Alhambra
+62002,Alton
+62006,Batchtown
+62009,Benld
+62010,Bethalto
+62011,Bingham
+62012,Brighton
+62013,Brussels
+62014,Bunker Hill
+62015,Butler
+62016,Carrollton
+62017,Coffeen
+62018,Cottage Hills
+62019,Donnellson
+62021,Dorsey
+62022,Dow
+62023,Eagarville
+62024,East Alton
+62025,Edwardsville
+62026,Edwardsville
+62027,Eldred
+62028,Elsah
+62030,Fidelity
+62031,Fieldon
+62032,Fillmore
+62033,Gillespie
+62034,Glen Carbon
+62035,Godfrey
+62036,Golden Eagle
+62037,Grafton
+62040,Granite City
+62044,Greenfield
+62045,Hamburg
+62046,Hamel
+62047,Hardin
+62048,Hartford
+62049,Hillsboro
+62050,Hillview
+62051,Irving
+62052,Jerseyville
+62053,Kampsville
+62054,Kane
+62056,Litchfield
+62058,Livingston
+62059,Lovejoy
+62060,Madison
+62061,Marine
+62062,Maryville
+62063,Medora
+62065,Michael
+62067,Moro
+62069,Mount Olive
+62070,Mozier
+62071,National Stock Yards
+62074,New Douglas
+62075,Nokomis
+62076,Ohlman
+62077,Panama
+62078,Patterson
+62079,Piasa
+62080,Ramsey
+62081,Rockbridge
+62082,Roodhouse
+62083,Rosamond
+62084,Roxana
+62085,Sawyerville
+62086,Sorento
+62087,South Roxana
+62088,Staunton
+62089,Taylor Springs
+62090,Venice
+62091,Walshville
+62092,White Hall
+62093,Wilsonville
+62094,Witt
+62095,Wood River
+62097,Worden
+62098,Wrights
+62201,East Saint Louis
+62202,East Saint Louis
+62203,East Saint Louis
+62204,East Saint Louis
+62205,East Saint Louis
+62206,East Saint Louis
+62207,East Saint Louis
+62208,Fairview Heights
+62214,Addieville
+62215,Albers
+62216,Aviston
+62217,Baldwin
+62218,Bartelso
+62219,Beckemeyer
+62220,Belleville
+62221,Belleville
+62222,Belleville
+62223,Belleville
+62224,Mascoutah
+62225,Scott Air Force Base
+62226,Belleville
+62230,Breese
+62231,Carlyle
+62232,Caseyville
+62233,Chester
+62234,Collinsville
+62236,Columbia
+62237,Coulterville
+62238,Cutler
+62239,Dupo
+62240,East Carondelet
+62241,Ellis Grove
+62242,Evansville
+62243,Freeburg
+62244,Fults
+62245,Germantown
+62246,Greenville
+62247,Hagarstown
+62248,Hecker
+62249,Highland
+62250,Hoffman
+62252,Huey
+62253,Keyesport
+62254,Lebanon
+62255,Lenzburg
+62256,Maeystown
+62257,Marissa
+62258,Mascoutah
+62259,Menard
+62260,Millstadt
+62261,Modoc
+62262,Mulberry Grove
+62263,Nashville
+62264,New Athens
+62265,New Baden
+62266,New Memphis
+62268,Oakdale
+62269,O Fallon
+62271,Okawville
+62272,Percy
+62273,Pierron
+62274,Pinckneyville
+62275,Pocahontas
+62277,Prairie du Rocher
+62278,Red Bud
+62279,Renault
+62280,Rockwood
+62281,Saint Jacob
+62282,Saint Libory
+62283,Shattuc
+62284,Smithboro
+62285,Smithton
+62286,Sparta
+62288,Steeleville
+62289,Summerfield
+62292,Tilden
+62293,Trenton
+62294,Troy
+62295,Valmeyer
+62297,Walsh
+62298,Waterloo
+62301,Quincy
+62305,Quincy
+62306,Quincy
+62310,Adrian
+62311,Augusta
+62312,Barry
+62313,Basco
+62314,Baylis
+62316,Bowen
+62318,Burnside
+62319,Camden
+62320,Camp Point
+62321,Carthage
+62323,Chambersburg
+62324,Clayton
+62325,Coatsburg
+62326,Colchester
+62329,Colusa
+62330,Dallas City
+62334,Elvaston
+62336,Ferris
+62338,Fowler
+62339,Golden
+62340,Griggsville
+62341,Hamilton
+62343,Hull
+62344,Huntsville
+62345,Kinderhook
+62346,La Prairie
+62347,Liberty
+62348,Lima
+62349,Loraine
+62351,Mendon
+62352,Milton
+62353,Mount Sterling
+62354,Nauvoo
+62355,Nebo
+62356,New Canton
+62357,New Salem
+62358,Niota
+62359,Paloma
+62360,Payson
+62361,Pearl
+62362,Perry
+62363,Pittsfield
+62365,Plainville
+62366,Pleasant Hill
+62367,Plymouth
+62370,Rockport
+62373,Sutter
+62374,Tennessee
+62375,Timewell
+62376,Ursa
+62378,Versailles
+62379,Warsaw
+62380,West Point
+62401,Effingham
+62410,Allendale
+62411,Altamont
+62413,Annapolis
+62414,Beecher City
+62415,Birds
+62417,Bridgeport
+62418,Brownstown
+62419,Calhoun
+62420,Casey
+62421,Claremont
+62422,Cowden
+62423,Dennison
+62424,Dieterich
+62425,Dundas
+62426,Edgewood
+62427,Flat Rock
+62428,Greenup
+62431,Herrick
+62432,Hidalgo
+62433,Hutsonville
+62434,Ingraham
+62435,Janesville
+62436,Jewett
+62438,Lakewood
+62439,Lawrenceville
+62440,Lerna
+62441,Marshall
+62442,Martinsville
+62443,Mason
+62444,Mode
+62445,Montrose
+62446,Mount Erie
+62447,Neoga
+62448,Newton
+62449,Oblong
+62450,Olney
+62451,Palestine
+62452,Parkersburg
+62454,Robinson
+62458,Saint Elmo
+62459,Sainte Marie
+62460,Saint Francisville
+62461,Shumway
+62462,Sigel
+62463,Stewardson
+62464,Stoy
+62465,Strasburg
+62466,Sumner
+62467,Teutopolis
+62468,Toledo
+62469,Trilla
+62471,Vandalia
+62473,Watson
+62474,Westfield
+62475,West Liberty
+62476,West Salem
+62477,West Union
+62478,West York
+62479,Wheeler
+62480,Willow Hill
+62481,Yale
+62501,Argenta
+62510,Assumption
+62511,Atwater
+62512,Beason
+62513,Blue Mound
+62514,Boody
+62515,Buffalo
+62517,Bulpitt
+62518,Chestnut
+62519,Cornland
+62520,Dawson
+62521,Decatur
+62522,Decatur
+62523,Decatur
+62524,Decatur
+62525,Decatur
+62526,Decatur
+62527,Decatur
+62530,Divernon
+62531,Edinburg
+62532,Elwin
+62533,Farmersville
+62534,Findlay
+62535,Forsyth
+62536,Glenarm
+62537,Harristown
+62538,Harvel
+62539,Illiopolis
+62540,Kincaid
+62541,Lake Fork
+62543,Latham
+62544,Macon
+62545,Mechanicsburg
+62546,Morrisonville
+62547,Mount Auburn
+62548,Mount Pulaski
+62549,Mt Zion
+62550,Moweaqua
+62551,Niantic
+62552,Oakley
+62553,Oconee
+62554,Oreana
+62555,Owaneco
+62556,Palmer
+62557,Pana
+62558,Pawnee
+62560,Raymond
+62561,Riverton
+62563,Rochester
+62565,Shelbyville
+62567,Stonington
+62568,Taylorville
+62570,Tovey
+62571,Tower Hill
+62572,Waggoner
+62573,Warrensburg
+62601,Alexander
+62610,Alsey
+62611,Arenzville
+62612,Ashland
+62613,Athens
+62615,Auburn
+62617,Bath
+62618,Beardstown
+62621,Bluffs
+62622,Bluff Springs
+62624,Browning
+62625,Cantrall
+62626,Carlinville
+62627,Chandlerville
+62628,Chapin
+62629,Chatham
+62630,Chesterfield
+62631,Concord
+62633,Easton
+62634,Elkhart
+62635,Emden
+62638,Franklin
+62639,Frederick
+62640,Girard
+62642,Greenview
+62643,Hartsburg
+62644,Havana
+62649,Hettick
+62650,Jacksonville
+62651,Jacksonville
+62655,Kilbourne
+62656,Lincoln
+62659,Lincoln's New Salem
+62660,Literberry
+62661,Loami
+62662,Lowder
+62663,Manchester
+62664,Mason City
+62665,Meredosia
+62666,Middletown
+62667,Modesto
+62668,Murrayville
+62670,New Berlin
+62671,New Holland
+62672,Nilwood
+62673,Oakford
+62674,Palmyra
+62675,Petersburg
+62677,Pleasant Plains
+62681,Rushville
+62682,San Jose
+62683,Scottville
+62684,Sherman
+62685,Shipman
+62686,Standard City
+62688,Tallula
+62689,Thayer
+62690,Virden
+62691,Virginia
+62692,Waverly
+62693,Williamsville
+62694,Winchester
+62695,Woodson
+62701,Springfield
+62702,Springfield
+62703,Springfield
+62704,Springfield
+62705,Springfield
+62706,Springfield
+62707,Springfield
+62708,Springfield
+62709,Springfield
+62713,Springfield
+62715,Springfield
+62716,Springfield
+62718,Springfield
+62719,Springfield
+62720,Springfield
+62721,Springfield
+62722,Springfield
+62723,Springfield
+62726,Springfield
+62736,Springfield
+62739,Springfield
+62746,Springfield
+62756,Springfield
+62757,Springfield
+62761,Springfield
+62762,Springfield
+62763,Springfield
+62764,Springfield
+62765,Springfield
+62766,Springfield
+62767,Springfield
+62769,Springfield
+62776,Springfield
+62777,Springfield
+62781,Springfield
+62786,Springfield
+62791,Springfield
+62794,Springfield
+62796,Springfield
+62801,Centralia
+62803,Hoyleton
+62805,Akin
+62806,Albion
+62807,Alma
+62808,Ashley
+62809,Barnhill
+62810,Belle Rive
+62811,Bellmont
+62812,Benton
+62814,Bluford
+62815,Bone Gap
+62816,Bonnie
+62817,Broughton
+62818,Browns
+62819,Buckner
+62820,Burnt Prairie
+62821,Carmi
+62822,Christopher
+62823,Cisne
+62824,Clay City
+62825,Coello
+62827,Crossville
+62828,Dahlgren
+62829,Dale
+62830,Dix
+62831,Du Bois
+62832,Du Quoin
+62833,Ellery
+62834,Emma
+62835,Enfield
+62836,Ewing
+62837,Fairfield
+62838,Farina
+62839,Flora
+62840,Frankfort Heights
+62841,Freeman Spur
+62842,Geff
+62843,Golden Gate
+62844,Grayville
+62845,Herald
+62846,Ina
+62847,Iola
+62848,Irvington
+62849,Iuka
+62850,Johnsonville
+62851,Keenes
+62852,Keensburg
+62853,Kell
+62854,Kinmundy
+62855,Lancaster
+62856,Logan
+62857,Loogootee
+62858,Louisville
+62859,Mc Leansboro
+62860,Macedonia
+62861,Maunie
+62862,Mill Shoals
+62863,Mount Carmel
+62864,Mount Vernon
+62865,Mulkeytown
+62866,Nason
+62867,New Haven
+62868,Noble
+62869,Norris City
+62870,Odin
+62871,Omaha
+62872,Opdyke
+62874,Orient
+62875,Patoka
+62876,Radom
+62877,Richview
+62878,Rinard
+62879,Sailor Springs
+62880,Saint Peter
+62881,Salem
+62882,Sandoval
+62883,Scheller
+62884,Sesser
+62885,Shobonier
+62886,Sims
+62887,Springerton
+62888,Tamaroa
+62889,Texico
+62890,Thompsonville
+62891,Valier
+62892,Vernon
+62893,Walnut Hill
+62894,Waltonville
+62895,Wayne City
+62896,West Frankfort
+62897,Whittington
+62898,Woodlawn
+62899,Xenia
+62901,Carbondale
+62902,Carbondale
+62903,Carbondale
+62905,Alto Pass
+62906,Anna
+62907,Ava
+62908,Belknap
+62909,Boles
+62910,Brookport
+62912,Buncombe
+62913,Cache
+62914,Cairo
+62915,Cambria
+62916,Campbell Hill
+62917,Carrier Mills
+62918,Carterville
+62919,Cave in Rock
+62920,Cobden
+62921,Colp
+62922,Creal Springs
+62923,Cypress
+62924,De Soto
+62926,Dongola
+62927,Dowell
+62928,Eddyville
+62930,Eldorado
+62931,Elizabethtown
+62932,Elkville
+62933,Energy
+62934,Equality
+62935,Galatia
+62938,Golconda
+62939,Goreville
+62940,Gorham
+62941,Grand Chain
+62942,Grand Tower
+62943,Grantsburg
+62944,Hamletsburg
+62946,Harrisburg
+62947,Herod
+62948,Herrin
+62949,Hurst
+62950,Jacob
+62951,Johnston City
+62952,Jonesboro
+62953,Joppa
+62954,Junction
+62955,Karbers Ridge
+62956,Karnak
+62957,Mc Clure
+62958,Makanda
+62959,Marion
+62960,Metropolis
+62961,Millcreek
+62962,Miller City
+62963,Mound City
+62964,Mounds
+62965,Muddy
+62966,Murphysboro
+62967,New Burnside
+62969,Olive Branch
+62970,Olmsted
+62971,Oraville
+62972,Ozark
+62973,Perks
+62974,Pittsburg
+62975,Pomona
+62976,Pulaski
+62977,Raleigh
+62979,Ridgway
+62982,Rosiclare
+62983,Royalton
+62984,Shawneetown
+62985,Simpson
+62987,Stonefort
+62988,Tamms
+62990,Thebes
+62991,Tunnel Hill
+62992,Ullin
+62993,Unity
+62994,Vergennes
+62995,Vienna
+62996,Villa Ridge
+62997,Willisville
+62998,Wolf Lake
+62999,Zeigler
+83201,Pocatello
+83202,Pocatello
+83203,Fort Hall
+83204,Pocatello
+83205,Pocatello
+83206,Pocatello
+83209,Pocatello
+83210,Aberdeen
+83211,American Falls
+83212,Arbon
+83213,Arco
+83214,Arimo
+83215,Atomic City
+83217,Bancroft
+83218,Basalt
+83220,Bern
+83221,Blackfoot
+83223,Bloomington
+83226,Challis
+83227,Clayton
+83228,Clifton
+83229,Cobalt
+83230,Conda
+83232,Dayton
+83233,Dingle
+83234,Downey
+83235,Ellis
+83236,Firth
+83237,Franklin
+83238,Geneva
+83239,Georgetown
+83241,Grace
+83243,Holbrook
+83244,Howe
+83245,Inkom
+83246,Lava Hot Springs
+83250,Mc Cammon
+83251,Mackay
+83252,Malad City
+83253,May
+83254,Montpelier
+83255,Moore
+83256,Moreland
+83261,Paris
+83262,Pingree
+83263,Preston
+83271,Rockland
+83272,Saint Charles
+83274,Shelley
+83276,Soda Springs
+83277,Springfield
+83278,Stanley
+83281,Swanlake
+83283,Thatcher
+83285,Wayan
+83286,Weston
+83287,Fish Haven
+83301,Twin Falls
+83302,Rogerson
+83303,Twin Falls
+83311,Albion
+83312,Almo
+83313,Bellevue
+83314,Bliss
+83316,Buhl
+83318,Burley
+83320,Carey
+83321,Castleford
+83322,Corral
+83323,Declo
+83324,Dietrich
+83325,Eden
+83327,Fairfield
+83328,Filer
+83330,Gooding
+83332,Hagerman
+83333,Hailey
+83334,Hansen
+83335,Hazelton
+83336,Heyburn
+83337,Hill City
+83338,Jerome
+83340,Ketchum
+83341,Kimberly
+83342,Malta
+83343,Minidoka
+83344,Murtaugh
+83346,Oakley
+83347,Paul
+83348,Picabo
+83349,Richfield
+83350,Rupert
+83352,Shoshone
+83353,Sun Valley
+83354,Sun Valley
+83355,Wendell
+83401,Idaho Falls
+83402,Idaho Falls
+83403,Idaho Falls
+83404,Idaho Falls
+83405,Idaho Falls
+83406,Idaho Falls
+83415,Idaho Falls
+83420,Ashton
+83421,Chester
+83422,Driggs
+83423,Dubois
+83424,Felt
+83425,Hamer
+83427,Iona
+83428,Irwin
+83429,Island Park
+83431,Lewisville
+83433,Macks Inn
+83434,Menan
+83435,Monteview
+83436,Newdale
+83438,Parker
+83440,Rexburg
+83441,Rexburg
+83442,Rigby
+83443,Ririe
+83444,Roberts
+83445,Saint Anthony
+83446,Spencer
+83447,Squirrel
+83448,Sugar City
+83449,Swan Valley
+83450,Terreton
+83451,Teton
+83452,Tetonia
+83454,Ucon
+83455,Victor
+83460,Rexburg
+83462,Carmen
+83463,Gibbonsville
+83464,Leadore
+83465,Lemhi
+83466,North Fork
+83467,Salmon
+83468,Tendoy
+83469,Shoup
+83501,Lewiston
+83520,Ahsahka
+83522,Cottonwood
+83523,Craigmont
+83524,Culdesac
+83525,Elk City
+83526,Ferdinand
+83530,Grangeville
+83531,Fenn
+83533,Greencreek
+83535,Juliaetta
+83536,Kamiah
+83537,Kendrick
+83539,Kooskia
+83540,Lapwai
+83541,Lenore
+83542,Lucile
+83543,Nezperce
+83544,Orofino
+83545,Peck
+83546,Pierce
+83547,Pollock
+83548,Reubens
+83549,Riggins
+83551,Spalding
+83552,Stites
+83553,Weippe
+83554,White Bird
+83555,Winchester
+83601,Atlanta
+83602,Banks
+83604,Bruneau
+83605,Caldwell
+83606,Caldwell
+83607,Caldwell
+83610,Cambridge
+83611,Cascade
+83612,Council
+83615,Donnelly
+83616,Eagle
+83617,Emmett
+83619,Fruitland
+83620,Fruitvale
+83622,Garden Valley
+83623,Glenns Ferry
+83624,Grand View
+83626,Greenleaf
+83627,Hammett
+83628,Homedale
+83629,Horseshoe Bend
+83630,Huston
+83631,Idaho City
+83632,Indian Valley
+83633,King Hill
+83634,Kuna
+83635,Lake Fork
+83636,Letha
+83637,Lowman
+83638,Mc Call
+83639,Marsing
+83641,Melba
+83642,Meridian
+83643,Mesa
+83644,Middleton
+83645,Midvale
+83647,Mountain Home
+83648,Mountain Home A F B
+83650,Murphy
+83651,Nampa
+83652,Nampa
+83653,Nampa
+83654,New Meadows
+83655,New Plymouth
+83656,Notus
+83657,Ola
+83660,Parma
+83661,Payette
+83666,Placerville
+83669,Star
+83670,Sweet
+83671,Warren
+83672,Weiser
+83676,Wilder
+83677,Yellow Pine
+83680,Meridian
+83686,Nampa
+83687,Nampa
+83701,Boise
+83702,Boise
+83703,Boise
+83704,Boise
+83705,Boise
+83706,Boise
+83707,Boise
+83708,Boise
+83709,Boise
+83711,Boise
+83712,Boise
+83713,Boise
+83714,Boise
+83715,Boise
+83716,Boise
+83717,Boise
+83719,Boise
+83720,Boise
+83721,Boise
+83722,Boise
+83723,Boise
+83724,Boise
+83725,Boise
+83726,Boise
+83727,Boise
+83728,Boise
+83729,Boise
+83730,Boise
+83731,Boise
+83732,Boise
+83733,Boise
+83735,Boise
+83744,Boise
+83756,Boise
+83757,Boise
+83788,Boise
+83799,Boise
+83801,Athol
+83802,Avery
+83803,Bayview
+83804,Blanchard
+83805,Bonners Ferry
+83806,Bovill
+83808,Calder
+83809,Careywood
+83810,Cataldo
+83811,Clark Fork
+83812,Clarkia
+83813,Cocolalla
+83814,Coeur d Alene
+83815,Coeur d Alene
+83816,Coeur d Alene
+83821,Coolin
+83822,Oldtown
+83823,Deary
+83824,Desmet
+83825,Dover
+83826,Eastport
+83827,Elk River
+83830,Fernwood
+83832,Genesee
+83833,Harrison
+83834,Harvard
+83835,Hayden
+83836,Hope
+83837,Kellogg
+83839,Kingston
+83840,Kootenai
+83841,Laclede
+83842,Medimont
+83843,Moscow
+83844,Moscow
+83845,Moyie Springs
+83846,Mullan
+83847,Naples
+83848,Nordman
+83849,Osburn
+83850,Pinehurst
+83851,Plummer
+83852,Ponderay
+83853,Porthill
+83854,Post Falls
+83855,Potlatch
+83856,Priest River
+83857,Princeton
+83858,Rathdrum
+83860,Sagle
+83861,Saint Maries
+83862,Samuels
+83864,Sandpoint
+83865,Colburn
+83866,Santa
+83867,Silverton
+83868,Smelterville
+83869,Spirit Lake
+83870,Tensed
+83871,Troy
+83872,Viola
+83873,Wallace
+83874,Murray
+83876,Worley
+83877,Post Falls
+83888,Sandpoint
+50001,Ackworth
+50002,Adair
+50003,Adel
+50005,Albion
+50006,Alden
+50007,Alleman
+50008,Allerton
+50009,Altoona
+50010,Ames
+50011,Ames
+50012,Ames
+50013,Ames
+50014,Ames
+50015,Ankeny
+50020,Anita
+50021,Ankeny
+50022,Atlantic
+50025,Audubon
+50026,Bagley
+50027,Barnes City
+50028,Baxter
+50029,Bayard
+50031,Beaver
+50032,Berwick
+50033,Bevington
+50034,Blairsburg
+50035,Bondurant
+50036,Boone
+50037,Boone
+50038,Booneville
+50039,Bouton
+50040,Boxholm
+50041,Bradford
+50042,Brayton
+50043,Buckeye
+50044,Bussey
+50046,Cambridge
+50047,Carlisle
+50048,Casey
+50049,Chariton
+50050,Churdan
+50051,Clemons
+50052,Clio
+50054,Colfax
+50055,Collins
+50056,Colo
+50057,Columbia
+50058,Coon Rapids
+50059,Cooper
+50060,Corydon
+50061,Cumming
+50062,Dallas
+50063,Dallas Center
+50064,Dana
+50065,Davis City
+50066,Dawson
+50067,Decatur
+50068,Derby
+50069,De Soto
+50070,Dexter
+50071,Dows
+50072,Earlham
+50073,Elkhart
+50074,Ellston
+50075,Ellsworth
+50076,Exira
+50078,Ferguson
+50101,Galt
+50102,Garden City
+50103,Garden Grove
+50104,Gibson
+50105,Gilbert
+50106,Gilman
+50107,Grand Junction
+50108,Grand River
+50109,Granger
+50110,Gray
+50111,Grimes
+50112,Grinnell
+50115,Guthrie Center
+50116,Hamilton
+50117,Hamlin
+50118,Hartford
+50119,Harvey
+50120,Haverhill
+50122,Hubbard
+50123,Humeston
+50124,Huxley
+50125,Indianola
+50126,Iowa Falls
+50127,Ira
+50128,Jamaica
+50129,Jefferson
+50130,Jewell
+50131,Johnston
+50132,Kamrar
+50133,Kellerton
+50134,Kelley
+50135,Kellogg
+50136,Keswick
+50137,Killduff
+50138,Knoxville
+50139,Lacona
+50140,Lamoni
+50141,Laurel
+50142,Le Grand
+50143,Leighton
+50144,Leon
+50145,Liberty Center
+50146,Linden
+50147,Lineville
+50148,Liscomb
+50149,Lorimor
+50150,Lovilia
+50151,Lucas
+50152,Luther
+50153,Lynnville
+50154,Mc Callsburg
+50155,Macksburg
+50156,Madrid
+50157,Malcom
+50158,Marshalltown
+50160,Martensdale
+50161,Maxwell
+50162,Melbourne
+50163,Melcher
+50164,Menlo
+50165,Millerton
+50166,Milo
+50167,Minburn
+50168,Mingo
+50169,Mitchellville
+50170,Monroe
+50171,Montezuma
+50173,Montour
+50174,Murray
+50177,Grinnell
+50197,Knoxville
+50198,Knoxville
+50201,Nevada
+50206,New Providence
+50207,New Sharon
+50208,Newton
+50210,New Virginia
+50211,Norwalk
+50212,Ogden
+50213,Osceola
+50214,Otley
+50216,Panora
+50217,Paton
+50218,Patterson
+50219,Pella
+50220,Perry
+50222,Peru
+50223,Pilot Mound
+50225,Pleasantville
+50226,Polk City
+50227,Popejoy
+50228,Prairie City
+50229,Prole
+50230,Radcliffe
+50231,Randall
+50232,Reasnor
+50233,Redfield
+50234,Rhodes
+50235,Rippey
+50236,Roland
+50237,Runnells
+50238,Russell
+50239,Saint Anthony
+50240,Saint Charles
+50241,Saint Marys
+50242,Searsboro
+50243,Sheldahl
+50244,Slater
+50246,Stanhope
+50247,State Center
+50248,Story City
+50249,Stratford
+50250,Stuart
+50251,Sully
+50252,Swan
+50254,Thayer
+50255,Thornburg
+50256,Tracy
+50257,Truro
+50258,Union
+50259,Gifford
+50261,Van Meter
+50262,Van Wert
+50263,Waukee
+50264,Weldon
+50265,West Des Moines
+50266,West Des Moines
+50268,What Cheer
+50269,Whitten
+50271,Williams
+50272,Williamson
+50273,Winterset
+50274,Wiota
+50275,Woodburn
+50276,Woodward
+50277,Yale
+50278,Zearing
+50301,Des Moines
+50302,Des Moines
+50303,Des Moines
+50304,Des Moines
+50305,Des Moines
+50306,Des Moines
+50307,Des Moines
+50308,Des Moines
+50309,Des Moines
+50310,Des Moines
+50311,Des Moines
+50312,Des Moines
+50313,Des Moines
+50314,Des Moines
+50315,Des Moines
+50316,Des Moines
+50317,Des Moines
+50318,Des Moines
+50319,Des Moines
+50320,Des Moines
+50321,Des Moines
+50322,Urbandale
+50323,Urbandale
+50325,Clive
+50328,Des Moines
+50329,Des Moines
+50330,Des Moines
+50331,Des Moines
+50332,Des Moines
+50333,Des Moines
+50334,Des Moines
+50335,Des Moines
+50336,Des Moines
+50338,Des Moines
+50339,Des Moines
+50340,Des Moines
+50347,Des Moines
+50350,Des Moines
+50359,Des Moines
+50360,Des Moines
+50361,Des Moines
+50362,Des Moines
+50363,Des Moines
+50364,Des Moines
+50367,Des Moines
+50368,Des Moines
+50369,Des Moines
+50380,Des Moines
+50381,Des Moines
+50391,Des Moines
+50392,Des Moines
+50393,Des Moines
+50394,Des Moines
+50395,Des Moines
+50396,Des Moines
+50397,Des Moines
+50398,West Des Moines
+50401,Mason City
+50402,Mason City
+50420,Alexander
+50421,Belmond
+50423,Britt
+50424,Buffalo Center
+50426,Carpenter
+50427,Chapin
+50428,Clear Lake
+50430,Corwith
+50431,Coulter
+50432,Crystal Lake
+50433,Dougherty
+50434,Fertile
+50435,Floyd
+50436,Forest City
+50438,Garner
+50439,Goodell
+50440,Grafton
+50441,Hampton
+50444,Hanlontown
+50446,Joice
+50447,Kanawha
+50448,Kensett
+50449,Klemme
+50450,Lake Mills
+50451,Lakota
+50452,Latimer
+50453,Leland
+50454,Little Cedar
+50455,Mc Intire
+50456,Manly
+50457,Meservey
+50458,Nora Springs
+50459,Northwood
+50460,Orchard
+50461,Osage
+50464,Plymouth
+50465,Rake
+50466,Riceville
+50467,Rock Falls
+50468,Rockford
+50469,Rockwell
+50470,Rowan
+50471,Rudd
+50472,Saint Ansgar
+50473,Scarville
+50475,Sheffield
+50476,Stacyville
+50477,Swaledale
+50478,Thompson
+50479,Thornton
+50480,Titonka
+50481,Toeterville
+50482,Ventura
+50483,Wesley
+50484,Woden
+50501,Fort Dodge
+50510,Albert City
+50511,Algona
+50514,Armstrong
+50515,Ayrshire
+50516,Badger
+50517,Bancroft
+50518,Barnum
+50519,Bode
+50520,Bradgate
+50521,Burnside
+50522,Burt
+50523,Callender
+50524,Clare
+50525,Clarion
+50526,Clarion
+50527,Curlew
+50528,Cylinder
+50529,Dakota City
+50530,Dayton
+50531,Dolliver
+50532,Duncombe
+50533,Eagle Grove
+50535,Early
+50536,Emmetsburg
+50538,Farnhamville
+50539,Fenton
+50540,Fonda
+50541,Gilmore City
+50542,Goldfield
+50543,Gowrie
+50544,Harcourt
+50545,Hardy
+50546,Havelock
+50548,Humboldt
+50551,Jolley
+50552,Knierim
+50554,Laurens
+50556,Ledyard
+50557,Lehigh
+50558,Livermore
+50559,Lone Rock
+50560,Lu Verne
+50561,Lytton
+50562,Mallard
+50563,Manson
+50565,Marathon
+50566,Moorland
+50567,Nemaha
+50568,Newell
+50569,Otho
+50570,Ottosen
+50571,Palmer
+50573,Plover
+50574,Pocahontas
+50575,Pomeroy
+50576,Rembrandt
+50577,Renwick
+50578,Ringsted
+50579,Rockwell City
+50581,Rolfe
+50582,Rutland
+50583,Sac City
+50585,Sioux Rapids
+50586,Somers
+50588,Storm Lake
+50590,Swea City
+50591,Thor
+50592,Truesdale
+50593,Varina
+50594,Vincent
+50595,Webster City
+50597,West Bend
+50598,Whittemore
+50599,Woolstock
+50601,Ackley
+50602,Allison
+50603,Alta Vista
+50604,Aplington
+50605,Aredale
+50606,Arlington
+50607,Aurora
+50608,Austinville
+50609,Beaman
+50611,Bristow
+50612,Buckingham
+50613,Cedar Falls
+50614,Cedar Falls
+50616,Charles City
+50619,Clarksville
+50620,Colwell
+50621,Conrad
+50622,Denver
+50623,Dewar
+50624,Dike
+50625,Dumont
+50626,Dunkerton
+50627,Eldora
+50628,Elma
+50629,Fairbank
+50630,Fredericksburg
+50631,Frederika
+50632,Garwin
+50633,Geneva
+50634,Gilbertville
+50635,Gladbrook
+50636,Greene
+50638,Grundy Center
+50641,Hazleton
+50642,Holland
+50643,Hudson
+50644,Independence
+50645,Ionia
+50647,Janesville
+50648,Jesup
+50649,Kesley
+50650,Lamont
+50651,La Porte City
+50652,Lincoln
+50653,Marble Rock
+50654,Masonville
+50655,Maynard
+50657,Morrison
+50658,Nashua
+50659,New Hampton
+50660,New Hartford
+50661,North Washington
+50662,Oelwein
+50664,Oran
+50665,Parkersburg
+50666,Plainfield
+50667,Raymond
+50668,Readlyn
+50669,Reinbeck
+50670,Shell Rock
+50671,Stanley
+50672,Steamboat Rock
+50673,Stout
+50674,Sumner
+50675,Traer
+50676,Tripoli
+50677,Waverly
+50680,Wellsburg
+50681,Westgate
+50682,Winthrop
+50701,Waterloo
+50702,Waterloo
+50703,Waterloo
+50704,Waterloo
+50706,Waterloo
+50707,Evansdale
+50799,Waterloo
+50801,Creston
+50830,Afton
+50831,Arispe
+50833,Bedford
+50835,Benton
+50836,Blockton
+50837,Bridgewater
+50839,Carbon
+50840,Clearfield
+50841,Corning
+50842,Cromwell
+50843,Cumberland
+50845,Diagonal
+50846,Fontanelle
+50847,Grant
+50848,Gravity
+50849,Greenfield
+50851,Lenox
+50853,Massena
+50854,Mount Ayr
+50857,Nodaway
+50858,Orient
+50859,Prescott
+50860,Redding
+50861,Shannon City
+50862,Sharpsburg
+50863,Tingley
+50864,Villisca
+50936,Des Moines
+50940,Des Moines
+50947,Des Moines
+50950,Des Moines
+50980,Des Moines
+50981,Des Moines
+51001,Akron
+51002,Alta
+51003,Alton
+51004,Anthon
+51005,Aurelia
+51006,Battle Creek
+51007,Bronson
+51008,Brunsville
+51009,Calumet
+51010,Castana
+51011,Chatsworth
+51012,Cherokee
+51014,Cleghorn
+51015,Climbing Hill
+51016,Correctionville
+51017,Craig
+51018,Cushing
+51019,Danbury
+51020,Galva
+51022,Granville
+51023,Hawarden
+51024,Hinton
+51025,Holstein
+51026,Hornick
+51027,Ireton
+51028,Kingsley
+51029,Larrabee
+51030,Lawton
+51031,Le Mars
+51033,Linn Grove
+51034,Mapleton
+51035,Marcus
+51036,Maurice
+51037,Meriden
+51038,Merrill
+51039,Moville
+51040,Onawa
+51041,Orange City
+51044,Oto
+51045,Oyens
+51046,Paullina
+51047,Peterson
+51048,Pierson
+51049,Quimby
+51050,Remsen
+51051,Rodney
+51052,Salix
+51053,Schaller
+51054,Sergeant Bluff
+51055,Sloan
+51056,Smithland
+51058,Sutherland
+51059,Turin
+51060,Ute
+51061,Washta
+51062,Westfield
+51063,Whiting
+51101,Sioux City
+51102,Sioux City
+51103,Sioux City
+51104,Sioux City
+51105,Sioux City
+51106,Sioux City
+51108,Sioux City
+51109,Sioux City
+51111,Sioux City
+51201,Sheldon
+51230,Alvord
+51231,Archer
+51232,Ashton
+51234,Boyden
+51235,Doon
+51237,George
+51238,Hospers
+51239,Hull
+51240,Inwood
+51241,Larchwood
+51242,Lester
+51243,Little Rock
+51244,Matlock
+51245,Primghar
+51246,Rock Rapids
+51247,Rock Valley
+51248,Sanborn
+51249,Sibley
+51250,Sioux Center
+51301,Spencer
+51330,Allendorf
+51331,Arnolds Park
+51333,Dickens
+51334,Estherville
+51338,Everly
+51340,Fostoria
+51341,Gillett Grove
+51342,Graettinger
+51343,Greenville
+51344,Gruver
+51345,Harris
+51346,Hartley
+51347,Lake Park
+51349,May City
+51350,Melvin
+51351,Milford
+51354,Ocheyedan
+51355,Okoboji
+51357,Royal
+51358,Ruthven
+51360,Spirit Lake
+51363,Superior
+51364,Terril
+51365,Wallingford
+51366,Webb
+51401,Carroll
+51430,Arcadia
+51431,Arthur
+51432,Aspinwall
+51433,Auburn
+51436,Breda
+51439,Charter Oak
+51440,Dedham
+51441,Deloit
+51442,Denison
+51443,Glidden
+51444,Halbur
+51445,Ida Grove
+51446,Irwin
+51447,Kirkman
+51448,Kiron
+51449,Lake City
+51450,Lake View
+51451,Lanesboro
+51452,Lidderdale
+51453,Lohrville
+51454,Manilla
+51455,Manning
+51458,Odebolt
+51459,Ralston
+51460,Ricketts
+51461,Schleswig
+51462,Scranton
+51463,Templeton
+51465,Vail
+51466,Wall Lake
+51467,Westside
+51501,Council Bluffs
+51502,Council Bluffs
+51503,Council Bluffs
+51510,Carter Lake
+51520,Arion
+51521,Avoca
+51523,Blencoe
+51525,Carson
+51526,Crescent
+51527,Defiance
+51528,Dow City
+51529,Dunlap
+51530,Earling
+51531,Elk Horn
+51532,Elliott
+51533,Emerson
+51534,Glenwood
+51535,Griswold
+51536,Hancock
+51537,Harlan
+51540,Hastings
+51541,Henderson
+51542,Honey Creek
+51543,Kimballton
+51544,Lewis
+51545,Little Sioux
+51546,Logan
+51548,Mc Clelland
+51549,Macedonia
+51550,Magnolia
+51551,Malvern
+51552,Marne
+51553,Minden
+51554,Mineola
+51555,Missouri Valley
+51556,Modale
+51557,Mondamin
+51558,Moorhead
+51559,Neola
+51560,Oakland
+51561,Pacific Junction
+51562,Panama
+51563,Persia
+51564,Pisgah
+51565,Portsmouth
+51566,Red Oak
+51570,Shelby
+51571,Silver City
+51572,Soldier
+51573,Stanton
+51574,Tennant
+51575,Treynor
+51576,Underwood
+51577,Walnut
+51578,Westphalia
+51579,Woodbine
+51591,Red Oak
+51593,Harlan
+51601,Shenandoah
+51602,Shenandoah
+51603,Shenandoah
+51630,Blanchard
+51631,Braddyville
+51632,Clarinda
+51636,Coin
+51637,College Springs
+51638,Essex
+51639,Farragut
+51640,Hamburg
+51645,Imogene
+51646,New Market
+51647,Northboro
+51648,Percival
+51649,Randolph
+51650,Riverton
+51651,Shambaugh
+51652,Sidney
+51653,Tabor
+51654,Thurman
+51656,Yorktown
+52001,Dubuque
+52002,Dubuque
+52003,Dubuque
+52004,Dubuque
+52030,Andrew
+52031,Bellevue
+52032,Bernard
+52033,Cascade
+52035,Colesburg
+52036,Delaware
+52037,Delmar
+52038,Dundee
+52039,Durango
+52040,Dyersville
+52041,Earlville
+52042,Edgewood
+52043,Elkader
+52044,Elkport
+52045,Epworth
+52046,Farley
+52047,Farmersburg
+52048,Garber
+52049,Garnavillo
+52050,Greeley
+52052,Guttenberg
+52053,Holy Cross
+52054,La Motte
+52055,Littleport
+52056,Luxemburg
+52057,Manchester
+52060,Maquoketa
+52064,Miles
+52065,New Vienna
+52066,North Buena Vista
+52068,Peosta
+52069,Preston
+52070,Sabula
+52071,Saint Donatus
+52072,Saint Olaf
+52073,Sherrill
+52074,Spragueville
+52075,Springbrook
+52076,Strawberry Point
+52077,Volga
+52078,Worthington
+52079,Zwingle
+52099,Dubuque
+52101,Decorah
+52131,Burr Oak
+52132,Calmar
+52133,Castalia
+52134,Chester
+52135,Clermont
+52136,Cresco
+52140,Dorchester
+52141,Elgin
+52142,Fayette
+52144,Fort Atkinson
+52146,Harpers Ferry
+52147,Hawkeye
+52149,Highlandville
+52151,Lansing
+52154,Lawler
+52155,Lime Springs
+52156,Luana
+52157,Mc Gregor
+52158,Marquette
+52159,Monona
+52160,New Albin
+52161,Ossian
+52162,Postville
+52163,Protivin
+52164,Randalia
+52165,Ridgeway
+52166,Saint Lucas
+52168,Spillville
+52169,Wadena
+52170,Waterville
+52171,Waucoma
+52172,Waukon
+52175,West Union
+52201,Ainsworth
+52202,Alburnett
+52203,Amana
+52204,Amana
+52205,Anamosa
+52206,Atkins
+52207,Baldwin
+52208,Belle Plaine
+52209,Blairstown
+52210,Brandon
+52211,Brooklyn
+52212,Center Junction
+52213,Center Point
+52214,Central City
+52215,Chelsea
+52216,Clarence
+52217,Clutier
+52218,Coggon
+52219,Prairieburg
+52220,Conroy
+52221,Guernsey
+52222,Deep River
+52223,Delhi
+52224,Dysart
+52225,Elberon
+52226,Elwood
+52227,Ely
+52228,Fairfax
+52229,Garrison
+52231,Harper
+52232,Hartwick
+52233,Hiawatha
+52235,Hills
+52236,Homestead
+52237,Hopkinton
+52240,Iowa City
+52241,Coralville
+52242,Iowa City
+52243,Iowa City
+52244,Iowa City
+52245,Iowa City
+52246,Iowa City
+52247,Kalona
+52248,Keota
+52249,Keystone
+52251,Ladora
+52252,Langworthy
+52253,Lisbon
+52254,Lost Nation
+52255,Lowden
+52257,Luzerne
+52301,Marengo
+52302,Marion
+52305,Martelle
+52306,Mechanicsville
+52307,Middle Amana
+52308,Millersburg
+52309,Monmouth
+52310,Monticello
+52312,Morley
+52313,Mount Auburn
+52314,Mount Vernon
+52315,Newhall
+52316,North English
+52317,North Liberty
+52318,Norway
+52319,Oakdale
+52320,Olin
+52321,Onslow
+52322,Oxford
+52323,Oxford Junction
+52324,Palo
+52325,Parnell
+52326,Quasqueton
+52327,Riverside
+52328,Robins
+52329,Rowley
+52330,Ryan
+52332,Shellsburg
+52333,Solon
+52334,South Amana
+52335,South English
+52336,Springville
+52337,Stanwood
+52338,Swisher
+52339,Tama
+52340,Tiffin
+52341,Toddville
+52342,Toledo
+52344,Troy Mills
+52345,Urbana
+52346,Van Horne
+52347,Victor
+52348,Vining
+52349,Vinton
+52350,Viola
+52351,Walford
+52352,Walker
+52353,Washington
+52354,Watkins
+52355,Webster
+52356,Wellman
+52358,West Branch
+52359,West Chester
+52361,Williamsburg
+52362,Wyoming
+52401,Cedar Rapids
+52402,Cedar Rapids
+52403,Cedar Rapids
+52404,Cedar Rapids
+52405,Cedar Rapids
+52406,Cedar Rapids
+52407,Cedar Rapids
+52408,Cedar Rapids
+52409,Cedar Rapids
+52410,Cedar Rapids
+52411,Cedar Rapids
+52497,Cedar Rapids
+52498,Cedar Rapids
+52499,Cedar Rapids
+52501,Ottumwa
+52530,Agency
+52531,Albia
+52533,Batavia
+52534,Beacon
+52535,Birmingham
+52536,Blakesburg
+52537,Bloomfield
+52538,West Grove
+52540,Brighton
+52542,Cantril
+52543,Cedar
+52544,Centerville
+52548,Chillicothe
+52549,Cincinnati
+52550,Delta
+52551,Douds
+52552,Drakesville
+52553,Eddyville
+52554,Eldon
+52555,Exline
+52556,Fairfield
+52557,Fairfield
+52560,Floris
+52561,Fremont
+52562,Hayesville
+52563,Hedrick
+52565,Keosauqua
+52566,Kirkville
+52567,Libertyville
+52568,Martinsburg
+52569,Melrose
+52570,Milton
+52571,Moravia
+52572,Moulton
+52573,Mount Sterling
+52574,Mystic
+52576,Ollie
+52577,Oskaloosa
+52580,Packwood
+52581,Plano
+52583,Promise City
+52584,Pulaski
+52585,Richland
+52586,Rose Hill
+52588,Selma
+52590,Seymour
+52591,Sigourney
+52593,Udell
+52594,Unionville
+52595,University Park
+52601,Burlington
+52619,Argyle
+52620,Bonaparte
+52621,Crawfordsville
+52623,Danville
+52624,Denmark
+52625,Donnellson
+52626,Farmington
+52627,Fort Madison
+52630,Hillsboro
+52631,Houghton
+52632,Keokuk
+52635,Lockridge
+52637,Mediapolis
+52638,Middletown
+52639,Montrose
+52640,Morning Sun
+52641,Mount Pleasant
+52642,Rome
+52644,Mount Union
+52645,New London
+52646,Oakville
+52647,Olds
+52648,Pilot Grove
+52649,Salem
+52650,Sperry
+52651,Stockport
+52652,Swedesburg
+52653,Wapello
+52654,Wayland
+52655,West Burlington
+52656,West Point
+52657,Saint Paul
+52658,Wever
+52659,Winfield
+52660,Yarmouth
+52701,Andover
+52720,Atalissa
+52721,Bennett
+52722,Bettendorf
+52726,Blue Grass
+52727,Bryant
+52728,Buffalo
+52729,Calamus
+52730,Camanche
+52731,Charlotte
+52732,Clinton
+52733,Clinton
+52736,Clinton
+52737,Columbus City
+52738,Columbus Junction
+52739,Conesville
+52742,De Witt
+52745,Dixon
+52746,Donahue
+52747,Durant
+52748,Eldridge
+52749,Fruitland
+52750,Goose Lake
+52751,Grand Mound
+52752,Grandview
+52753,Le Claire
+52754,Letts
+52755,Lone Tree
+52756,Long Grove
+52757,Low Moor
+52758,Mc Causland
+52759,Montpelier
+52760,Moscow
+52761,Muscatine
+52765,New Liberty
+52766,Nichols
+52767,Pleasant Valley
+52768,Princeton
+52769,Stockton
+52771,Teeds Grove
+52772,Tipton
+52773,Walcott
+52774,Welton
+52776,West Liberty
+52777,Wheatland
+52778,Wilton
+52801,Davenport
+52802,Davenport
+52803,Davenport
+52804,Davenport
+52805,Davenport
+52806,Davenport
+52807,Davenport
+52808,Davenport
+52809,Davenport
+96701,Aiea
+96703,Anahola
+96704,Captain Cook
+96705,Eleele
+96706,Ewa Beach
+96707,Kapolei
+96708,Haiku
+96709,Kapolei
+96710,Hakalau
+96712,Haleiwa
+96713,Hana
+96714,Hanalei
+96715,Hanamaulu
+96716,Hanapepe
+96717,Hauula
+96718,Hawaii National Park
+96719,Hawi
+96720,Hilo
+96721,Hilo
+96722,Princeville
+96725,Holualoa
+96726,Honaunau
+96727,Honokaa
+96728,Honomu
+96729,Hoolehua
+96730,Kaaawa
+96731,Kahuku
+96732,Kahului
+96733,Kahului
+96734,Kailua
+96737,Ocean View
+96738,Waikoloa
+96739,Keauhou
+96740,Kailua Kona
+96741,Kalaheo
+96742,Kalaupapa
+96743,Kamuela
+96744,Kaneohe
+96745,Kailua Kona
+96746,Kapaa
+96747,Kaumakani
+96748,Kaunakakai
+96749,Keaau
+96750,Kealakekua
+96751,Kealia
+96752,Kekaha
+96753,Kihei
+96754,Kilauea
+96755,Kapaau
+96756,Koloa
+96757,Kualapuu
+96759,Kunia
+96760,Kurtistown
+96761,Lahaina
+96762,Laie
+96763,Lanai City
+96764,Laupahoehoe
+96765,Lawai
+96766,Lihue
+96767,Lahaina
+96768,Makawao
+96769,Makaweli
+96770,Maunaloa
+96771,Mountain View
+96772,Naalehu
+96773,Ninole
+96774,Ookala
+96775,Paauhau
+96776,Paauilo
+96777,Pahala
+96778,Pahoa
+96779,Paia
+96780,Papaaloa
+96781,Papaikou
+96782,Pearl City
+96783,Pepeekeo
+96784,Puunene
+96785,Volcano
+96786,Wahiawa
+96788,Pukalani
+96789,Mililani
+96790,Kula
+96791,Waialua
+96792,Waianae
+96793,Wailuku
+96795,Waimanalo
+96796,Waimea
+96797,Waipahu
+96801,Honolulu
+96802,Honolulu
+96803,Honolulu
+96804,Honolulu
+96805,Honolulu
+96806,Honolulu
+96807,Honolulu
+96808,Honolulu
+96809,Honolulu
+96810,Honolulu
+96811,Honolulu
+96812,Honolulu
+96813,Honolulu
+96814,Honolulu
+96815,Honolulu
+96816,Honolulu
+96817,Honolulu
+96818,Honolulu
+96819,Honolulu
+96820,Honolulu
+96821,Honolulu
+96822,Honolulu
+96823,Honolulu
+96824,Honolulu
+96825,Honolulu
+96826,Honolulu
+96827,Honolulu
+96828,Honolulu
+96830,Honolulu
+96835,Honolulu
+96836,Honolulu
+96837,Honolulu
+96838,Honolulu
+96839,Honolulu
+96840,Honolulu
+96841,Honolulu
+96842,Honolulu
+96843,Honolulu
+96844,Honolulu
+96845,Honolulu
+96846,Honolulu
+96847,Honolulu
+96848,Honolulu
+96849,Honolulu
+96850,Honolulu
+96853,Hickam AFB
+96854,Wheeler Army Airfield
+96857,Schofield Barracks
+96858,Fort Shafter
+96859,Tripler Army Medical Ctr
+96860,Pearl Harbor
+96861,Camp H M Smith
+96862,Barbers Point N A S
+96863,M C B H Kaneohe Bay
+96898,Wake Island
+96910,Hagatna
+96911,Tamuning
+96912,Dededo
+96913,Barrigada
+96914,Yona
+96915,Santa Rita
+96916,Merizo
+96917,Inarajan
+96918,Umatac
+96919,Agana Heights
+96921,Barrigada
+96922,Asan
+96923,Mangilao
+96925,Piti
+96926,Sinajana
+96927,Mongmong
+96928,Agat
+96929,Yigo
+96930,Talofofo
+96931,Tamuning
+96932,Hagatna
+30002,Avondale Estates
+30003,Norcross
+30004,Alpharetta
+30005,Alpharetta
+30006,Marietta
+30007,Marietta
+30008,Marietta
+30009,Alpharetta
+30010,Norcross
+30011,Auburn
+30012,Conyers
+30013,Conyers
+30014,Covington
+30015,Covington
+30016,Covington
+30017,Grayson
+30018,Jersey
+30019,Dacula
+30021,Clarkston
+30022,Alpharetta
+30023,Alpharetta
+30024,Suwanee
+30025,Social Circle
+30026,Duluth
+30028,Cumming
+30029,Duluth
+30030,Decatur
+30031,Decatur
+30032,Decatur
+30033,Decatur
+30034,Decatur
+30035,Decatur
+30036,Decatur
+30037,Decatur
+30038,Lithonia
+30039,Snellville
+30040,Cumming
+30041,Cumming
+30042,Lawrenceville
+30043,Lawrenceville
+30044,Lawrenceville
+30045,Lawrenceville
+30046,Lawrenceville
+30047,Lilburn
+30048,Lilburn
+30052,Loganville
+30054,Oxford
+30055,Mansfield
+30056,Newborn
+30058,Lithonia
+30060,Marietta
+30061,Marietta
+30062,Marietta
+30063,Marietta
+30064,Marietta
+30065,Marietta
+30066,Marietta
+30067,Marietta
+30068,Marietta
+30069,Marietta
+30070,Porterdale
+30071,Norcross
+30072,Pine Lake
+30074,Redan
+30075,Roswell
+30076,Roswell
+30077,Roswell
+30078,Snellville
+30079,Scottdale
+30080,Smyrna
+30081,Smyrna
+30082,Smyrna
+30083,Stone Mountain
+30084,Tucker
+30085,Tucker
+30086,Stone Mountain
+30087,Stone Mountain
+30088,Stone Mountain
+30090,Marietta
+30091,Norcross
+30092,Norcross
+30093,Norcross
+30094,Conyers
+30095,Duluth
+30096,Duluth
+30097,Duluth
+30098,Duluth
+30099,Duluth
+30101,Acworth
+30102,Acworth
+30103,Adairsville
+30104,Aragon
+30105,Armuchee
+30106,Austell
+30107,Ball Ground
+30108,Bowdon
+30109,Bowdon Junction
+30110,Bremen
+30111,Clarkdale
+30113,Buchanan
+30114,Canton
+30115,Canton
+30116,Carrollton
+30117,Carrollton
+30118,Carrollton
+30119,Carrollton
+30120,Cartersville
+30121,Cartersville
+30122,Lithia Springs
+30123,Cassville
+30124,Cave Spring
+30125,Cedartown
+30126,Mableton
+30127,Powder Springs
+30129,Coosa
+30132,Dallas
+30133,Douglasville
+30134,Douglasville
+30135,Douglasville
+30137,Emerson
+30138,Esom Hill
+30139,Fairmount
+30140,Felton
+30141,Hiram
+30142,Holly Springs
+30143,Jasper
+30144,Kennesaw
+30145,Kingston
+30146,Lebanon
+30147,Lindale
+30148,Marble Hill
+30149,Mount Berry
+30150,Mount Zion
+30151,Nelson
+30152,Kennesaw
+30153,Rockmart
+30154,Douglasville
+30157,Dallas
+30161,Rome
+30162,Rome
+30163,Rome
+30164,Rome
+30165,Rome
+30168,Austell
+30170,Roopville
+30171,Rydal
+30172,Shannon
+30173,Silver Creek
+30175,Talking Rock
+30176,Tallapoosa
+30177,Tate
+30178,Taylorsville
+30179,Temple
+30180,Villa Rica
+30182,Waco
+30183,Waleska
+30184,White
+30185,Whitesburg
+30187,Winston
+30188,Woodstock
+30189,Woodstock
+30204,Barnesville
+30205,Brooks
+30206,Concord
+30212,Experiment
+30213,Fairburn
+30214,Fayetteville
+30215,Fayetteville
+30216,Flovilla
+30217,Franklin
+30218,Gay
+30219,Glenn
+30220,Grantville
+30222,Greenville
+30223,Griffin
+30224,Griffin
+30228,Hampton
+30229,Haralson
+30230,Hogansville
+30232,Inman
+30233,Jackson
+30234,Jenkinsburg
+30236,Jonesboro
+30237,Jonesboro
+30238,Jonesboro
+30240,Lagrange
+30241,Lagrange
+30248,Locust Grove
+30250,Lovejoy
+30251,Luthersville
+30252,McDonough
+30253,McDonough
+30256,Meansville
+30257,Milner
+30258,Molena
+30259,Moreland
+30260,Morrow
+30261,Lagrange
+30263,Newnan
+30264,Newnan
+30265,Newnan
+30266,Orchard Hill
+30268,Palmetto
+30269,Peachtree City
+30271,Newnan
+30272,Red Oak
+30273,Rex
+30274,Riverdale
+30275,Sargent
+30276,Senoia
+30277,Sharpsburg
+30281,Stockbridge
+30284,Sunny Side
+30285,The Rock
+30286,Thomaston
+30287,Morrow
+30288,Conley
+30289,Turin
+30290,Tyrone
+30291,Union City
+30292,Williamson
+30293,Woodbury
+30294,Ellenwood
+30295,Zebulon
+30296,Riverdale
+30297,Forest Park
+30298,Forest Park
+30301,Atlanta
+30302,Atlanta
+30303,Atlanta
+30304,Atlanta
+30305,Atlanta
+30306,Atlanta
+30307,Atlanta
+30308,Atlanta
+30309,Atlanta
+30310,Atlanta
+30311,Atlanta
+30312,Atlanta
+30313,Atlanta
+30314,Atlanta
+30315,Atlanta
+30316,Atlanta
+30317,Atlanta
+30318,Atlanta
+30319,Atlanta
+30320,Atlanta
+30321,Atlanta
+30322,Atlanta
+30323,Atlanta
+30324,Atlanta
+30325,Atlanta
+30326,Atlanta
+30327,Atlanta
+30328,Atlanta
+30329,Atlanta
+30330,Atlanta
+30331,Atlanta
+30332,Atlanta
+30333,Atlanta
+30334,Atlanta
+30335,Atlanta
+30336,Atlanta
+30337,Atlanta
+30338,Atlanta
+30339,Atlanta
+30340,Atlanta
+30341,Atlanta
+30342,Atlanta
+30343,Atlanta
+30344,Atlanta
+30345,Atlanta
+30346,Atlanta
+30347,Atlanta
+30348,Atlanta
+30349,Atlanta
+30350,Atlanta
+30351,Atlanta
+30353,Atlanta
+30354,Atlanta
+30355,Atlanta
+30356,Atlanta
+30357,Atlanta
+30358,Atlanta
+30359,Atlanta
+30360,Atlanta
+30361,Atlanta
+30362,Atlanta
+30363,Atlanta
+30364,Atlanta
+30365,Atlanta
+30366,Atlanta
+30367,Atlanta
+30368,Atlanta
+30369,Atlanta
+30370,Atlanta
+30371,Atlanta
+30374,Atlanta
+30375,Atlanta
+30376,Atlanta
+30377,Atlanta
+30378,Atlanta
+30379,Atlanta
+30380,Atlanta
+30381,Atlanta
+30384,Atlanta
+30385,Atlanta
+30386,Atlanta
+30387,Atlanta
+30388,Atlanta
+30389,Atlanta
+30390,Atlanta
+30392,Atlanta
+30394,Atlanta
+30396,Atlanta
+30398,Atlanta
+30399,Atlanta
+30401,Swainsboro
+30410,Ailey
+30411,Alamo
+30412,Alston
+30413,Bartow
+30414,Bellville
+30415,Brooklet
+30417,Claxton
+30420,Cobbtown
+30421,Collins
+30423,Daisy
+30424,Dover
+30425,Garfield
+30426,Girard
+30427,Glennville
+30428,Glenwood
+30429,Hagan
+30434,Louisville
+30436,Lyons
+30438,Manassas
+30439,Metter
+30441,Midville
+30442,Millen
+30445,Mount Vernon
+30446,Newington
+30447,Norristown
+30448,Nunez
+30449,Oliver
+30450,Portal
+30451,Pulaski
+30452,Register
+30453,Reidsville
+30454,Rockledge
+30455,Rocky Ford
+30456,Sardis
+30457,Soperton
+30458,Statesboro
+30459,Statesboro
+30460,Statesboro
+30461,Statesboro
+30464,Stillmore
+30466,Summertown
+30467,Sylvania
+30470,Tarrytown
+30471,Twin City
+30473,Uvalda
+30474,Vidalia
+30475,Vidalia
+30477,Wadley
+30499,Reidsville
+30501,Gainesville
+30502,Chestnut Mountain
+30503,Gainesville
+30504,Gainesville
+30505,Gainesville
+30506,Gainesville
+30507,Gainesville
+30510,Alto
+30511,Baldwin
+30512,Blairsville
+30513,Blue Ridge
+30514,Blairsville
+30515,Buford
+30516,Bowersville
+30517,Braselton
+30518,Buford
+30519,Buford
+30520,Canon
+30521,Carnesville
+30522,Cherrylog
+30523,Clarkesville
+30525,Clayton
+30527,Clermont
+30528,Cleveland
+30529,Commerce
+30530,Commerce
+30531,Cornelia
+30533,Dahlonega
+30534,Dawsonville
+30535,Demorest
+30537,Dillard
+30538,Eastanollee
+30539,East Ellijay
+30540,Ellijay
+30541,Epworth
+30542,Flowery Branch
+30543,Gillsville
+30544,Demorest
+30545,Helen
+30546,Hiawassee
+30547,Homer
+30548,Hoschton
+30549,Jefferson
+30552,Lakemont
+30553,Lavonia
+30554,Lula
+30555,Mc Caysville
+30557,Martin
+30558,Maysville
+30559,Mineral Bluff
+30560,Morganton
+30562,Mountain City
+30563,Mount Airy
+30564,Murrayville
+30565,Nicholson
+30566,Oakwood
+30567,Pendergrass
+30568,Rabun Gap
+30571,Sautee Nacoochee
+30572,Suches
+30573,Tallulah Falls
+30575,Talmo
+30576,Tiger
+30577,Toccoa
+30580,Turnerville
+30581,Wiley
+30582,Young Harris
+30596,Alto
+30597,Dahlonega
+30598,Toccoa
+30599,Commerce
+30601,Athens
+30602,Athens
+30603,Athens
+30604,Athens
+30605,Athens
+30606,Athens
+30607,Athens
+30608,Athens
+30609,Athens
+30610,Athens
+30612,Athens
+30613,Athens
+30619,Arnoldsville
+30620,Bethlehem
+30621,Bishop
+30622,Bogart
+30623,Bostwick
+30624,Bowman
+30625,Buckhead
+30627,Carlton
+30628,Colbert
+30629,Comer
+30630,Crawford
+30631,Crawfordville
+30633,Danielsville
+30634,Dewy Rose
+30635,Elberton
+30638,Farmington
+30639,Franklin Springs
+30641,Good Hope
+30642,Greensboro
+30643,Hartwell
+30645,High Shoals
+30646,Hull
+30647,Ila
+30648,Lexington
+30650,Madison
+30655,Monroe
+30656,Monroe
+30660,Rayle
+30662,Royston
+30663,Rutledge
+30664,Sharon
+30665,Siloam
+30666,Statham
+30667,Stephens
+30668,Tignall
+30669,Union Point
+30671,Maxeys
+30673,Washington
+30677,Watkinsville
+30678,White Plains
+30680,Winder
+30683,Winterville
+30701,Calhoun
+30703,Calhoun
+30705,Chatsworth
+30707,Chickamauga
+30708,Cisco
+30710,Cohutta
+30711,Crandall
+30719,Dalton
+30720,Dalton
+30721,Dalton
+30722,Dalton
+30724,Eton
+30725,Flintstone
+30726,Graysville
+30728,La Fayette
+30730,Lyerly
+30731,Menlo
+30732,Oakman
+30733,Plainville
+30734,Ranger
+30735,Resaca
+30736,Ringgold
+30738,Rising Fawn
+30739,Rock Spring
+30740,Rocky Face
+30741,Rossville
+30742,Fort Oglethorpe
+30746,Sugar Valley
+30747,Summerville
+30750,Lookout Mountain
+30751,Tennga
+30752,Trenton
+30753,Trion
+30755,Tunnel Hill
+30756,Varnell
+30757,Wildwood
+30802,Appling
+30803,Avera
+30805,Blythe
+30806,Boneville
+30807,Camak
+30808,Dearing
+30809,Evans
+30810,Gibson
+30811,Gough
+30812,Gracewood
+30813,Grovetown
+30814,Harlem
+30815,Hephzibah
+30816,Keysville
+30817,Lincolnton
+30818,Matthews
+30819,Mesena
+30820,Mitchell
+30821,Norwood
+30822,Perkins
+30823,Stapleton
+30824,Thomson
+30828,Warrenton
+30830,Waynesboro
+30833,Wrens
+30901,Augusta
+30903,Augusta
+30904,Augusta
+30905,Augusta
+30906,Augusta
+30907,Augusta
+30909,Augusta
+30910,Augusta
+30911,Augusta
+30912,Augusta
+30913,Augusta
+30914,Augusta
+30916,Augusta
+30917,Augusta
+30919,Augusta
+30999,Augusta
+31001,Abbeville
+31002,Adrian
+31003,Allentown
+31004,Bolingbroke
+31005,Bonaire
+31006,Butler
+31007,Byromville
+31008,Byron
+31009,Cadwell
+31010,Cordele
+31011,Chauncey
+31012,Chester
+31013,Clinchfield
+31014,Cochran
+31015,Cordele
+31016,Culloden
+31017,Danville
+31018,Davisboro
+31019,Dexter
+31020,Dry Branch
+31021,Dublin
+31022,Dudley
+31023,Eastman
+31024,Eatonton
+31025,Elko
+31027,Dublin
+31028,Centerville
+31029,Forsyth
+31030,Fort Valley
+31031,Gordon
+31032,Gray
+31033,Haddock
+31034,Hardwick
+31035,Harrison
+31036,Hawkinsville
+31037,Helena
+31038,Hillsboro
+31039,Howard
+31040,Dublin
+31041,Ideal
+31042,Irwinton
+31044,Jeffersonville
+31045,Jewell
+31046,Juliette
+31047,Kathleen
+31049,Kite
+31050,Knoxville
+31051,Lilly
+31052,Lizella
+31054,Mc Intyre
+31055,Mc Rae
+31057,Marshallville
+31058,Mauk
+31060,Milan
+31061,Milledgeville
+31062,Milledgeville
+31063,Montezuma
+31064,Monticello
+31065,Montrose
+31066,Musella
+31067,Oconee
+31068,Oglethorpe
+31069,Perry
+31070,Pinehurst
+31071,Pineview
+31072,Pitts
+31073,Plainfield
+31075,Rentz
+31076,Reynolds
+31077,Rhine
+31078,Roberta
+31079,Rochelle
+31081,Rupert
+31082,Sandersville
+31083,Scotland
+31084,Seville
+31085,Shady Dale
+31086,Smarr
+31087,Sparta
+31088,Warner Robins
+31089,Tennille
+31090,Toomsboro
+31091,Unadilla
+31092,Vienna
+31093,Warner Robins
+31094,Warthen
+31095,Warner Robins
+31096,Wrightsville
+31097,Yatesville
+31098,Warner Robins
+31099,Warner Robins
+31106,Atlanta
+31107,Atlanta
+31119,Atlanta
+31126,Atlanta
+31131,Atlanta
+31139,Atlanta
+31141,Atlanta
+31145,Atlanta
+31146,Atlanta
+31150,Atlanta
+31156,Atlanta
+31191,Atlanta
+31192,Atlanta
+31193,Atlanta
+31195,Atlanta
+31196,Atlanta
+31197,Atlanta
+31198,Atlanta
+31199,Atlanta
+31201,Macon
+31202,Macon
+31203,Macon
+31204,Macon
+31205,Macon
+31206,Macon
+31207,Macon
+31208,Macon
+31209,Macon
+31210,Macon
+31211,Macon
+31212,Macon
+31213,Macon
+31216,Macon
+31217,Macon
+31220,Macon
+31221,Macon
+31294,Macon
+31295,Macon
+31296,Macon
+31297,Macon
+31298,Macon
+31299,Macon
+31301,Allenhurst
+31302,Bloomingdale
+31303,Clyo
+31304,Crescent
+31305,Darien
+31307,Eden
+31308,Ellabell
+31309,Fleming
+31310,Hinesville
+31312,Guyton
+31313,Hinesville
+31314,Fort Stewart
+31315,Fort Stewart
+31316,Ludowici
+31318,Meldrim
+31319,Meridian
+31320,Midway
+31321,Pembroke
+31322,Pooler
+31323,Riceboro
+31324,Richmond Hill
+31326,Rincon
+31327,Sapelo Island
+31328,Tybee Island
+31329,Springfield
+31331,Townsend
+31332,Valona
+31333,Walthourville
+31401,Savannah
+31402,Savannah
+31403,Savannah
+31404,Savannah
+31405,Savannah
+31406,Savannah
+31407,Savannah
+31408,Savannah
+31409,Savannah
+31410,Savannah
+31411,Savannah
+31412,Savannah
+31414,Savannah
+31415,Savannah
+31416,Savannah
+31418,Savannah
+31419,Savannah
+31420,Savannah
+31421,Savannah
+31422,Savannah
+31498,Savannah
+31499,Savannah
+31501,Waycross
+31502,Waycross
+31503,Waycross
+31510,Alma
+31512,Ambrose
+31513,Baxley
+31515,Baxley
+31516,Blackshear
+31518,Bristol
+31519,Broxton
+31520,Brunswick
+31521,Brunswick
+31522,Saint Simons Island
+31523,Brunswick
+31524,Brunswick
+31525,Brunswick
+31527,Jekyll Island
+31532,Denton
+31533,Douglas
+31534,Douglas
+31535,Douglas
+31537,Folkston
+31539,Hazlehurst
+31542,Hoboken
+31543,Hortense
+31544,Jacksonville
+31545,Jesup
+31546,Jesup
+31547,Kings Bay
+31548,Kingsland
+31549,Lumber City
+31550,Manor
+31551,Mershon
+31552,Millwood
+31553,Nahunta
+31554,Nicholls
+31555,Odum
+31556,Offerman
+31557,Patterson
+31558,Saint Marys
+31560,Screven
+31561,Sea Island
+31563,Surrency
+31564,Waresboro
+31565,Waverly
+31566,Waynesville
+31567,West Green
+31568,White Oak
+31569,Woodbine
+31598,Jesup
+31599,Jesup
+31601,Valdosta
+31602,Valdosta
+31603,Valdosta
+31604,Valdosta
+31605,Valdosta
+31606,Valdosta
+31620,Adel
+31622,Alapaha
+31623,Argyle
+31624,Axson
+31625,Barney
+31626,Boston
+31627,Cecil
+31629,Dixie
+31630,Du Pont
+31631,Fargo
+31632,Hahira
+31634,Homerville
+31635,Lakeland
+31636,Lake Park
+31637,Lenox
+31638,Morven
+31639,Nashville
+31641,Naylor
+31642,Pearson
+31643,Quitman
+31645,Ray City
+31646,Saint George
+31647,Sparks
+31648,Statenville
+31649,Stockton
+31650,Willacoochee
+31698,Valdosta
+31699,Valdosta
+31701,Albany
+31702,Albany
+31703,Albany
+31704,Albany
+31705,Albany
+31706,Albany
+31707,Albany
+31708,Albany
+31709,Americus
+31710,Americus
+31711,Andersonville
+31712,Arabi
+31713,Arlington
+31714,Ashburn
+31715,Attapulgus
+31716,Baconton
+31717,Bainbridge
+31718,Bainbridge
+31720,Barwick
+31722,Berlin
+31723,Blakely
+31724,Bluffton
+31725,Brinson
+31726,Bronwood
+31727,Brookfield
+31728,Cairo
+31729,Calvary
+31730,Camilla
+31732,Cedar Springs
+31733,Chula
+31734,Climax
+31735,Cobb
+31736,Coleman
+31737,Colquitt
+31738,Coolidge
+31739,Cotton
+31740,Cuthbert
+31741,Damascus
+31742,Dawson
+31743,De Soto
+31744,Doerun
+31745,Donalsonville
+31746,Edison
+31747,Ellenton
+31749,Enigma
+31750,Fitzgerald
+31751,Fort Gaines
+31752,Fowlstown
+31753,Funston
+31754,Georgetown
+31756,Hartsfield
+31757,Thomasville
+31758,Thomasville
+31759,Iron City
+31760,Irwinville
+31761,Jakin
+31762,Leary
+31763,Leesburg
+31764,Leslie
+31765,Meigs
+31766,Morgan
+31767,Morris
+31768,Moultrie
+31769,Mystic
+31770,Newton
+31771,Norman Park
+31772,Oakfield
+31773,Ochlocknee
+31774,Ocilla
+31775,Omega
+31776,Moultrie
+31777,Parrott
+31778,Pavo
+31779,Pelham
+31780,Plains
+31781,Poulan
+31782,Putney
+31783,Rebecca
+31784,Sale City
+31785,Sasser
+31786,Shellman
+31787,Smithville
+31789,Sumner
+31790,Sycamore
+31791,Sylvester
+31792,Thomasville
+31793,Tifton
+31794,Tifton
+31795,Ty Ty
+31796,Warwick
+31797,Whigham
+31798,Wray
+31799,Thomasville
+31801,Box Springs
+31803,Buena Vista
+31804,Cataula
+31805,Cusseta
+31806,Ellaville
+31807,Ellerslie
+31808,Fortson
+31810,Geneva
+31811,Hamilton
+31812,Junction City
+31814,Louvale
+31815,Lumpkin
+31816,Manchester
+31820,Midland
+31821,Omaha
+31822,Pine Mountain
+31823,Pine Mountain Valley
+31824,Preston
+31825,Richland
+31826,Shiloh
+31827,Talbotton
+31829,Upatoi
+31830,Warm Springs
+31831,Waverly Hall
+31832,Weston
+31833,West Point
+31836,Woodland
+31901,Columbus
+31902,Columbus
+31903,Columbus
+31904,Columbus
+31905,Fort Benning
+31906,Columbus
+31907,Columbus
+31908,Columbus
+31909,Columbus
+31914,Columbus
+31917,Columbus
+31993,Columbus
+31994,Columbus
+31995,Fort Benning
+31997,Columbus
+31998,Columbus
+31999,Columbus
+39901,Atlanta
+96941,Pohnpei
+96942,Chuuk
+96943,Yap
+96944,Kosrae
+32004,Ponte Vedra Beach
+32007,Bostwick
+32008,Branford
+32009,Bryceville
+32011,Callahan
+32013,Day
+32024,Lake City
+32025,Lake City
+32030,Doctors Inlet
+32033,Elkton
+32034,Fernandina Beach
+32035,Fernandina Beach
+32038,Fort White
+32040,Glen Saint Mary
+32041,Yulee
+32042,Graham
+32043,Green Cove Springs
+32044,Hampton
+32046,Hilliard
+32050,Middleburg
+32052,Jasper
+32053,Jennings
+32054,Lake Butler
+32055,Lake City
+32056,Lake City
+32058,Lawtey
+32059,Lee
+32060,Live Oak
+32061,Lulu
+32062,Mc Alpin
+32063,Macclenny
+32064,Live Oak
+32065,Orange Park
+32066,Mayo
+32067,Orange Park
+32068,Middleburg
+32071,O Brien
+32072,Olustee
+32073,Orange Park
+32079,Penney Farms
+32082,Ponte Vedra Beach
+32083,Raiford
+32084,Saint Augustine
+32085,Saint Augustine
+32086,Saint Augustine
+32087,Sanderson
+32091,Starke
+32092,Saint Augustine
+32094,Wellborn
+32095,Saint Augustine
+32096,White Springs
+32097,Yulee
+32099,Jacksonville
+32102,Astor
+32105,Barberville
+32110,Bunnell
+32111,Candler
+32112,Crescent City
+32113,Citra
+32114,Daytona Beach
+32115,Daytona Beach
+32116,Daytona Beach
+32117,Daytona Beach
+32118,Daytona Beach
+32119,Daytona Beach
+32120,Daytona Beach
+32121,Daytona Beach
+32122,Daytona Beach
+32123,Daytona Beach
+32124,Daytona Beach
+32125,Daytona Beach
+32126,Daytona Beach
+32127,Daytona Beach
+32129,Port Orange
+32130,De Leon Springs
+32131,East Palatka
+32132,Edgewater
+32133,Eastlake Weir
+32134,Fort Mc Coy
+32135,Palm Coast
+32136,Flagler Beach
+32137,Palm Coast
+32138,Grandin
+32139,Georgetown
+32140,Florahome
+32141,Edgewater
+32142,Palm Coast
+32145,Hastings
+32147,Hollister
+32148,Interlachen
+32149,Interlachen
+32151,Flagler Beach
+32157,Lake Como
+32158,Lady Lake
+32159,Lady Lake
+32160,Lake Geneva
+32164,Palm Coast
+32168,New Smyrna Beach
+32169,New Smyrna Beach
+32170,New Smyrna Beach
+32173,Ormond Beach
+32174,Ormond Beach
+32175,Ormond Beach
+32176,Ormond Beach
+32177,Palatka
+32178,Palatka
+32179,Ocklawaha
+32180,Pierson
+32181,Pomona Park
+32182,Orange Springs
+32183,Ocklawaha
+32185,Putnam Hall
+32187,San Mateo
+32189,Satsuma
+32190,Seville
+32192,Sparr
+32193,Welaka
+32195,Weirsdale
+32198,Daytona Beach
+32201,Jacksonville
+32202,Jacksonville
+32203,Jacksonville
+32204,Jacksonville
+32205,Jacksonville
+32206,Jacksonville
+32207,Jacksonville
+32208,Jacksonville
+32209,Jacksonville
+32210,Jacksonville
+32211,Jacksonville
+32212,Jacksonville
+32214,Jacksonville
+32215,Jacksonville
+32216,Jacksonville
+32217,Jacksonville
+32218,Jacksonville
+32219,Jacksonville
+32220,Jacksonville
+32221,Jacksonville
+32222,Jacksonville
+32223,Jacksonville
+32224,Jacksonville
+32225,Jacksonville
+32226,Jacksonville
+32227,Jacksonville
+32228,Jacksonville
+32229,Jacksonville
+32230,Jacksonville
+32231,Jacksonville
+32232,Jacksonville
+32233,Atlantic Beach
+32234,Jacksonville
+32235,Jacksonville
+32236,Jacksonville
+32237,Jacksonville
+32238,Jacksonville
+32239,Jacksonville
+32240,Jacksonville Beach
+32241,Jacksonville
+32244,Jacksonville
+32245,Jacksonville
+32246,Jacksonville
+32247,Jacksonville
+32250,Jacksonville Beach
+32254,Jacksonville
+32255,Jacksonville
+32256,Jacksonville
+32257,Jacksonville
+32258,Jacksonville
+32259,Jacksonville
+32260,Jacksonville
+32266,Neptune Beach
+32267,Jacksonville
+32276,Jacksonville
+32277,Jacksonville
+32301,Tallahassee
+32302,Tallahassee
+32303,Tallahassee
+32304,Tallahassee
+32305,Wakulla Springs
+32306,Tallahassee
+32307,Tallahassee
+32308,Tallahassee
+32309,Miccosukee Cpo
+32310,Tallahassee
+32311,Tallahassee
+32312,Tallahassee
+32313,Tallahassee
+32314,Tallahassee
+32315,Tallahassee
+32316,Tallahassee
+32317,Tallahassee
+32320,Apalachicola
+32321,Bristol
+32322,Carrabelle
+32323,Lanark Village
+32324,Chattahoochee
+32326,Crawfordville
+32327,Crawfordville
+32328,Eastpoint
+32329,Apalachicola
+32330,Greensboro
+32331,Greenville
+32332,Gretna
+32333,Havana
+32334,Hosford
+32335,Sumatra
+32336,Lamont
+32337,Lloyd
+32340,Madison
+32341,Madison
+32343,Midway
+32344,Monticello
+32345,Monticello
+32346,Panacea
+32347,Perry
+32348,Perry
+32350,Pinetta
+32351,Quincy
+32352,Mount Pleasant
+32353,Quincy
+32355,Saint Marks
+32356,Salem
+32357,Shady Grove
+32358,Sopchoppy
+32359,Steinhatchee
+32360,Telogia
+32361,Wacissa
+32362,Woodville
+32395,Tallahassee
+32399,Tallahassee
+32401,Panama City
+32402,Panama City
+32403,Panama City
+32404,Panama City
+32405,Panama City
+32406,Panama City
+32407,Panama City Beach
+32408,Panama City
+32409,Panama City
+32410,Mexico Beach
+32411,Panama City
+32412,Panama City
+32413,Panama City Beach
+32417,Panama City
+32420,Alford
+32421,Altha
+32422,Argyle
+32423,Bascom
+32424,Blountstown
+32425,Bonifay
+32426,Campbellton
+32427,Caryville
+32428,Chipley
+32430,Clarksville
+32431,Cottondale
+32432,Cypress
+32433,Defuniak Springs
+32434,Mossy Head
+32435,Defuniak Springs
+32437,Ebro
+32438,Fountain
+32439,Freeport
+32440,Graceville
+32442,Grand Ridge
+32443,Greenwood
+32444,Lynn Haven
+32445,Malone
+32446,Marianna
+32447,Marianna
+32448,Marianna
+32449,Kinard
+32452,Noma
+32454,Point Washington
+32455,Ponce de Leon
+32456,Port Saint Joe
+32457,Port Saint Joe
+32459,Santa Rosa Beach
+32460,Sneads
+32461,Sunnyside
+32462,Vernon
+32463,Wausau
+32464,Westville
+32465,Wewahitchka
+32466,Youngstown
+32501,Pensacola
+32502,Pensacola
+32503,Pensacola
+32504,Pensacola
+32505,Pensacola
+32506,Pensacola
+32507,Pensacola
+32508,Pensacola
+32509,Pensacola
+32511,Pensacola
+32512,Pensacola
+32513,Pensacola
+32514,Pensacola
+32516,Pensacola
+32520,Pensacola
+32521,Pensacola
+32522,Pensacola
+32523,Pensacola
+32524,Pensacola
+32526,Pensacola
+32530,Bagdad
+32531,Baker
+32533,Cantonment
+32534,Pensacola
+32535,Century
+32536,Crestview
+32537,Milligan
+32538,Paxton
+32539,Crestview
+32540,Destin
+32541,Destin
+32542,Eglin AFB
+32544,Hurlburt Field
+32547,Fort Walton Beach
+32548,Fort Walton Beach
+32549,Fort Walton Beach
+32550,Destin
+32559,Pensacola
+32560,Gonzalez
+32561,Gulf Breeze
+32562,Gulf Breeze
+32563,Harold
+32564,Holt
+32565,Jay
+32566,Gulf Breeze
+32567,Laurel Hill
+32568,Mc David
+32569,Mary Esther
+32570,Milton
+32571,Milton
+32572,Milton
+32573,Pensacola
+32574,Pensacola
+32575,Pensacola
+32576,Pensacola
+32577,Molino
+32578,Niceville
+32579,Shalimar
+32580,Valparaiso
+32581,Pensacola
+32582,Pensacola
+32583,Milton
+32588,Niceville
+32589,Pensacola
+32590,Pensacola
+32591,Pensacola
+32592,Pensacola
+32593,Pensacola
+32594,Pensacola
+32595,Pensacola
+32596,Pensacola
+32597,Pensacola
+32598,Pensacola
+32601,Gainesville
+32602,Gainesville
+32603,Gainesville
+32604,Gainesville
+32605,Gainesville
+32606,Gainesville
+32607,Gainesville
+32608,Gainesville
+32609,Gainesville
+32610,Gainesville
+32611,Gainesville
+32612,Gainesville
+32613,Gainesville
+32614,Gainesville
+32615,Alachua
+32616,Alachua
+32617,Anthony
+32618,Archer
+32619,Bell
+32621,Bronson
+32622,Brooker
+32625,Cedar Key
+32626,Chiefland
+32627,Gainesville
+32628,Cross City
+32631,Earleton
+32633,Evinston
+32634,Fairfield
+32635,Gainesville
+32639,Gulf Hammock
+32640,Hawthorne
+32641,Gainesville
+32643,High Springs
+32644,Chiefland
+32648,Horseshoe Beach
+32653,Gainesville
+32654,Island Grove
+32655,High Springs
+32656,Keystone Heights
+32658,La Crosse
+32662,Lochloosa
+32663,Lowell
+32664,Mc Intosh
+32666,Melrose
+32667,Micanopy
+32668,Morriston
+32669,Newberry
+32680,Old Town
+32681,Orange Lake
+32683,Otter Creek
+32686,Reddick
+32692,Suwannee
+32693,Trenton
+32694,Waldo
+32696,Williston
+32697,Worthington Springs
+32701,Altamonte Springs
+32702,Altoona
+32703,Apopka
+32704,Apopka
+32706,Cassadaga
+32707,Casselberry
+32708,Winter Springs
+32709,Christmas
+32710,Clarcona
+32712,Apopka
+32713,Debary
+32714,Altamonte Springs
+32715,Altamonte Springs
+32716,Altamonte Springs
+32718,Casselberry
+32719,Winter Springs
+32720,Deland
+32721,Deland
+32722,Glenwood
+32724,Deland
+32725,Deltona
+32726,Eustis
+32727,Eustis
+32728,Deltona
+32730,Casselberry
+32732,Geneva
+32733,Goldenrod
+32735,Grand Island
+32736,Eustis
+32738,Deltona
+32739,Deltona
+32744,Lake Helen
+32746,Lake Mary
+32747,Lake Monroe
+32750,Longwood
+32751,Maitland
+32752,Longwood
+32754,Mims
+32756,Mount Dora
+32757,Mount Dora
+32759,Oak Hill
+32762,Oviedo
+32763,Orange City
+32764,Osteen
+32765,Oviedo
+32766,Oviedo
+32767,Paisley
+32768,Plymouth
+32771,Sanford
+32772,Sanford
+32773,Sanford
+32774,Orange City
+32775,Scottsmoor
+32776,Sorrento
+32777,Tangerine
+32778,Tavares
+32779,Longwood
+32780,Titusville
+32781,Titusville
+32782,Titusville
+32783,Titusville
+32784,Umatilla
+32789,Winter Park
+32790,Winter Park
+32791,Longwood
+32792,Winter Park
+32793,Winter Park
+32794,Maitland
+32795,Lake Mary
+32796,Titusville
+32798,Zellwood
+32799,Mid Florida
+32801,Orlando
+32802,Orlando
+32803,Orlando
+32804,Orlando
+32805,Orlando
+32806,Orlando
+32807,Orlando
+32808,Orlando
+32809,Orlando
+32810,Orlando
+32811,Orlando
+32812,Orlando
+32813,Orlando
+32814,Orlando
+32815,Orlando
+32816,Orlando
+32817,Orlando
+32818,Orlando
+32819,Orlando
+32820,Orlando
+32821,Orlando
+32822,Orlando
+32824,Orlando
+32825,Orlando
+32826,Orlando
+32827,Orlando
+32828,Orlando
+32829,Orlando
+32830,Orlando
+32831,Orlando
+32832,Orlando
+32833,Orlando
+32834,Orlando
+32835,Orlando
+32836,Orlando
+32837,Orlando
+32839,Orlando
+32853,Orlando
+32854,Orlando
+32855,Orlando
+32856,Orlando
+32857,Orlando
+32858,Orlando
+32859,Orlando
+32860,Orlando
+32861,Orlando
+32862,Orlando
+32867,Orlando
+32868,Orlando
+32869,Orlando
+32872,Orlando
+32877,Orlando
+32878,Orlando
+32886,Orlando
+32887,Orlando
+32889,Orlando
+32890,Orlando
+32891,Orlando
+32893,Orlando
+32897,Orlando
+32898,Orlando
+32899,Orlando
+32901,Melbourne
+32902,Melbourne
+32903,Indialantic
+32904,Melbourne
+32905,Palm Bay
+32906,Palm Bay
+32907,Palm Bay
+32908,Palm Bay
+32909,Palm Bay
+32910,Palm Bay
+32911,Palm Bay
+32912,Melbourne
+32919,Melbourne
+32920,Cape Canaveral
+32922,Cocoa
+32923,Cocoa
+32924,Cocoa
+32925,Patrick AFB
+32926,Cocoa
+32927,Cocoa
+32931,Cocoa Beach
+32932,Cocoa Beach
+32934,Melbourne
+32935,Melbourne
+32936,Melbourne
+32937,Satellite Beach
+32940,Melbourne
+32941,Melbourne
+32948,Fellsmere
+32949,Grant
+32950,Malabar
+32951,Melbourne Beach
+32952,Merritt Island
+32953,Merritt Island
+32954,Merritt Island
+32955,Rockledge
+32956,Rockledge
+32957,Roseland
+32958,Sebastian
+32959,Sharpes
+32960,Vero Beach
+32961,Vero Beach
+32962,Vero Beach
+32963,Vero Beach
+32964,Vero Beach
+32965,Vero Beach
+32966,Vero Beach
+32967,Vero Beach
+32968,Vero Beach
+32969,Vero Beach
+32970,Wabasso
+32971,Winter Beach
+32976,Sebastian
+32978,Sebastian
+33001,Long Key
+33002,Hialeah
+33004,Dania
+33008,Hallandale
+33009,Hallandale
+33010,Hialeah
+33011,Hialeah
+33012,Hialeah
+33013,Hialeah
+33014,Hialeah
+33015,Hialeah
+33016,Hialeah
+33017,Hialeah
+33018,Hialeah
+33019,Hollywood
+33020,Hollywood
+33021,Hollywood
+33022,Hollywood
+33023,Hollywood
+33024,Hollywood
+33025,Hollywood
+33026,Hollywood
+33027,Hollywood
+33028,Hollywood
+33029,Hollywood
+33030,Homestead
+33031,Homestead
+33032,Homestead
+33033,Homestead
+33034,Homestead
+33035,Homestead
+33036,Islamorada
+33037,Key Largo
+33039,Homestead
+33040,Key West
+33041,Key West
+33042,Summerland Key
+33043,Big Pine Key
+33044,Sugarloaf Shores
+33045,Key West
+33050,Marathon
+33051,Key Colony Beach
+33052,Marathon Shores
+33054,Opa Locka
+33055,Opa Locka
+33056,Opa Locka
+33060,Pompano Beach
+33061,Pompano Beach
+33062,Pompano Beach
+33063,Pompano Beach
+33064,Pompano Beach
+33065,Pompano Beach
+33066,Pompano Beach
+33067,Pompano Beach
+33068,Pompano Beach
+33069,Pompano Beach
+33070,Tavernier
+33071,Pompano Beach
+33072,Pompano Beach
+33073,Pompano Beach
+33074,Pompano Beach
+33075,Pompano Beach
+33076,Pompano Beach
+33077,Pompano Beach
+33081,Hollywood
+33082,Pembroke Pines
+33083,Hollywood
+33084,Hollywood
+33090,Homestead
+33092,Homestead
+33093,Pompano Beach
+33097,Pompano Beach
+33101,Miami
+33102,Miami
+33107,Miami
+33109,Miami Beach
+33110,Miami
+33111,Miami
+33112,Miami
+33114,Miami
+33116,Miami
+33119,Miami Beach
+33121,Miami
+33122,Miami
+33124,Miami
+33125,Miami
+33126,Miami
+33127,Miami
+33128,Miami
+33129,Miami
+33130,Miami
+33131,Miami
+33132,Miami
+33133,Miami
+33134,Miami
+33135,Miami
+33136,Miami
+33137,Miami
+33138,Miami
+33139,Miami Beach
+33140,Miami Beach
+33141,Miami Beach
+33142,Miami
+33143,Miami
+33144,Miami
+33145,Miami
+33146,Miami
+33147,Miami
+33148,Miami
+33149,Key Biscayne
+33150,Miami
+33151,Miami
+33152,Miami
+33153,Miami
+33154,Miami
+33155,Miami
+33156,Miami
+33157,Miami
+33158,Miami
+33159,Miami
+33160,North Miami Beach
+33161,Miami
+33162,Miami
+33163,Miami
+33164,Miami
+33165,Miami
+33166,Miami
+33167,Miami
+33168,Miami
+33169,Miami
+33170,Miami
+33172,Miami
+33173,Miami
+33174,Miami
+33175,Miami
+33176,Miami
+33177,Miami
+33178,Miami
+33179,Miami
+33180,Miami
+33181,Miami
+33182,Miami
+33183,Miami
+33184,Miami
+33185,Miami
+33186,Miami
+33187,Miami
+33188,Miami
+33189,Miami
+33190,Miami
+33192,Miami
+33193,Miami
+33194,Miami
+33195,Miami
+33196,Miami
+33197,Miami
+33199,Miami
+33231,Miami
+33233,Miami
+33234,Miami
+33238,Miami
+33239,Miami Beach
+33242,Miami
+33243,Miami
+33245,Miami
+33247,Miami
+33255,Miami
+33256,Miami
+33257,Miami
+33261,Miami
+33265,Miami
+33266,Miami
+33269,Miami
+33280,Miami
+33283,Miami
+33296,Miami
+33299,Miami
+33301,Fort Lauderdale
+33302,Fort Lauderdale
+33303,Fort Lauderdale
+33304,Fort Lauderdale
+33305,Fort Lauderdale
+33306,Fort Lauderdale
+33307,Fort Lauderdale
+33308,Fort Lauderdale
+33309,Fort Lauderdale
+33310,Fort Lauderdale
+33311,Fort Lauderdale
+33312,Fort Lauderdale
+33313,Fort Lauderdale
+33314,Fort Lauderdale
+33315,Fort Lauderdale
+33316,Fort Lauderdale
+33317,Fort Lauderdale
+33318,Fort Lauderdale
+33319,Fort Lauderdale
+33320,Fort Lauderdale
+33321,Fort Lauderdale
+33322,Fort Lauderdale
+33323,Fort Lauderdale
+33324,Fort Lauderdale
+33325,Fort Lauderdale
+33326,Weston
+33327,Weston
+33328,Fort Lauderdale
+33329,Fort Lauderdale
+33330,Fort Lauderdale
+33331,Fort Lauderdale
+33332,Fort Lauderdale
+33334,Fort Lauderdale
+33335,Fort Lauderdale
+33337,Fort Lauderdale
+33338,Fort Lauderdale
+33339,Fort Lauderdale
+33340,Fort Lauderdale
+33345,Fort Lauderdale
+33346,Fort Lauderdale
+33348,Fort Lauderdale
+33349,Fort Lauderdale
+33351,Fort Lauderdale
+33355,Fort Lauderdale
+33359,Fort Lauderdale
+33388,Fort Lauderdale
+33394,Fort Lauderdale
+33401,West Palm Beach
+33402,West Palm Beach
+33403,West Palm Beach
+33404,West Palm Beach
+33405,West Palm Beach
+33406,West Palm Beach
+33407,West Palm Beach
+33408,North Palm Beach
+33409,West Palm Beach
+33410,West Palm Beach
+33411,West Palm Beach
+33412,West Palm Beach
+33413,West Palm Beach
+33414,West Palm Beach
+33415,West Palm Beach
+33416,West Palm Beach
+33417,West Palm Beach
+33418,West Palm Beach
+33419,West Palm Beach
+33420,West Palm Beach
+33421,West Palm Beach
+33422,West Palm Beach
+33424,Boynton Beach
+33425,Boynton Beach
+33426,Boynton Beach
+33427,Boca Raton
+33428,Boca Raton
+33429,Boca Raton
+33430,Belle Glade
+33431,Boca Raton
+33432,Boca Raton
+33433,Boca Raton
+33434,Boca Raton
+33435,Boynton Beach
+33436,Boynton Beach
+33437,Boynton Beach
+33438,Canal Point
+33439,Bryant
+33440,Clewiston
+33441,Deerfield Beach
+33442,Deerfield Beach
+33443,Deerfield Beach
+33444,Delray Beach
+33445,Delray Beach
+33446,Delray Beach
+33447,Delray Beach
+33448,Delray Beach
+33454,Lake Worth
+33455,Hobe Sound
+33458,Jupiter
+33459,Lake Harbor
+33460,Lake Worth
+33461,Lake Worth
+33462,Lake Worth
+33463,Lake Worth
+33464,Lake Worth
+33465,Lake Worth
+33466,Lake Worth
+33467,Lake Worth
+33468,Jupiter
+33469,Jupiter
+33470,Loxahatchee
+33471,Moore Haven
+33474,Boynton Beach
+33475,Hobe Sound
+33476,Pahokee
+33477,Jupiter
+33478,Jupiter
+33480,Palm Beach
+33481,Boca Raton
+33482,Delray Beach
+33483,Delray Beach
+33484,Delray Beach
+33486,Boca Raton
+33487,Boca Raton
+33488,Boca Raton
+33493,South Bay
+33496,Boca Raton
+33497,Boca Raton
+33498,Boca Raton
+33499,Boca Raton
+33503,Balm
+33509,Brandon
+33510,Brandon
+33511,Brandon
+33513,Bushnell
+33514,Center Hill
+33521,Coleman
+33523,Dade City
+33524,Crystal Springs
+33525,Dade City
+33526,Dade City
+33527,Dover
+33530,Durant
+33534,Gibsonton
+33537,Lacoochee
+33538,Lake Panasoffkee
+33539,Zephyrhills
+33540,Zephyrhills
+33541,Zephyrhills
+33543,Zephyrhills
+33544,Zephyrhills
+33547,Lithia
+33548,Lutz
+33549,Lutz
+33550,Mango
+33556,Odessa
+33564,Plant City
+33565,Plant City
+33566,Plant City
+33567,Plant City
+33568,Riverview
+33569,Riverview
+33570,Ruskin
+33571,Sun City Center
+33572,Apollo Beach
+33573,Sun City Center
+33574,Saint Leo
+33576,San Antonio
+33583,Seffner
+33584,Seffner
+33585,Sumterville
+33586,Sun City
+33587,Sydney
+33592,Thonotosassa
+33593,Trilby
+33594,Valrico
+33595,Valrico
+33597,Webster
+33598,Wimauma
+33601,Tampa
+33602,Tampa
+33603,Tampa
+33604,Tampa
+33605,Tampa
+33606,Tampa
+33607,Tampa
+33608,Tampa
+33609,Tampa
+33610,Tampa
+33611,Tampa
+33612,Tampa
+33613,Tampa
+33614,Tampa
+33615,Tampa
+33616,Tampa
+33617,Tampa
+33618,Tampa
+33619,Tampa
+33620,Tampa
+33621,Tampa
+33622,Tampa
+33623,Tampa
+33624,Tampa
+33625,Tampa
+33626,Tampa
+33629,Tampa
+33630,Tampa
+33631,Tampa
+33633,Tampa
+33634,Tampa
+33635,Tampa
+33637,Tampa
+33647,Tampa
+33650,Tampa
+33651,Tampa
+33655,Tampa
+33660,Tampa
+33661,Tampa
+33662,Tampa
+33663,Tampa
+33664,Tampa
+33672,Tampa
+33673,Tampa
+33674,Tampa
+33675,Tampa
+33677,Tampa
+33679,Tampa
+33680,Tampa
+33681,Tampa
+33682,Tampa
+33684,Tampa
+33685,Tampa
+33686,Tampa
+33687,Tampa
+33688,Tampa
+33689,Tampa
+33690,Tampa
+33694,Tampa
+33697,Tampa
+33701,Saint Petersburg
+33702,Saint Petersburg
+33703,Saint Petersburg
+33704,Saint Petersburg
+33705,Saint Petersburg
+33706,Saint Petersburg
+33707,Saint Petersburg
+33708,Saint Petersburg
+33709,Saint Petersburg
+33710,Saint Petersburg
+33711,Saint Petersburg
+33712,Saint Petersburg
+33713,Saint Petersburg
+33714,Saint Petersburg
+33715,Saint Petersburg
+33716,Saint Petersburg
+33728,Saint Petersburg
+33729,Saint Petersburg
+33730,Saint Petersburg
+33731,Saint Petersburg
+33732,Saint Petersburg
+33733,Saint Petersburg
+33734,Saint Petersburg
+33736,Saint Petersburg
+33737,Saint Petersburg
+33738,Saint Petersburg
+33740,Saint Petersburg
+33741,Saint Petersburg
+33742,Saint Petersburg
+33743,Saint Petersburg
+33744,Bay Pines
+33747,Saint Petersburg
+33755,Clearwater
+33756,Clearwater
+33757,Clearwater
+33758,Clearwater
+33759,Clearwater
+33760,Clearwater
+33761,Clearwater
+33762,Clearwater
+33763,Clearwater
+33764,Clearwater
+33765,Clearwater
+33766,Clearwater
+33767,Clearwater Beach
+33769,Clearwater
+33770,Largo
+33771,Largo
+33772,Seminole
+33773,Largo
+33774,Largo
+33775,Seminole
+33776,Seminole
+33777,Largo
+33778,Largo
+33779,Largo
+33780,Pinellas Park
+33781,Pinellas Park
+33782,Pinellas Park
+33784,Saint Petersburg
+33785,Indian Rocks Beach
+33786,Belleair Beach
+33801,Lakeland
+33802,Lakeland
+33803,Lakeland
+33804,Lakeland
+33805,Lakeland
+33806,Lakeland
+33807,Lakeland
+33809,Lakeland
+33810,Lakeland
+33811,Lakeland
+33813,Lakeland
+33815,Lakeland
+33820,Alturas
+33823,Auburndale
+33825,Avon Park
+33826,Avon Park
+33827,Babson Park
+33830,Bartow
+33831,Bartow
+33834,Bowling Green
+33835,Bradley
+33836,Davenport
+33837,Davenport
+33838,Dundee
+33839,Eagle Lake
+33840,Eaton Park
+33841,Fort Meade
+33843,Frostproof
+33844,Haines City
+33845,Haines City
+33846,Highland City
+33847,Homeland
+33848,Intercession City
+33849,Kathleen
+33850,Lake Alfred
+33851,Lake Hamilton
+33852,Lake Placid
+33853,Lake Wales
+33854,Fedhaven
+33855,Indian Lake Estates
+33856,Nalcrest
+33857,Lorida
+33858,Loughman
+33859,Lake Wales
+33860,Mulberry
+33862,Lake Placid
+33863,Nichols
+33865,Ona
+33867,River Ranch
+33868,Polk City
+33870,Sebring
+33871,Sebring
+33872,Sebring
+33873,Wauchula
+33877,Waverly
+33880,Winter Haven
+33881,Winter Haven
+33882,Winter Haven
+33883,Winter Haven
+33884,Winter Haven
+33885,Winter Haven
+33888,Winter Haven
+33890,Zolfo Springs
+33901,Fort Myers
+33902,Fort Myers
+33903,North Fort Myers
+33904,Cape Coral
+33905,Fort Myers
+33906,Fort Myers
+33907,Fort Myers
+33908,Fort Myers
+33909,Cape Coral
+33910,Cape Coral
+33911,Fort Myers
+33912,Fort Myers
+33913,Fort Myers
+33914,Cape Coral
+33915,Cape Coral
+33916,Fort Myers
+33917,North Fort Myers
+33918,North Fort Myers
+33919,Fort Myers
+33920,Alva
+33921,Boca Grande
+33922,Bokeelia
+33924,Captiva
+33927,El Jobean
+33928,Estero
+33930,Felda
+33931,Fort Myers Beach
+33932,Fort Myers Beach
+33935,Labelle
+33936,Lehigh Acres
+33938,Murdock
+33944,Palmdale
+33945,Pineland
+33946,Placida
+33947,Rotonda West
+33948,Port Charlotte
+33949,Port Charlotte
+33950,Punta Gorda
+33951,Punta Gorda
+33952,Port Charlotte
+33953,Port Charlotte
+33954,Port Charlotte
+33955,Punta Gorda
+33956,Saint James City
+33957,Sanibel
+33960,Venus
+33965,Fort Myers
+33970,Lehigh Acres
+33971,Lehigh Acres
+33972,Lehigh Acres
+33975,Labelle
+33980,Port Charlotte
+33981,Port Charlotte
+33982,Punta Gorda
+33983,Punta Gorda
+33990,Cape Coral
+33991,Cape Coral
+33993,Cape Coral
+33994,Fort Myers
+34101,Naples
+34102,Naples
+34103,Naples
+34104,Naples
+34105,Naples
+34106,Naples
+34107,Vanderbilt Beach
+34108,Naples
+34109,Naples
+34110,Naples
+34112,Naples
+34113,Naples
+34114,Naples
+34116,Naples
+34117,Naples
+34119,Naples
+34120,Naples
+34133,Bonita Springs
+34134,Bonita Springs
+34135,Bonita Springs
+34136,Bonita Springs
+34137,Copeland
+34138,Chokoloskee
+34139,Everglades City
+34140,Goodland
+34141,Ochopee
+34142,Immokalee
+34143,Immokalee
+34145,Marco Island
+34146,Marco Island
+34201,Bradenton
+34202,Bradenton
+34203,Bradenton
+34204,Bradenton
+34205,Bradenton
+34206,Bradenton
+34207,Bradenton
+34208,Bradenton
+34209,Bradenton
+34210,Bradenton
+34215,Cortez
+34216,Anna Maria
+34217,Bradenton Beach
+34218,Holmes Beach
+34219,Parrish
+34220,Palmetto
+34221,Palmetto
+34222,Ellenton
+34223,Englewood
+34224,Englewood
+34228,Longboat Key
+34229,Osprey
+34230,Sarasota
+34231,Sarasota
+34232,Sarasota
+34233,Sarasota
+34234,Sarasota
+34235,Sarasota
+34236,Sarasota
+34237,Sarasota
+34238,Sarasota
+34239,Sarasota
+34240,Sarasota
+34241,Sarasota
+34242,Sarasota
+34243,Sarasota
+34250,Terra Ceia
+34251,Myakka City
+34260,Manasota
+34264,Oneco
+34265,Arcadia
+34266,Arcadia
+34267,Fort Ogden
+34268,Nocatee
+34270,Tallevast
+34272,Laurel
+34274,Nokomis
+34275,Nokomis
+34276,Sarasota
+34277,Sarasota
+34278,Sarasota
+34280,Bradenton
+34281,Bradenton
+34282,Bradenton
+34284,Venice
+34285,Venice
+34286,North Port
+34287,North Port
+34292,Venice
+34293,Venice
+34295,Englewood
+34420,Belleview
+34421,Belleview
+34423,Crystal River
+34428,Crystal River
+34429,Crystal River
+34430,Dunnellon
+34431,Dunnellon
+34432,Dunnellon
+34433,Dunnellon
+34434,Dunnellon
+34436,Floral City
+34442,Hernando
+34445,Holder
+34446,Homosassa
+34447,Homosassa Springs
+34448,Homosassa
+34449,Inglis
+34450,Inverness
+34451,Inverness
+34452,Inverness
+34453,Inverness
+34460,Lecanto
+34461,Lecanto
+34464,Beverly Hills
+34465,Beverly Hills
+34470,Ocala
+34471,Ocala
+34472,Ocala
+34473,Ocala
+34474,Ocala
+34475,Ocala
+34476,Ocala
+34477,Ocala
+34478,Ocala
+34479,Ocala
+34480,Ocala
+34481,Ocala
+34482,Ocala
+34483,Ocala
+34484,Oxford
+34487,Homosassa
+34488,Silver Springs
+34489,Silver Springs
+34491,Summerfield
+34492,Summerfield
+34498,Yankeetown
+34601,Brooksville
+34602,Brooksville
+34603,Brooksville
+34605,Brooksville
+34606,Spring Hill
+34607,Spring Hill
+34608,Spring Hill
+34609,Brooksville
+34610,Brooksville
+34611,Spring Hill
+34613,Brooksville
+34614,Brooksville
+34636,Istachatta
+34639,Land O Lakes
+34652,New Port Richey
+34653,New Port Richey
+34654,New Port Richey
+34655,New Port Richey
+34656,New Port Richey
+34660,Ozona
+34661,Nobleton
+34667,Hudson
+34668,Port Richey
+34669,Hudson
+34673,Port Richey
+34674,Hudson
+34677,Oldsmar
+34679,Aripeka
+34680,Elfers
+34681,Crystal Beach
+34682,Palm Harbor
+34683,Palm Harbor
+34684,Palm Harbor
+34685,Palm Harbor
+34688,Tarpon Springs
+34689,Tarpon Springs
+34690,Holiday
+34691,Holiday
+34695,Safety Harbor
+34697,Dunedin
+34698,Dunedin
+34705,Astatula
+34711,Clermont
+34712,Clermont
+34713,Clermont
+34729,Ferndale
+34731,Fruitland Park
+34734,Gotha
+34736,Groveland
+34737,Howey in the Hills
+34739,Kenansville
+34740,Killarney
+34741,Kissimmee
+34742,Kissimmee
+34743,Kissimmee
+34744,Kissimmee
+34745,Kissimmee
+34746,Kissimmee
+34747,Kissimmee
+34748,Leesburg
+34749,Leesburg
+34753,Mascotte
+34755,Minneola
+34756,Montverde
+34758,Kissimmee
+34759,Kissimmee
+34760,Oakland
+34761,Ocoee
+34762,Okahumpka
+34769,Saint Cloud
+34770,Saint Cloud
+34771,Saint Cloud
+34772,Saint Cloud
+34773,Saint Cloud
+34777,Winter Garden
+34778,Winter Garden
+34785,Wildwood
+34786,Windermere
+34787,Winter Garden
+34788,Leesburg
+34789,Leesburg
+34797,Yalaha
+34945,Fort Pierce
+34946,Fort Pierce
+34947,Fort Pierce
+34948,Fort Pierce
+34949,Fort Pierce
+34950,Fort Pierce
+34951,Fort Pierce
+34952,Port Saint Lucie
+34953,Port Saint Lucie
+34954,Fort Pierce
+34956,Indiantown
+34957,Jensen Beach
+34958,Jensen Beach
+34972,Okeechobee
+34973,Okeechobee
+34974,Okeechobee
+34979,Fort Pierce
+34981,Fort Pierce
+34982,Fort Pierce
+34983,Port Saint Lucie
+34984,Port Saint Lucie
+34985,Port Saint Lucie
+34986,Port Saint Lucie
+34987,Port Saint Lucie
+34988,Port Saint Lucie
+34990,Palm City
+34991,Palm City
+34992,Port Salerno
+34994,Stuart
+34995,Stuart
+34996,Stuart
+34997,Stuart
+19701,Bear
+19702,Newark
+19703,Claymont
+19706,Delaware City
+19707,Hockessin
+19708,Kirkwood
+19709,Middletown
+19710,Montchanin
+19711,Newark
+19712,Newark
+19713,Newark
+19714,Newark
+19715,Newark
+19716,Newark
+19717,Newark
+19718,Newark
+19720,New Castle
+19721,New Castle
+19725,Newark
+19726,Newark
+19730,Odessa
+19731,Port Penn
+19732,Rockland
+19733,Saint Georges
+19734,Townsend
+19735,Winterthur
+19736,Yorklyn
+19801,Wilmington
+19802,Wilmington
+19803,Wilmington
+19804,Wilmington
+19805,Wilmington
+19806,Wilmington
+19807,Wilmington
+19808,Wilmington
+19809,Wilmington
+19810,Wilmington
+19850,Wilmington
+19880,Wilmington
+19884,Wilmington
+19885,Wilmington
+19886,Wilmington
+19887,Wilmington
+19889,Wilmington
+19890,Wilmington
+19891,Wilmington
+19892,Wilmington
+19893,Wilmington
+19894,Wilmington
+19895,Wilmington
+19896,Wilmington
+19897,Wilmington
+19898,Wilmington
+19899,Wilmington
+19901,Dover
+19902,Dover AFB
+19903,Dover
+19904,Dover
+19905,Dover
+19930,Bethany Beach
+19931,Bethel
+19933,Bridgeville
+19934,Camden Wyoming
+19936,Cheswold
+19938,Clayton
+19939,Dagsboro
+19940,Delmar
+19941,Ellendale
+19942,Farmington
+19943,Felton
+19944,Fenwick Island
+19945,Frankford
+19946,Frederica
+19947,Georgetown
+19950,Greenwood
+19951,Harbeson
+19952,Harrington
+19953,Hartly
+19954,Houston
+19955,Kenton
+19956,Laurel
+19958,Lewes
+19960,Lincoln
+19961,Little Creek
+19962,Magnolia
+19963,Milford
+19964,Marydel
+19966,Millsboro
+19967,Millville
+19968,Milton
+19969,Nassau
+19970,Ocean View
+19971,Rehoboth Beach
+19973,Seaford
+19975,Selbyville
+19977,Smyrna
+19979,Viola
+19980,Woodside
+20001,Washington
+20002,Washington
+20003,Washington
+20004,Washington
+20005,Washington
+20006,Washington
+20007,Washington
+20008,Washington
+20009,Washington
+20010,Washington
+20011,Washington
+20012,Washington
+20013,Washington
+20015,Washington
+20016,Washington
+20017,Washington
+20018,Washington
+20019,Washington
+20020,Washington
+20024,Washington
+20026,Washington
+20029,Washington
+20030,Washington
+20032,Washington
+20033,Washington
+20035,Washington
+20036,Washington
+20037,Washington
+20038,Washington
+20039,Washington
+20040,Washington
+20041,Washington
+20042,Washington
+20043,Washington
+20044,Washington
+20045,Washington
+20046,Washington
+20047,Washington
+20049,Washington
+20050,Washington
+20051,Washington
+20052,Washington
+20053,Washington
+20055,Washington
+20056,Washington
+20057,Washington
+20058,Washington
+20059,Washington
+20060,Washington
+20061,Washington
+20062,Washington
+20063,Washington
+20064,Washington
+20065,Washington
+20066,Washington
+20067,Washington
+20068,Washington
+20069,Washington
+20070,Washington
+20071,Washington
+20073,Washington
+20074,Washington
+20075,Washington
+20076,Washington
+20077,Washington
+20078,Washington
+20080,Washington
+20081,Washington
+20082,Washington
+20088,Washington
+20090,Washington
+20091,Washington
+20097,Washington
+20098,Washington
+20099,Washington
+20201,Washington
+20202,Washington
+20203,Washington
+20204,Washington
+20206,Washington
+20207,Washington
+20208,Washington
+20210,Washington
+20211,Washington
+20212,Washington
+20213,Washington
+20214,Washington
+20215,Washington
+20216,Washington
+20217,Washington
+20218,Washington
+20219,Washington
+20220,Washington
+20221,Washington
+20222,Washington
+20223,Washington
+20224,Washington
+20226,Washington
+20227,Washington
+20228,Washington
+20229,Washington
+20230,Washington
+20231,Washington
+20232,Washington
+20233,Washington
+20235,Washington
+20237,Washington
+20238,Washington
+20239,Washington
+20240,Washington
+20241,Washington
+20242,Washington
+20244,Washington
+20245,Washington
+20250,Washington
+20251,Washington
+20254,Washington
+20260,Washington
+20261,Washington
+20262,Washington
+20265,Washington
+20266,Washington
+20268,Washington
+20270,Washington
+20277,Washington
+20289,Washington
+20299,Washington
+20301,Washington
+20303,Washington
+20306,Washington
+20307,Washington
+20310,Washington
+20314,Washington
+20315,Washington
+20317,Washington
+20318,Washington
+20319,Washington
+20330,Washington
+20332,Washington
+20336,Washington
+20337,Washington
+20338,Washington
+20340,Washington
+20350,Washington
+20370,Washington
+20372,Washington
+20373,Washington
+20374,Washington
+20375,Washington
+20380,Washington
+20388,Washington
+20389,Washington
+20390,Washington
+20391,Washington
+20392,Washington
+20393,Washington
+20394,Washington
+20395,Washington
+20398,Washington
+20401,Washington
+20402,Washington
+20403,Washington
+20404,Washington
+20405,Washington
+20406,Washington
+20407,Washington
+20408,Washington
+20409,Washington
+20410,Washington
+20411,Washington
+20412,Washington
+20413,Washington
+20414,Washington
+20415,Washington
+20416,Washington
+20418,Washington
+20419,Washington
+20420,Washington
+20421,Washington
+20422,Washington
+20423,Washington
+20424,Washington
+20425,Washington
+20426,Washington
+20427,Washington
+20428,Washington
+20429,Washington
+20431,Washington
+20433,Washington
+20434,Washington
+20435,Washington
+20436,Washington
+20437,Washington
+20439,Washington
+20440,Washington
+20441,Washington
+20442,Washington
+20444,Washington
+20447,Washington
+20451,Washington
+20453,Washington
+20456,Washington
+20460,Washington
+20463,Washington
+20468,Washington
+20469,Washington
+20470,Washington
+20472,Washington
+20500,Washington
+20501,Washington
+20502,Washington
+20503,Washington
+20504,Washington
+20505,Washington
+20506,Washington
+20507,Washington
+20508,Washington
+20510,Washington
+20515,Washington
+20520,Washington
+20521,Washington
+20522,Washington
+20523,Washington
+20524,Washington
+20525,Washington
+20526,Washington
+20527,Washington
+20530,Washington
+20531,Washington
+20532,Washington
+20533,Washington
+20534,Washington
+20535,Washington
+20536,Washington
+20537,Washington
+20538,Washington
+20539,Washington
+20540,Washington
+20541,Washington
+20542,Washington
+20543,Washington
+20544,Washington
+20546,Washington
+20547,Washington
+20548,Washington
+20549,Washington
+20550,Washington
+20551,Washington
+20552,Washington
+20553,Washington
+20554,Washington
+20555,Washington
+20557,Washington
+20558,Washington
+20559,Washington
+20560,Washington
+20565,Washington
+20566,Washington
+20570,Washington
+20571,Washington
+20572,Washington
+20573,Washington
+20575,Washington
+20576,Washington
+20577,Washington
+20578,Washington
+20579,Washington
+20580,Washington
+20581,Washington
+20585,Washington
+20586,Washington
+20590,Washington
+20591,Washington
+20593,Washington
+20594,Washington
+20597,Washington
+20599,Washington
+06001,Avon
+06002,Bloomfield
+06006,Windsor
+06010,Bristol
+06011,Bristol
+06013,Burlington
+06016,Broad Brook
+06018,Canaan
+06019,Canton
+06020,Canton Center
+06021,Colebrook
+06022,Collinsville
+06023,East Berlin
+06024,East Canaan
+06025,East Glastonbury
+06026,East Granby
+06027,East Hartland
+06028,East Windsor Hill
+06029,Ellington
+06030,Farmington
+06031,Falls Village
+06032,Farmington
+06033,Glastonbury
+06034,Farmington
+06035,Granby
+06037,Kensington
+06039,Lakeville
+06040,Manchester
+06041,Manchester
+06043,Bolton
+06045,Manchester
+06049,Melrose
+06050,New Britain
+06051,New Britain
+06052,New Britain
+06053,New Britain
+06057,New Hartford
+06058,Norfolk
+06059,North Canton
+06060,North Granby
+06061,Pine Meadow
+06062,Plainville
+06063,Pleasant Valley
+06064,Poquonock
+06065,Riverton
+06066,Vernon Rockville
+06067,Rocky Hill
+06068,Salisbury
+06069,Sharon
+06070,Simsbury
+06071,Somers
+06072,Somersville
+06073,South Glastonbury
+06074,South Windsor
+06075,Stafford
+06076,Stafford Springs
+06077,Staffordville
+06078,Suffield
+06079,Taconic
+06080,Suffield
+06081,Tariffville
+06082,Enfield
+06083,Enfield
+06084,Tolland
+06085,Unionville
+06087,Unionville
+06088,East Windsor
+06089,Weatogue
+06090,West Granby
+06091,West Hartland
+06092,West Simsbury
+06093,West Suffield
+06094,Winchester Center
+06095,Windsor
+06096,Windsor Locks
+06098,Winsted
+06101,Hartford
+06102,Hartford
+06103,Hartford
+06104,Hartford
+06105,Hartford
+06106,Hartford
+06107,W Hartford
+06108,East Hartford
+06109,Wethersfield
+06110,W Hartford
+06111,Newington
+06112,Hartford
+06114,Hartford
+06115,Hartford
+06117,W Hartford
+06118,East Hartford
+06119,W Hartford
+06120,Hartford
+06123,Hartford
+06126,Hartford
+06127,W Hartford
+06128,East Hartford
+06129,Wethersfield
+06131,Newington
+06132,Hartford
+06133,W Hartford
+06134,Hartford
+06137,W Hartford
+06138,East Hartford
+06140,Hartford
+06141,Hartford
+06142,Hartford
+06143,Hartford
+06144,Hartford
+06145,Hartford
+06146,Hartford
+06147,Hartford
+06150,Hartford
+06151,Hartford
+06152,Hartford
+06153,Hartford
+06154,Hartford
+06155,Hartford
+06156,Hartford
+06160,Hartford
+06161,Hartford
+06167,Hartford
+06176,Hartford
+06180,Hartford
+06183,Hartford
+06199,Hartford
+06226,Willimantic
+06230,Abington
+06231,Amston
+06232,Andover
+06233,Ballouville
+06234,Brooklyn
+06235,Chaplin
+06237,Columbia
+06238,Coventry
+06239,Danielson
+06241,Dayville
+06242,Eastford
+06243,East Killingly
+06244,East Woodstock
+06245,Fabyan
+06246,Grosvenor Dale
+06247,Hampton
+06248,Hebron
+06249,Lebanon
+06250,Mansfield Center
+06251,Mansfield Depot
+06254,North Franklin
+06255,North Grosvenordale
+06256,North Windham
+06258,Pomfret
+06259,Pomfret Center
+06260,Putnam
+06262,Quinebaug
+06263,Rogers
+06264,Scotland
+06265,South Willington
+06266,South Windham
+06267,South Woodstock
+06268,Storrs Mansfield
+06269,Storrs Mansfield
+06277,Thompson
+06278,Ashford
+06279,Willington
+06280,Windham
+06281,Woodstock
+06282,Woodstock Valley
+06320,New London
+06330,Baltic
+06331,Canterbury
+06332,Central Village
+06333,East Lyme
+06334,Bozrah
+06335,Gales Ferry
+06336,Gilman
+06337,Glasgo
+06339,Ledyard
+06340,Groton
+06349,Groton
+06350,Hanover
+06351,Jewett City
+06353,Montville
+06354,Moosup
+06355,Mystic
+06357,Niantic
+06359,North Stonington
+06360,Norwich
+06365,Preston
+06370,Oakdale
+06371,Old Lyme
+06372,Old Mystic
+06373,Oneco
+06374,Plainfield
+06375,Quaker Hill
+06376,South Lyme
+06377,Sterling
+06378,Stonington
+06379,Pawcatuck
+06380,Taftville
+06382,Uncasville
+06383,Versailles
+06384,Voluntown
+06385,Waterford
+06386,Waterford
+06387,Wauregan
+06388,West Mystic
+06389,Yantic
+06401,Ansonia
+06403,Beacon Falls
+06404,Botsford
+06405,Branford
+06408,Cheshire
+06409,Centerbrook
+06410,Cheshire
+06411,Cheshire
+06412,Chester
+06413,Clinton
+06414,Cobalt
+06415,Colchester
+06416,Cromwell
+06417,Deep River
+06418,Derby
+06419,Killingworth
+06420,Salem
+06422,Durham
+06423,East Haddam
+06424,East Hampton
+06426,Essex
+06430,Fairfield
+06431,Fairfield
+06432,Fairfield
+06436,Greens Farms
+06437,Guilford
+06438,Haddam
+06439,Hadlyme
+06440,Hawleyville
+06441,Higganum
+06442,Ivoryton
+06443,Madison
+06444,Marion
+06447,Marlborough
+06450,Meriden
+06451,Meriden
+06454,Meriden
+06455,Middlefield
+06456,Middle Haddam
+06457,Middletown
+06459,Middletown
+06460,Milford
+06467,Milldale
+06468,Monroe
+06469,Moodus
+06470,Newtown
+06471,North Branford
+06472,Northford
+06473,North Haven
+06474,North Westchester
+06475,Old Saybrook
+06477,Orange
+06478,Oxford
+06479,Plantsville
+06480,Portland
+06481,Rockfall
+06482,Sandy Hook
+06483,Seymour
+06484,Shelton
+06487,South Britain
+06488,Southbury
+06489,Southington
+06490,Southport
+06491,Stevenson
+06492,Wallingford
+06493,Wallingford
+06494,Wallingford
+06497,Stratford
+06498,Westbrook
+06501,New Haven
+06502,New Haven
+06503,New Haven
+06504,New Haven
+06505,New Haven
+06506,New Haven
+06507,New Haven
+06508,New Haven
+06509,New Haven
+06510,New Haven
+06511,New Haven
+06512,East Haven
+06513,New Haven
+06514,Hamden
+06515,New Haven
+06516,West Haven
+06517,Hamden
+06518,Hamden
+06519,New Haven
+06520,New Haven
+06521,New Haven
+06524,Bethany
+06525,Woodbridge
+06530,New Haven
+06531,New Haven
+06532,New Haven
+06533,New Haven
+06534,New Haven
+06535,New Haven
+06536,New Haven
+06537,New Haven
+06538,New Haven
+06540,New Haven
+06601,Bridgeport
+06602,Bridgeport
+06604,Bridgeport
+06605,Bridgeport
+06606,Bridgeport
+06607,Bridgeport
+06608,Bridgeport
+06610,Bridgeport
+06611,Trumbull
+06612,Easton
+06614,Stratford
+06615,Stratford
+06650,Bridgeport
+06673,Bridgeport
+06699,Bridgeport
+06701,Waterbury
+06702,Waterbury
+06703,Waterbury
+06704,Waterbury
+06705,Waterbury
+06706,Waterbury
+06708,Waterbury
+06710,Waterbury
+06712,Prospect
+06716,Wolcott
+06720,Waterbury
+06721,Waterbury
+06722,Waterbury
+06723,Waterbury
+06724,Waterbury
+06725,Waterbury
+06726,Waterbury
+06749,Waterbury
+06750,Bantam
+06751,Bethlehem
+06752,Bridgewater
+06753,Cornwall
+06754,Cornwall Bridge
+06755,Gaylordsville
+06756,Goshen
+06757,Kent
+06758,Lakeside
+06759,Litchfield
+06762,Middlebury
+06763,Morris
+06770,Naugatuck
+06776,New Milford
+06777,New Preston Marble Dale
+06778,Northfield
+06779,Oakville
+06781,Pequabuck
+06782,Plymouth
+06783,Roxbury
+06784,Sherman
+06785,South Kent
+06786,Terryville
+06787,Thomaston
+06790,Torrington
+06791,Harwinton
+06793,Washington
+06794,Washington Depot
+06795,Watertown
+06796,West Cornwall
+06798,Woodbury
+06801,Bethel
+06804,Brookfield
+06807,Cos Cob
+06810,Danbury
+06811,Danbury
+06812,New Fairfield
+06813,Danbury
+06814,Danbury
+06816,Danbury
+06817,Danbury
+06820,Darien
+06829,Georgetown
+06830,Greenwich
+06831,Greenwich
+06832,Greenwich
+06836,Greenwich
+06840,New Canaan
+06842,New Canaan
+06850,Norwalk
+06851,Norwalk
+06852,Norwalk
+06853,Norwalk
+06854,Norwalk
+06855,Norwalk
+06856,Norwalk
+06857,Norwalk
+06858,Norwalk
+06859,Norwalk
+06860,Norwalk
+06870,Old Greenwich
+06875,Redding Center
+06876,Redding Ridge
+06877,Ridgefield
+06878,Riverside
+06879,Ridgefield
+06880,Westport
+06881,Westport
+06883,Weston
+06888,Westport
+06889,Westport
+06896,Redding
+06897,Wilton
+06901,Stamford
+06902,Stamford
+06903,Stamford
+06904,Stamford
+06905,Stamford
+06906,Stamford
+06907,Stamford
+06910,Stamford
+06911,Stamford
+06912,Stamford
+06913,Stamford
+06914,Stamford
+06920,Stamford
+06921,Stamford
+06922,Stamford
+06925,Stamford
+06926,Stamford
+06927,Stamford
+06928,Stamford
+80001,Arvada
+80002,Arvada
+80003,Arvada
+80004,Arvada
+80005,Arvada
+80006,Arvada
+80007,Arvada
+80010,Aurora
+80011,Aurora
+80012,Aurora
+80013,Aurora
+80014,Aurora
+80015,Aurora
+80016,Aurora
+80017,Aurora
+80018,Aurora
+80019,Aurora
+80020,Broomfield
+80021,Broomfield
+80022,Commerce City
+80024,Dupont
+80025,Eldorado Springs
+80026,Lafayette
+80027,Louisville
+80028,Louisville
+80030,Westminster
+80031,Westminster
+80033,Wheat Ridge
+80034,Wheat Ridge
+80035,Westminster
+80036,Westminster
+80037,Commerce City
+80038,Broomfield
+80040,Aurora
+80041,Aurora
+80042,Aurora
+80044,Aurora
+80045,Aurora
+80046,Aurora
+80047,Aurora
+80101,Agate
+80102,Bennett
+80103,Byers
+80104,Castle Rock
+80105,Deer Trail
+80106,Elbert
+80107,Elizabeth
+80110,Englewood
+80111,Englewood
+80112,Englewood
+80116,Franktown
+80117,Kiowa
+80118,Larkspur
+80120,Littleton
+80121,Littleton
+80122,Littleton
+80123,Littleton
+80124,Littleton
+80125,Littleton
+80126,Littleton
+80127,Littleton
+80128,Littleton
+80131,Louviers
+80132,Monument
+80133,Palmer Lake
+80134,Parker
+80135,Sedalia
+80136,Strasburg
+80137,Watkins
+80138,Parker
+80150,Englewood
+80151,Englewood
+80154,Englewood
+80155,Englewood
+80160,Littleton
+80161,Littleton
+80162,Littleton
+80163,Littleton
+80165,Littleton
+80166,Littleton
+80201,Denver
+80202,Denver
+80203,Denver
+80204,Denver
+80205,Denver
+80206,Denver
+80207,Denver
+80208,Denver
+80209,Denver
+80210,Denver
+80211,Denver
+80212,Denver
+80214,Denver
+80215,Denver
+80216,Denver
+80217,Denver
+80218,Denver
+80219,Denver
+80220,Denver
+80221,Denver
+80222,Denver
+80223,Denver
+80224,Denver
+80225,Denver
+80226,Denver
+80227,Denver
+80228,Denver
+80229,Denver
+80230,Denver
+80231,Denver
+80232,Denver
+80233,Denver
+80234,Denver
+80235,Denver
+80236,Denver
+80237,Denver
+80238,Denver
+80239,Denver
+80241,Denver
+80243,Denver
+80244,Denver
+80246,Denver
+80248,Denver
+80249,Denver
+80250,Denver
+80251,Denver
+80252,Denver
+80254,Denver
+80255,Denver
+80256,Denver
+80257,Denver
+80259,Denver
+80260,Denver
+80261,Denver
+80262,Denver
+80263,Denver
+80264,Denver
+80265,Denver
+80266,Denver
+80270,Denver
+80271,Denver
+80273,Denver
+80274,Denver
+80275,Denver
+80279,Denver
+80280,Denver
+80281,Denver
+80290,Denver
+80291,Denver
+80292,Denver
+80293,Denver
+80294,Denver
+80295,Denver
+80299,Denver
+80301,Boulder
+80302,Boulder
+80303,Boulder
+80304,Boulder
+80306,Boulder
+80307,Boulder
+80308,Boulder
+80309,Boulder
+80310,Boulder
+80314,Boulder
+80321,Boulder
+80322,Boulder
+80323,Boulder
+80328,Boulder
+80329,Boulder
+80401,Golden
+80402,Golden
+80403,Golden
+80419,Golden
+80420,Alma
+80421,Bailey
+80422,Black Hawk
+80423,Bond
+80424,Breckenridge
+80425,Buffalo Creek
+80426,Burns
+80427,Central City
+80428,Clark
+80429,Climax
+80430,Coalmont
+80432,Como
+80433,Conifer
+80434,Cowdrey
+80435,Dillon
+80436,Dumont
+80437,Evergreen
+80438,Empire
+80439,Evergreen
+80440,Fairplay
+80442,Fraser
+80443,Frisco
+80444,Georgetown
+80446,Granby
+80447,Grand Lake
+80448,Grant
+80449,Hartsel
+80451,Hot Sulphur Springs
+80452,Idaho Springs
+80453,Idledale
+80454,Indian Hills
+80455,Jamestown
+80456,Jefferson
+80457,Kittredge
+80459,Kremmling
+80461,Leadville
+80463,Mc Coy
+80465,Morrison
+80466,Nederland
+80467,Oak Creek
+80468,Parshall
+80469,Phippsburg
+80470,Pine
+80471,Pinecliffe
+80473,Rand
+80474,Rollinsville
+80475,Shawnee
+80476,Silver Plume
+80477,Steamboat Springs
+80478,Tabernash
+80479,Toponas
+80480,Walden
+80481,Ward
+80482,Winter Park
+80483,Yampa
+80487,Steamboat Springs
+80488,Steamboat Springs
+80497,Silverthorne
+80498,Silverthorne
+80501,Longmont
+80502,Longmont
+80503,Longmont
+80504,Longmont
+80510,Allenspark
+80511,Estes Park
+80512,Bellvue
+80513,Berthoud
+80514,Dacono
+80515,Drake
+80516,Erie
+80517,Estes Park
+80520,Firestone
+80521,Fort Collins
+80522,Fort Collins
+80523,Fort Collins
+80524,Fort Collins
+80525,Fort Collins
+80526,Fort Collins
+80527,Fort Collins
+80528,Fort Collins
+80530,Frederick
+80532,Glen Haven
+80533,Hygiene
+80534,Johnstown
+80535,Laporte
+80536,Livermore
+80537,Loveland
+80538,Loveland
+80539,Loveland
+80540,Lyons
+80541,Masonville
+80542,Mead
+80543,Milliken
+80544,Niwot
+80545,Red Feather Lakes
+80546,Severance
+80547,Timnath
+80549,Wellington
+80550,Windsor
+80551,Windsor
+80553,Fort Collins
+80601,Brighton
+80610,Ault
+80611,Briggsdale
+80612,Carr
+80614,Eastlake
+80615,Eaton
+80620,Evans
+80621,Fort Lupton
+80622,Galeton
+80623,Gilcrest
+80624,Gill
+80631,Greeley
+80632,Greeley
+80633,Greeley
+80634,Greeley
+80638,Greeley
+80639,Greeley
+80640,Henderson
+80642,Hudson
+80643,Keenesburg
+80644,Kersey
+80645,La Salle
+80646,Lucerne
+80648,Nunn
+80649,Orchard
+80650,Pierce
+80651,Platteville
+80652,Roggen
+80653,Weldona
+80654,Wiggins
+80701,Fort Morgan
+80705,Log Lane Village
+80720,Akron
+80721,Amherst
+80722,Atwood
+80723,Brush
+80726,Crook
+80727,Eckley
+80728,Fleming
+80729,Grover
+80731,Haxtun
+80732,Hereford
+80733,Hillrose
+80734,Holyoke
+80735,Idalia
+80736,Iliff
+80737,Julesburg
+80740,Lindon
+80741,Merino
+80742,New Raymer
+80743,Otis
+80744,Ovid
+80745,Padroni
+80746,Paoli
+80747,Peetz
+80749,Sedgwick
+80750,Snyder
+80751,Sterling
+80754,Stoneham
+80755,Vernon
+80757,Woodrow
+80758,Wray
+80759,Yuma
+80801,Anton
+80802,Arapahoe
+80804,Arriba
+80805,Bethune
+80807,Burlington
+80808,Calhan
+80809,Cascade
+80810,Cheyenne Wells
+80812,Cope
+80813,Cripple Creek
+80814,Divide
+80815,Flagler
+80816,Florissant
+80817,Fountain
+80818,Genoa
+80819,Green Mountain Falls
+80820,Guffey
+80821,Hugo
+80822,Joes
+80823,Karval
+80824,Kirk
+80825,Kit Carson
+80826,Limon
+80827,Lake George
+80828,Limon
+80829,Manitou Springs
+80830,Matheson
+80831,Peyton
+80832,Ramah
+80833,Rush
+80834,Seibert
+80835,Simla
+80836,Stratton
+80840,U S A F Academy
+80841,U S A F Academy
+80860,Victor
+80861,Vona
+80862,Wild Horse
+80863,Woodland Park
+80864,Yoder
+80866,Woodland Park
+80901,Colorado Springs
+80903,Colorado Springs
+80904,Colorado Springs
+80905,Colorado Springs
+80906,Colorado Springs
+80907,Colorado Springs
+80908,Colorado Springs
+80909,Colorado Springs
+80910,Colorado Springs
+80911,Colorado Springs
+80912,Colorado Springs
+80913,Colorado Springs
+80914,Colorado Springs
+80915,Colorado Springs
+80916,Colorado Springs
+80917,Colorado Springs
+80918,Colorado Springs
+80919,Colorado Springs
+80920,Colorado Springs
+80921,Colorado Springs
+80922,Colorado Springs
+80925,Colorado Springs
+80926,Colorado Springs
+80928,Colorado Springs
+80929,Colorado Springs
+80930,Colorado Springs
+80931,Colorado Springs
+80932,Colorado Springs
+80933,Colorado Springs
+80934,Colorado Springs
+80935,Colorado Springs
+80936,Colorado Springs
+80937,Colorado Springs
+80940,Colorado Springs
+80941,Colorado Springs
+80942,Colorado Springs
+80943,Colorado Springs
+80944,Colorado Springs
+80945,Colorado Springs
+80946,Colorado Springs
+80947,Colorado Springs
+80949,Colorado Springs
+80950,Colorado Springs
+80960,Colorado Springs
+80962,Colorado Springs
+80970,Colorado Springs
+80977,Colorado Springs
+80995,Colorado Springs
+80997,Colorado Springs
+81001,Pueblo
+81002,Pueblo
+81003,Pueblo
+81004,Pueblo
+81005,Pueblo
+81006,Pueblo
+81007,Pueblo
+81008,Pueblo
+81009,Pueblo
+81010,Pueblo
+81011,Pueblo
+81012,Pueblo
+81013,Pueblo
+81014,Pueblo
+81015,Pueblo
+81019,Colorado City
+81020,Aguilar
+81021,Arlington
+81022,Avondale
+81023,Beulah
+81024,Boncarbo
+81025,Boone
+81027,Branson
+81029,Campo
+81030,Cheraw
+81033,Crowley
+81034,Crowley
+81036,Eads
+81038,Fort Lyon
+81039,Fowler
+81040,Gardner
+81041,Granada
+81042,Gulnare
+81043,Hartman
+81044,Hasty
+81045,Haswell
+81046,Hoehne
+81047,Holly
+81049,Kim
+81050,La Junta
+81052,Lamar
+81054,Las Animas
+81055,La Veta
+81057,Mc Clave
+81058,Manzanola
+81059,Model
+81062,Olney Springs
+81063,Ordway
+81064,Pritchett
+81066,Red Wing
+81067,Rocky Ford
+81069,Rye
+81071,Sheridan Lake
+81073,Springfield
+81074,Starkville
+81076,Sugar City
+81077,Swink
+81081,Trinchera
+81082,Trinidad
+81084,Two Buttes
+81087,Vilas
+81089,Walsenburg
+81090,Walsh
+81091,Weston
+81092,Wiley
+81101,Alamosa
+81102,Alamosa
+81120,Antonito
+81121,Arboles
+81122,Bayfield
+81123,Blanca
+81124,Capulin
+81125,Center
+81126,Chama
+81127,Chimney Rock
+81128,Chromo
+81129,Conejos
+81130,Creede
+81131,Crestone
+81132,Del Norte
+81133,Fort Garland
+81134,Garcia
+81135,Homelake
+81136,Hooper
+81137,Ignacio
+81138,Jaroso
+81140,La Jara
+81141,Manassa
+81143,Moffat
+81144,Monte Vista
+81146,Mosca
+81147,Pagosa Springs
+81148,Romeo
+81149,Saguache
+81151,Sanford
+81152,San Luis
+81153,San Pablo
+81154,South Fork
+81155,Villa Grove
+81157,Pagosa Springs
+81201,Salida
+81210,Almont
+81211,Buena Vista
+81212,Canon City
+81215,Canon City
+81220,Cimarron
+81221,Coal Creek
+81222,Coaldale
+81223,Cotopaxi
+81224,Crested Butte
+81225,Crested Butte
+81226,Florence
+81227,Monarch
+81228,Granite
+81230,Gunnison
+81231,Gunnison
+81232,Hillside
+81233,Howard
+81235,Lake City
+81236,Nathrop
+81237,Ohio City
+81239,Parlin
+81240,Penrose
+81241,Pitkin
+81242,Poncha Springs
+81243,Powderhorn
+81244,Rockvale
+81246,Canon City
+81247,Gunnison
+81248,Sargents
+81251,Twin Lakes
+81252,Westcliffe
+81253,Wetmore
+81290,Florence
+81301,Durango
+81302,Durango
+81320,Cahone
+81321,Cortez
+81323,Dolores
+81324,Dove Creek
+81325,Egnar
+81326,Hesperus
+81327,Lewis
+81328,Mancos
+81329,Marvel
+81330,Mesa Verde National Park
+81331,Pleasant View
+81332,Rico
+81334,Towaoc
+81335,Yellow Jacket
+81401,Montrose
+81402,Montrose
+81410,Austin
+81411,Bedrock
+81413,Cedaredge
+81414,Cory
+81415,Crawford
+81416,Delta
+81418,Eckert
+81419,Hotchkiss
+81420,Lazear
+81421,Maher
+81422,Naturita
+81423,Norwood
+81424,Nucla
+81425,Olathe
+81426,Ophir
+81427,Ouray
+81428,Paonia
+81429,Paradox
+81430,Placerville
+81431,Redvale
+81432,Ridgway
+81433,Silverton
+81434,Somerset
+81435,Telluride
+81501,Grand Junction
+81502,Grand Junction
+81503,Grand Junction
+81504,Grand Junction
+81505,Grand Junction
+81506,Grand Junction
+81520,Clifton
+81521,Fruita
+81522,Gateway
+81523,Glade Park
+81524,Loma
+81525,Mack
+81526,Palisade
+81527,Whitewater
+81601,Glenwood Springs
+81602,Glenwood Springs
+81610,Dinosaur
+81611,Aspen
+81612,Aspen
+81615,Snowmass Village
+81620,Avon
+81621,Basalt
+81623,Carbondale
+81624,Collbran
+81625,Craig
+81626,Craig
+81628,El Jebel
+81630,De Beque
+81631,Eagle
+81632,Edwards
+81633,Dinosaur
+81635,Parachute
+81636,Battlement Mesa
+81637,Gypsum
+81638,Hamilton
+81639,Hayden
+81640,Maybell
+81641,Meeker
+81642,Meredith
+81643,Mesa
+81645,Minturn
+81646,Molina
+81647,New Castle
+81648,Rangely
+81649,Red Cliff
+81650,Rifle
+81652,Silt
+81653,Slater
+81654,Snowmass
+81655,Wolcott
+81656,Woody Creek
+81657,Vail
+81658,Vail
+90001,Los Angeles
+90002,Los Angeles
+90003,Los Angeles
+90004,Los Angeles
+90005,Los Angeles
+90006,Los Angeles
+90007,Los Angeles
+90008,Los Angeles
+90009,Los Angeles
+90010,Los Angeles
+90011,Los Angeles
+90012,Los Angeles
+90013,Los Angeles
+90014,Los Angeles
+90015,Los Angeles
+90016,Los Angeles
+90017,Los Angeles
+90018,Los Angeles
+90019,Los Angeles
+90020,Los Angeles
+90021,Los Angeles
+90022,Los Angeles
+90023,Los Angeles
+90024,Los Angeles
+90025,Los Angeles
+90026,Los Angeles
+90027,Los Angeles
+90028,Los Angeles
+90029,Los Angeles
+90030,Los Angeles
+90031,Los Angeles
+90032,Los Angeles
+90033,Los Angeles
+90034,Los Angeles
+90035,Los Angeles
+90036,Los Angeles
+90037,Los Angeles
+90038,Los Angeles
+90039,Los Angeles
+90040,Los Angeles
+90041,Los Angeles
+90042,Los Angeles
+90043,Los Angeles
+90044,Los Angeles
+90045,Los Angeles
+90046,Los Angeles
+90047,Los Angeles
+90048,Los Angeles
+90049,Los Angeles
+90050,Los Angeles
+90051,Los Angeles
+90052,Los Angeles
+90053,Los Angeles
+90054,Los Angeles
+90055,Los Angeles
+90056,Los Angeles
+90057,Los Angeles
+90058,Los Angeles
+90059,Los Angeles
+90060,Los Angeles
+90061,Los Angeles
+90062,Los Angeles
+90063,Los Angeles
+90064,Los Angeles
+90065,Los Angeles
+90066,Los Angeles
+90067,Los Angeles
+90068,Los Angeles
+90069,West Hollywood
+90070,Los Angeles
+90071,Los Angeles
+90072,Los Angeles
+90073,Los Angeles
+90074,Los Angeles
+90075,Los Angeles
+90076,Los Angeles
+90077,Los Angeles
+90078,Los Angeles
+90079,Los Angeles
+90080,Los Angeles
+90081,Los Angeles
+90082,Los Angeles
+90083,Los Angeles
+90084,Los Angeles
+90086,Los Angeles
+90087,Los Angeles
+90088,Los Angeles
+90089,Los Angeles
+90091,Los Angeles
+90093,Los Angeles
+90094,Los Angeles
+90095,Los Angeles
+90096,Los Angeles
+90097,Los Angeles
+90099,Los Angeles
+90101,Los Angeles
+90102,Los Angeles
+90103,Los Angeles
+90174,Los Angeles
+90185,Los Angeles
+90201,Bell
+90202,Bell Gardens
+90209,Beverly Hills
+90210,Beverly Hills
+90211,Beverly Hills
+90212,Beverly Hills
+90213,Beverly Hills
+90220,Compton
+90221,Compton
+90222,Compton
+90223,Compton
+90224,Compton
+90230,Culver City
+90231,Culver City
+90232,Culver City
+90233,Culver City
+90239,Downey
+90240,Downey
+90241,Downey
+90242,Downey
+90245,El Segundo
+90247,Gardena
+90248,Gardena
+90249,Gardena
+90250,Hawthorne
+90251,Hawthorne
+90254,Hermosa Beach
+90255,Huntington Park
+90260,Lawndale
+90261,Lawndale
+90262,Lynwood
+90263,Malibu
+90264,Malibu
+90265,Malibu
+90266,Manhattan Beach
+90267,Manhattan Beach
+90270,Maywood
+90272,Pacific Palisades
+90274,Palos Verdes Peninsula
+90275,Rancho Palos Verdes
+90277,Redondo Beach
+90278,Redondo Beach
+90280,South Gate
+90290,Topanga
+90291,Venice
+90292,Marina Del Rey
+90293,Playa Del Rey
+90294,Venice
+90295,Marina Del Rey
+90296,Playa Del Rey
+90301,Inglewood
+90302,Inglewood
+90303,Inglewood
+90304,Inglewood
+90305,Inglewood
+90306,Inglewood
+90307,Inglewood
+90308,Inglewood
+90309,Inglewood
+90310,Inglewood
+90311,Inglewood
+90312,Inglewood
+90313,Inglewood
+90397,Inglewood
+90398,Inglewood
+90401,Santa Monica
+90402,Santa Monica
+90403,Santa Monica
+90404,Santa Monica
+90405,Santa Monica
+90406,Santa Monica
+90407,Santa Monica
+90408,Santa Monica
+90409,Santa Monica
+90410,Santa Monica
+90411,Santa Monica
+90501,Torrance
+90502,Torrance
+90503,Torrance
+90504,Torrance
+90505,Torrance
+90506,Torrance
+90507,Torrance
+90508,Torrance
+90509,Torrance
+90510,Torrance
+90601,Whittier
+90602,Whittier
+90603,Whittier
+90604,Whittier
+90605,Whittier
+90606,Whittier
+90607,Whittier
+90608,Whittier
+90609,Whittier
+90610,Whittier
+90612,Whittier
+90620,Buena Park
+90621,Buena Park
+90622,Buena Park
+90623,La Palma
+90624,Buena Park
+90630,Cypress
+90631,La Habra
+90632,La Habra
+90633,La Habra
+90637,La Mirada
+90638,La Mirada
+90639,La Mirada
+90640,Montebello
+90650,Norwalk
+90651,Norwalk
+90652,Norwalk
+90659,Norwalk
+90660,Pico Rivera
+90661,Pico Rivera
+90662,Pico Rivera
+90665,Pico Rivera
+90670,Santa FE Springs
+90671,Santa FE Springs
+90680,Stanton
+90701,Artesia
+90702,Artesia
+90703,Cerritos
+90704,Avalon
+90706,Bellflower
+90707,Bellflower
+90710,Harbor City
+90711,Lakewood
+90712,Lakewood
+90713,Lakewood
+90714,Lakewood
+90715,Lakewood
+90716,Hawaiian Gardens
+90717,Lomita
+90720,Los Alamitos
+90721,Los Alamitos
+90723,Paramount
+90731,San Pedro
+90732,San Pedro
+90733,San Pedro
+90734,San Pedro
+90740,Seal Beach
+90742,Sunset Beach
+90743,Surfside
+90744,Wilmington
+90745,Carson
+90746,Carson
+90747,Carson
+90748,Wilmington
+90749,Carson
+90801,Long Beach
+90802,Long Beach
+90803,Long Beach
+90804,Long Beach
+90805,Long Beach
+90806,Long Beach
+90807,Long Beach
+90808,Long Beach
+90809,Long Beach
+90810,Long Beach
+90813,Long Beach
+90814,Long Beach
+90815,Long Beach
+90822,Long Beach
+90831,Long Beach
+90832,Long Beach
+90833,Long Beach
+90834,Long Beach
+90835,Long Beach
+90840,Long Beach
+90842,Long Beach
+90844,Long Beach
+90845,Long Beach
+90846,Long Beach
+90847,Long Beach
+90848,Long Beach
+90853,Long Beach
+90888,Long Beach
+91001,Altadena
+91003,Altadena
+91006,Arcadia
+91007,Arcadia
+91009,Duarte
+91010,Duarte
+91011,La Canada Flintridge
+91012,La Canada Flintridge
+91016,Monrovia
+91017,Monrovia
+91020,Montrose
+91021,Montrose
+91023,Mount Wilson
+91024,Sierra Madre
+91025,Sierra Madre
+91030,South Pasadena
+91031,South Pasadena
+91040,Sunland
+91041,Sunland
+91042,Tujunga
+91043,Tujunga
+91046,Verdugo City
+91050,Pasadena
+91051,Pasadena
+91066,Arcadia
+91077,Arcadia
+91101,Pasadena
+91102,Pasadena
+91103,Pasadena
+91104,Pasadena
+91105,Pasadena
+91106,Pasadena
+91107,Pasadena
+91108,San Marino
+91109,Pasadena
+91110,Pasadena
+91114,Pasadena
+91115,Pasadena
+91116,Pasadena
+91117,Pasadena
+91118,San Marino
+91121,Pasadena
+91123,Pasadena
+91124,Pasadena
+91125,Pasadena
+91126,Pasadena
+91129,Pasadena
+91131,Pasadena
+91175,Pasadena
+91182,Pasadena
+91184,Pasadena
+91185,Pasadena
+91186,Pasadena
+91187,Pasadena
+91188,Pasadena
+91189,Pasadena
+91191,Pasadena
+91201,Glendale
+91202,Glendale
+91203,Glendale
+91204,Glendale
+91205,Glendale
+91206,Glendale
+91207,Glendale
+91208,Glendale
+91209,Glendale
+91210,Glendale
+91214,La Crescenta
+91221,Glendale
+91222,Glendale
+91224,La Crescenta
+91225,Glendale
+91226,Glendale
+91301,Agoura Hills
+91302,Calabasas
+91303,Canoga Park
+91304,Canoga Park
+91305,Canoga Park
+91306,Winnetka
+91307,West Hills
+91308,West Hills
+91309,Canoga Park
+91310,Castaic
+91311,Chatsworth
+91312,Chatsworth
+91313,Chatsworth
+91316,Encino
+91319,Newbury Park
+91320,Newbury Park
+91321,Newhall
+91322,Newhall
+91324,Northridge
+91325,Northridge
+91326,Northridge
+91327,Northridge
+91328,Northridge
+91329,Northridge
+91330,Northridge
+91331,Pacoima
+91333,Pacoima
+91334,Pacoima
+91335,Reseda
+91337,Reseda
+91340,San Fernando
+91341,San Fernando
+91342,Sylmar
+91343,North Hills
+91344,Granada Hills
+91345,Mission Hills
+91346,Mission Hills
+91350,Santa Clarita
+91351,Canyon Country
+91352,Sun Valley
+91353,Sun Valley
+91354,Valencia
+91355,Valencia
+91356,Tarzana
+91357,Tarzana
+91358,Thousand Oaks
+91359,Westlake Village
+91360,Thousand Oaks
+91361,Westlake Village
+91362,Thousand Oaks
+91363,Westlake Village
+91364,Woodland Hills
+91365,Woodland Hills
+91367,Woodland Hills
+91371,Woodland Hills
+91372,Calabasas
+91376,Agoura Hills
+91377,Oak Park
+91380,Santa Clarita
+91381,Stevenson Ranch
+91382,Santa Clarita
+91383,Santa Clarita
+91384,Castaic
+91385,Valencia
+91386,Canyon Country
+91388,Van Nuys
+91392,Sylmar
+91393,North Hills
+91394,Granada Hills
+91395,Mission Hills
+91396,Winnetka
+91399,Woodland Hills
+91401,Van Nuys
+91402,Panorama City
+91403,Sherman Oaks
+91404,Van Nuys
+91405,Van Nuys
+91406,Van Nuys
+91407,Van Nuys
+91408,Van Nuys
+91409,Van Nuys
+91410,Van Nuys
+91411,Van Nuys
+91412,Panorama City
+91413,Sherman Oaks
+91416,Encino
+91423,Sherman Oaks
+91426,Encino
+91436,Encino
+91470,Van Nuys
+91482,Van Nuys
+91495,Sherman Oaks
+91496,Van Nuys
+91497,Van Nuys
+91499,Van Nuys
+91501,Burbank
+91502,Burbank
+91503,Burbank
+91504,Burbank
+91505,Burbank
+91506,Burbank
+91507,Burbank
+91508,Burbank
+91510,Burbank
+91521,Burbank
+91522,Burbank
+91523,Burbank
+91526,Burbank
+91601,North Hollywood
+91602,North Hollywood
+91603,North Hollywood
+91604,Studio City
+91605,North Hollywood
+91606,North Hollywood
+91607,Valley Village
+91608,Universal City
+91609,North Hollywood
+91610,Toluca Lake
+91611,North Hollywood
+91612,North Hollywood
+91614,Studio City
+91615,North Hollywood
+91616,North Hollywood
+91617,Valley Village
+91618,North Hollywood
+91701,Alta Loma
+91702,Azusa
+91706,Baldwin Park
+91708,Chino
+91709,Chino Hills
+91710,Chino
+91711,Claremont
+91714,City of Industry
+91715,City of Industry
+91716,City of Industry
+91718,Corona
+91719,Corona
+91720,Corona
+91722,Covina
+91723,Covina
+91724,Covina
+91729,Rancho Cucamonga
+91730,Rancho Cucamonga
+91731,El Monte
+91732,El Monte
+91733,South El Monte
+91734,El Monte
+91735,El Monte
+91737,Alta Loma
+91739,Rancho Cucamonga
+91740,Glendora
+91741,Glendora
+91743,Guasti
+91744,La Puente
+91745,Hacienda Heights
+91746,La Puente
+91747,La Puente
+91748,Rowland Heights
+91749,La Puente
+91750,La Verne
+91752,Mira Loma
+91754,Monterey Park
+91755,Monterey Park
+91756,Monterey Park
+91758,Ontario
+91759,Mt Baldy
+91760,Norco
+91761,Ontario
+91762,Ontario
+91763,Montclair
+91764,Ontario
+91765,Diamond Bar
+91766,Pomona
+91767,Pomona
+91768,Pomona
+91769,Pomona
+91770,Rosemead
+91771,Rosemead
+91772,Rosemead
+91773,San Dimas
+91775,San Gabriel
+91776,San Gabriel
+91778,San Gabriel
+91780,Temple City
+91784,Upland
+91785,Upland
+91786,Upland
+91788,Walnut
+91789,Walnut
+91790,West Covina
+91791,West Covina
+91792,West Covina
+91793,West Covina
+91795,Walnut
+91797,Pomona
+91798,Ontario
+91799,Pomona
+91801,Alhambra
+91802,Alhambra
+91803,Alhambra
+91804,Alhambra
+91841,Alhambra
+91896,Alhambra
+91899,Alhambra
+91901,Alpine
+91902,Bonita
+91903,Alpine
+91905,Boulevard
+91906,Campo
+91908,Bonita
+91909,Chula Vista
+91910,Chula Vista
+91911,Chula Vista
+91912,Chula Vista
+91913,Chula Vista
+91914,Chula Vista
+91915,Chula Vista
+91916,Descanso
+91917,Dulzura
+91921,Chula Vista
+91931,Guatay
+91932,Imperial Beach
+91933,Imperial Beach
+91934,Jacumba
+91935,Jamul
+91941,La Mesa
+91942,La Mesa
+91943,La Mesa
+91944,La Mesa
+91945,Lemon Grove
+91946,Lemon Grove
+91947,Lincoln Acres
+91948,Mount Laguna
+91950,National City
+91951,National City
+91962,Pine Valley
+91963,Potrero
+91976,Spring Valley
+91977,Spring Valley
+91978,Spring Valley
+91979,Spring Valley
+91980,Tecate
+91987,Tecate
+91990,Potrero
+92003,Bonsall
+92004,Borrego Springs
+92007,Cardiff by the Sea
+92008,Carlsbad
+92009,Carlsbad
+92013,Carlsbad
+92014,Del Mar
+92018,Carlsbad
+92019,El Cajon
+92020,El Cajon
+92021,El Cajon
+92022,El Cajon
+92023,Encinitas
+92024,Encinitas
+92025,Escondido
+92026,Escondido
+92027,Escondido
+92028,Fallbrook
+92029,Escondido
+92030,Escondido
+92033,Escondido
+92036,Julian
+92037,La Jolla
+92038,La Jolla
+92039,La Jolla
+92040,Lakeside
+92046,Escondido
+92049,Oceanside
+92051,Oceanside
+92052,Oceanside
+92054,Oceanside
+92055,Camp Pendleton
+92056,Oceanside
+92057,Oceanside
+92058,Oceanside
+92059,Pala
+92060,Palomar Mountain
+92061,Pauma Valley
+92064,Poway
+92065,Ramona
+92066,Ranchita
+92067,Rancho Santa FE
+92068,San Luis Rey
+92069,San Marcos
+92070,Santa Ysabel
+92071,Santee
+92072,Santee
+92074,Poway
+92075,Solana Beach
+92078,San Marcos
+92079,San Marcos
+92082,Valley Center
+92083,Vista
+92084,Vista
+92085,Vista
+92086,Warner Springs
+92088,Fallbrook
+92090,El Cajon
+92091,Rancho Santa FE
+92092,La Jolla
+92093,La Jolla
+92096,San Marcos
+92101,San Diego
+92102,San Diego
+92103,San Diego
+92104,San Diego
+92105,San Diego
+92106,San Diego
+92107,San Diego
+92108,San Diego
+92109,San Diego
+92110,San Diego
+92111,San Diego
+92112,San Diego
+92113,San Diego
+92114,San Diego
+92115,San Diego
+92116,San Diego
+92117,San Diego
+92118,Coronado
+92119,San Diego
+92120,San Diego
+92121,San Diego
+92122,San Diego
+92123,San Diego
+92124,San Diego
+92126,San Diego
+92127,San Diego
+92128,San Diego
+92129,San Diego
+92130,San Diego
+92131,San Diego
+92132,San Diego
+92133,San Diego
+92134,San Diego
+92135,San Diego
+92136,San Diego
+92137,San Diego
+92138,San Diego
+92139,San Diego
+92140,San Diego
+92142,San Diego
+92143,San Ysidro
+92145,San Diego
+92147,San Diego
+92149,San Diego
+92150,San Diego
+92152,San Diego
+92153,San Diego
+92154,San Diego
+92155,San Diego
+92158,San Diego
+92159,San Diego
+92160,San Diego
+92161,San Diego
+92162,San Diego
+92163,San Diego
+92164,San Diego
+92165,San Diego
+92166,San Diego
+92167,San Diego
+92168,San Diego
+92169,San Diego
+92170,San Diego
+92171,San Diego
+92172,San Diego
+92173,San Ysidro
+92174,San Diego
+92175,San Diego
+92176,San Diego
+92177,San Diego
+92178,Coronado
+92179,San Diego
+92182,San Diego
+92184,San Diego
+92186,San Diego
+92187,San Diego
+92190,San Diego
+92191,San Diego
+92192,San Diego
+92193,San Diego
+92194,San Diego
+92195,San Diego
+92196,San Diego
+92197,San Diego
+92198,San Diego
+92199,San Diego
+92201,Indio
+92202,Indio
+92203,Indio
+92210,Indian Wells
+92211,Palm Desert
+92220,Banning
+92222,Bard
+92223,Beaumont
+92225,Blythe
+92226,Blythe
+92227,Brawley
+92230,Cabazon
+92231,Calexico
+92232,Calexico
+92233,Calipatria
+92234,Cathedral City
+92235,Cathedral City
+92236,Coachella
+92239,Desert Center
+92240,Desert Hot Springs
+92241,Desert Hot Springs
+92242,Earp
+92243,El Centro
+92244,El Centro
+92249,Heber
+92250,Holtville
+92251,Imperial
+92252,Joshua Tree
+92253,La Quinta
+92254,Mecca
+92255,Palm Desert
+92256,Morongo Valley
+92257,Niland
+92258,North Palm Springs
+92259,Ocotillo
+92260,Palm Desert
+92261,Palm Desert
+92262,Palm Springs
+92263,Palm Springs
+92264,Palm Springs
+92266,Palo Verde
+92267,Parker Dam
+92268,Pioneertown
+92270,Rancho Mirage
+92273,Seeley
+92274,Thermal
+92275,Salton City
+92276,Thousand Palms
+92277,Twentynine Palms
+92278,Twentynine Palms
+92280,Vidal
+92281,Westmorland
+92282,White Water
+92283,Winterhaven
+92284,Yucca Valley
+92285,Landers
+92286,Yucca Valley
+92292,Palm Springs
+92301,Adelanto
+92304,Amboy
+92305,Angelus Oaks
+92307,Apple Valley
+92308,Apple Valley
+92309,Baker
+92310,Fort Irwin
+92311,Barstow
+92312,Barstow
+92313,Grand Terrace
+92314,Big Bear City
+92315,Big Bear Lake
+92316,Bloomington
+92317,Blue Jay
+92318,Bryn Mawr
+92320,Calimesa
+92321,Cedar Glen
+92322,Cedarpines Park
+92323,Cima
+92324,Colton
+92325,Crestline
+92326,Crest Park
+92327,Daggett
+92328,Death Valley
+92329,Phelan
+92332,Essex
+92333,Fawnskin
+92334,Fontana
+92335,Fontana
+92336,Fontana
+92337,Fontana
+92338,Ludlow
+92339,Forest Falls
+92340,Hesperia
+92341,Green Valley Lake
+92342,Helendale
+92345,Hesperia
+92346,Highland
+92347,Hinkley
+92350,Loma Linda
+92352,Lake Arrowhead
+92354,Loma Linda
+92356,Lucerne Valley
+92357,Loma Linda
+92358,Lytle Creek
+92359,Mentone
+92363,Needles
+92364,Nipton
+92365,Newberry Springs
+92366,Mountain Pass
+92368,Oro Grande
+92369,Patton
+92371,Phelan
+92372,Pinon Hills
+92373,Redlands
+92374,Redlands
+92375,Redlands
+92376,Rialto
+92377,Rialto
+92378,Rimforest
+92382,Running Springs
+92384,Shoshone
+92385,Skyforest
+92386,Sugarloaf
+92389,Tecopa
+92391,Twin Peaks
+92392,Victorville
+92393,Victorville
+92394,Victorville
+92397,Wrightwood
+92398,Yermo
+92399,Yucaipa
+92401,San Bernardino
+92402,San Bernardino
+92403,San Bernardino
+92404,San Bernardino
+92405,San Bernardino
+92406,San Bernardino
+92407,San Bernardino
+92408,San Bernardino
+92410,San Bernardino
+92411,San Bernardino
+92412,San Bernardino
+92413,San Bernardino
+92414,San Bernardino
+92415,San Bernardino
+92416,San Bernardino
+92418,San Bernardino
+92420,San Bernardino
+92423,San Bernardino
+92424,San Bernardino
+92427,San Bernardino
+92501,Riverside
+92502,Riverside
+92503,Riverside
+92504,Riverside
+92505,Riverside
+92506,Riverside
+92507,Riverside
+92508,Riverside
+92509,Riverside
+92513,Riverside
+92514,Riverside
+92515,Riverside
+92516,Riverside
+92517,Riverside
+92518,March Air Force Base
+92519,Riverside
+92521,Riverside
+92522,Riverside
+92530,Lake Elsinore
+92531,Lake Elsinore
+92532,Lake Elsinore
+92536,Aguanga
+92539,Anza
+92543,Hemet
+92544,Hemet
+92545,Hemet
+92546,Hemet
+92548,Homeland
+92549,Idyllwild
+92551,Moreno Valley
+92552,Moreno Valley
+92553,Moreno Valley
+92554,Moreno Valley
+92555,Moreno Valley
+92556,Moreno Valley
+92557,Moreno Valley
+92561,Mountain Center
+92562,Murrieta
+92563,Murrieta
+92564,Murrieta
+92567,Nuevo
+92570,Perris
+92571,Perris
+92572,Perris
+92581,San Jacinto
+92582,San Jacinto
+92583,San Jacinto
+92584,Menifee
+92585,Sun City
+92586,Sun City
+92587,Sun City
+92589,Temecula
+92590,Temecula
+92591,Temecula
+92592,Temecula
+92593,Temecula
+92595,Wildomar
+92596,Winchester
+92599,Perris
+92602,Irvine
+92603,Irvine
+92604,Irvine
+92605,Huntington Beach
+92606,Irvine
+92607,Laguna Niguel
+92610,Foothill Ranch
+92612,Irvine
+92614,Irvine
+92615,Huntington Beach
+92616,Irvine
+92618,Irvine
+92619,Irvine
+92620,Irvine
+92623,Irvine
+92624,Capistrano Beach
+92625,Corona del Mar
+92626,Costa Mesa
+92627,Costa Mesa
+92628,Costa Mesa
+92629,Dana Point
+92630,Lake Forest
+92646,Huntington Beach
+92647,Huntington Beach
+92648,Huntington Beach
+92649,Huntington Beach
+92650,East Irvine
+92651,Laguna Beach
+92652,Laguna Beach
+92653,Laguna Hills
+92654,Laguna Hills
+92655,Midway City
+92656,Aliso Viejo
+92657,Newport Coast
+92658,Newport Beach
+92659,Newport Beach
+92660,Newport Beach
+92661,Newport Beach
+92662,Newport Beach
+92663,Newport Beach
+92672,San Clemente
+92673,San Clemente
+92674,San Clemente
+92675,San Juan Capistrano
+92676,Silverado
+92677,Laguna Niguel
+92678,Trabuco Canyon
+92679,Trabuco Canyon
+92683,Westminster
+92684,Westminster
+92685,Westminster
+92688,Rancho Santa Margarita
+92690,Mission Viejo
+92691,Mission Viejo
+92692,Mission Viejo
+92693,San Juan Capistrano
+92694,Ladera Ranch
+92697,Irvine
+92698,Aliso Viejo
+92701,Santa Ana
+92702,Santa Ana
+92703,Santa Ana
+92704,Santa Ana
+92705,Santa Ana
+92706,Santa Ana
+92707,Santa Ana
+92708,Fountain Valley
+92709,Irvine
+92710,Irvine
+92711,Santa Ana
+92712,Santa Ana
+92728,Fountain Valley
+92735,Santa Ana
+92780,Tustin
+92781,Tustin
+92782,Tustin
+92799,Santa Ana
+92801,Anaheim
+92802,Anaheim
+92803,Anaheim
+92804,Anaheim
+92805,Anaheim
+92806,Anaheim
+92807,Anaheim
+92808,Anaheim
+92811,Atwood
+92812,Anaheim
+92814,Anaheim
+92815,Anaheim
+92816,Anaheim
+92817,Anaheim
+92821,Brea
+92822,Brea
+92823,Brea
+92825,Anaheim
+92831,Fullerton
+92832,Fullerton
+92833,Fullerton
+92834,Fullerton
+92835,Fullerton
+92836,Fullerton
+92837,Fullerton
+92838,Fullerton
+92840,Garden Grove
+92841,Garden Grove
+92842,Garden Grove
+92843,Garden Grove
+92844,Garden Grove
+92845,Garden Grove
+92846,Garden Grove
+92850,Anaheim
+92856,Orange
+92857,Orange
+92859,Orange
+92860,Norco
+92861,Villa Park
+92862,Orange
+92863,Orange
+92864,Orange
+92865,Orange
+92866,Orange
+92867,Orange
+92868,Orange
+92869,Orange
+92870,Placentia
+92871,Placentia
+92877,Corona
+92878,Corona
+92879,Corona
+92880,Corona
+92881,Corona
+92882,Corona
+92883,Corona
+92885,Yorba Linda
+92886,Yorba Linda
+92887,Yorba Linda
+92899,Anaheim
+93001,Ventura
+93002,Ventura
+93003,Ventura
+93004,Ventura
+93005,Ventura
+93006,Ventura
+93007,Ventura
+93009,Ventura
+93010,Camarillo
+93011,Camarillo
+93012,Camarillo
+93013,Carpinteria
+93014,Carpinteria
+93015,Fillmore
+93016,Fillmore
+93020,Moorpark
+93021,Moorpark
+93022,Oak View
+93023,Ojai
+93024,Ojai
+93030,Oxnard
+93031,Oxnard
+93032,Oxnard
+93033,Oxnard
+93034,Oxnard
+93035,Oxnard
+93040,Piru
+93041,Port Hueneme
+93042,Point Mugu NAWC
+93043,Port Hueneme CBC Base
+93044,Port Hueneme
+93060,Santa Paula
+93061,Santa Paula
+93062,Simi Valley
+93063,Simi Valley
+93064,Brandeis
+93065,Simi Valley
+93066,Somis
+93067,Summerland
+93093,Simi Valley
+93094,Simi Valley
+93099,Simi Valley
+93101,Santa Barbara
+93102,Santa Barbara
+93103,Santa Barbara
+93105,Santa Barbara
+93106,Santa Barbara
+93107,Santa Barbara
+93108,Santa Barbara
+93109,Santa Barbara
+93110,Santa Barbara
+93111,Santa Barbara
+93116,Goleta
+93117,Goleta
+93118,Goleta
+93120,Santa Barbara
+93121,Santa Barbara
+93130,Santa Barbara
+93140,Santa Barbara
+93150,Santa Barbara
+93160,Santa Barbara
+93190,Santa Barbara
+93199,Goleta
+93201,Alpaugh
+93202,Armona
+93203,Arvin
+93204,Avenal
+93205,Bodfish
+93206,Buttonwillow
+93207,California Hot Springs
+93208,Camp Nelson
+93210,Coalinga
+93212,Corcoran
+93215,Delano
+93216,Delano
+93218,Ducor
+93219,Earlimart
+93220,Edison
+93221,Exeter
+93222,Frazier Park
+93223,Farmersville
+93224,Fellows
+93225,Frazier Park
+93226,Glennville
+93227,Goshen
+93230,Hanford
+93231,Hanford
+93232,Hanford
+93234,Huron
+93235,Ivanhoe
+93237,Kaweah
+93238,Kernville
+93239,Kettleman City
+93240,Lake Isabella
+93241,Lamont
+93242,Laton
+93243,Lebec
+93244,Lemon Cove
+93245,Lemoore
+93246,Lemoore
+93247,Lindsay
+93249,Lost Hills
+93250,Mc Farland
+93251,Mc Kittrick
+93252,Maricopa
+93254,New Cuyama
+93255,Onyx
+93256,Pixley
+93257,Porterville
+93258,Porterville
+93260,Posey
+93261,Richgrove
+93262,Sequoia National Park
+93263,Shafter
+93265,Springville
+93266,Stratford
+93267,Strathmore
+93268,Taft
+93270,Terra Bella
+93271,Three Rivers
+93272,Tipton
+93274,Tulare
+93275,Tulare
+93276,Tupman
+93277,Visalia
+93278,Visalia
+93279,Visalia
+93280,Wasco
+93282,Waukena
+93283,Weldon
+93285,Wofford Heights
+93286,Woodlake
+93287,Woody
+93291,Visalia
+93292,Visalia
+93301,Bakersfield
+93302,Bakersfield
+93303,Bakersfield
+93304,Bakersfield
+93305,Bakersfield
+93306,Bakersfield
+93307,Bakersfield
+93308,Bakersfield
+93309,Bakersfield
+93311,Bakersfield
+93312,Bakersfield
+93313,Bakersfield
+93380,Bakersfield
+93381,Bakersfield
+93382,Bakersfield
+93383,Bakersfield
+93384,Bakersfield
+93385,Bakersfield
+93386,Bakersfield
+93387,Bakersfield
+93388,Bakersfield
+93389,Bakersfield
+93390,Bakersfield
+93401,San Luis Obispo
+93402,Los Osos
+93403,San Luis Obispo
+93405,San Luis Obispo
+93406,San Luis Obispo
+93407,San Luis Obispo
+93408,San Luis Obispo
+93409,San Luis Obispo
+93410,San Luis Obispo
+93412,Los Osos
+93420,Arroyo Grande
+93421,Arroyo Grande
+93422,Atascadero
+93423,Atascadero
+93424,Avila Beach
+93426,Bradley
+93427,Buellton
+93428,Cambria
+93429,Casmalia
+93430,Cayucos
+93432,Creston
+93433,Grover Beach
+93434,Guadalupe
+93435,Harmony
+93436,Lompoc
+93437,Lompoc
+93438,Lompoc
+93440,Los Alamos
+93441,Los Olivos
+93442,Morro Bay
+93443,Morro Bay
+93444,Nipomo
+93445,Oceano
+93446,Paso Robles
+93447,Paso Robles
+93448,Pismo Beach
+93449,Pismo Beach
+93450,San Ardo
+93451,San Miguel
+93452,San Simeon
+93453,Santa Margarita
+93454,Santa Maria
+93455,Santa Maria
+93456,Santa Maria
+93457,Santa Maria
+93458,Santa Maria
+93460,Santa Ynez
+93461,Shandon
+93463,Solvang
+93464,Solvang
+93465,Templeton
+93483,Grover Beach
+93501,Mojave
+93502,Mojave
+93504,California City
+93505,California City
+93510,Acton
+93512,Benton
+93513,Big Pine
+93514,Bishop
+93515,Bishop
+93516,Boron
+93517,Bridgeport
+93518,Caliente
+93519,Cantil
+93522,Darwin
+93523,Edwards
+93524,Edwards
+93526,Independence
+93527,Inyokern
+93528,Johannesburg
+93529,June Lake
+93530,Keeler
+93531,Keene
+93532,Lake Hughes
+93534,Lancaster
+93535,Lancaster
+93536,Lancaster
+93539,Lancaster
+93541,Lee Vining
+93542,Little Lake
+93543,Littlerock
+93544,Llano
+93545,Lone Pine
+93546,Mammoth Lakes
+93549,Olancha
+93550,Palmdale
+93551,Palmdale
+93552,Palmdale
+93553,Pearblossom
+93554,Randsburg
+93555,Ridgecrest
+93556,Ridgecrest
+93558,Red Mountain
+93560,Rosamond
+93561,Tehachapi
+93562,Trona
+93563,Valyermo
+93581,Tehachapi
+93584,Lancaster
+93586,Lancaster
+93590,Palmdale
+93591,Palmdale
+93592,Trona
+93596,Boron
+93599,Palmdale
+93601,Ahwahnee
+93602,Auberry
+93603,Badger
+93604,Bass Lake
+93605,Big Creek
+93606,Biola
+93607,Burrel
+93608,Cantua Creek
+93609,Caruthers
+93610,Chowchilla
+93611,Clovis
+93612,Clovis
+93613,Clovis
+93614,Coarsegold
+93615,Cutler
+93616,Del Rey
+93618,Dinuba
+93620,Dos Palos
+93621,Dunlap
+93622,Firebaugh
+93623,Fish Camp
+93624,Five Points
+93625,Fowler
+93626,Friant
+93627,Helm
+93628,Hume
+93630,Kerman
+93631,Kingsburg
+93633,Kings Canyon National Pk
+93634,Lakeshore
+93635,Los Banos
+93637,Madera
+93638,Madera
+93639,Madera
+93640,Mendota
+93641,Miramonte
+93642,Mono Hot Springs
+93643,North Fork
+93644,Oakhurst
+93645,O Neals
+93646,Orange Cove
+93647,Orosi
+93648,Parlier
+93649,Piedra
+93650,Fresno
+93651,Prather
+93652,Raisin
+93653,Raymond
+93654,Reedley
+93656,Riverdale
+93657,Sanger
+93660,San Joaquin
+93661,Santa Rita Park
+93662,Selma
+93664,Shaver Lake
+93665,South Dos Palos
+93666,Sultana
+93667,Tollhouse
+93668,Tranquillity
+93669,Wishon
+93670,Yettem
+93673,Traver
+93675,Squaw Valley
+93701,Fresno
+93702,Fresno
+93703,Fresno
+93704,Fresno
+93705,Fresno
+93706,Fresno
+93707,Fresno
+93708,Fresno
+93709,Fresno
+93710,Fresno
+93711,Fresno
+93712,Fresno
+93714,Fresno
+93715,Fresno
+93716,Fresno
+93717,Fresno
+93718,Fresno
+93720,Fresno
+93721,Fresno
+93722,Fresno
+93724,Fresno
+93725,Fresno
+93726,Fresno
+93727,Fresno
+93728,Fresno
+93729,Fresno
+93740,Fresno
+93741,Fresno
+93744,Fresno
+93745,Fresno
+93747,Fresno
+93750,Fresno
+93755,Fresno
+93759,Fresno
+93760,Fresno
+93761,Fresno
+93762,Fresno
+93764,Fresno
+93765,Fresno
+93771,Fresno
+93772,Fresno
+93773,Fresno
+93774,Fresno
+93775,Fresno
+93776,Fresno
+93777,Fresno
+93778,Fresno
+93779,Fresno
+93780,Fresno
+93782,Fresno
+93784,Fresno
+93786,Fresno
+93790,Fresno
+93791,Fresno
+93792,Fresno
+93793,Fresno
+93794,Fresno
+93844,Fresno
+93888,Fresno
+93901,Salinas
+93902,Salinas
+93905,Salinas
+93906,Salinas
+93907,Salinas
+93908,Salinas
+93912,Salinas
+93915,Salinas
+93920,Big Sur
+93921,Carmel
+93922,Carmel
+93923,Carmel
+93924,Carmel Valley
+93925,Chualar
+93926,Gonzales
+93927,Greenfield
+93928,Jolon
+93930,King City
+93932,Lockwood
+93933,Marina
+93940,Monterey
+93942,Monterey
+93943,Monterey
+93944,Monterey
+93950,Pacific Grove
+93953,Pebble Beach
+93954,San Lucas
+93955,Seaside
+93960,Soledad
+93962,Spreckels
+94002,Belmont
+94003,Belmont
+94005,Brisbane
+94010,Burlingame
+94011,Burlingame
+94012,Burlingame
+94014,Daly City
+94015,Daly City
+94016,Daly City
+94017,Daly City
+94018,El Granada
+94019,Half Moon Bay
+94020,La Honda
+94021,Loma Mar
+94022,Los Altos
+94023,Los Altos
+94024,Los Altos
+94025,Menlo Park
+94026,Menlo Park
+94027,Atherton
+94028,Portola Valley
+94029,Menlo Park
+94030,Millbrae
+94031,Millbrae
+94035,Mountain View
+94037,Montara
+94038,Moss Beach
+94039,Mountain View
+94040,Mountain View
+94041,Mountain View
+94042,Mountain View
+94043,Mountain View
+94044,Pacifica
+94045,Pacifica
+94059,Redwood City
+94060,Pescadero
+94061,Redwood City
+94062,Redwood City
+94063,Redwood City
+94064,Redwood City
+94065,Redwood City
+94066,San Bruno
+94067,San Bruno
+94070,San Carlos
+94071,San Carlos
+94074,San Gregorio
+94080,South San Francisco
+94083,South San Francisco
+94086,Sunnyvale
+94087,Sunnyvale
+94088,Sunnyvale
+94089,Sunnyvale
+94090,Sunnyvale
+94096,San Bruno
+94098,San Bruno
+94099,South San Francisco
+94101,San Francisco
+94102,San Francisco
+94103,San Francisco
+94104,San Francisco
+94105,San Francisco
+94106,San Francisco
+94107,San Francisco
+94108,San Francisco
+94109,San Francisco
+94110,San Francisco
+94111,San Francisco
+94112,San Francisco
+94114,San Francisco
+94115,San Francisco
+94116,San Francisco
+94117,San Francisco
+94118,San Francisco
+94119,San Francisco
+94120,San Francisco
+94121,San Francisco
+94122,San Francisco
+94123,San Francisco
+94124,San Francisco
+94125,San Francisco
+94126,San Francisco
+94127,San Francisco
+94128,San Francisco
+94129,San Francisco
+94130,San Francisco
+94131,San Francisco
+94132,San Francisco
+94133,San Francisco
+94134,San Francisco
+94135,San Francisco
+94136,San Francisco
+94137,San Francisco
+94138,San Francisco
+94139,San Francisco
+94140,San Francisco
+94141,San Francisco
+94142,San Francisco
+94143,San Francisco
+94144,San Francisco
+94145,San Francisco
+94146,San Francisco
+94147,San Francisco
+94150,San Francisco
+94151,San Francisco
+94152,San Francisco
+94153,San Francisco
+94154,San Francisco
+94155,San Francisco
+94156,San Francisco
+94157,San Francisco
+94159,San Francisco
+94160,San Francisco
+94161,San Francisco
+94162,San Francisco
+94163,San Francisco
+94164,San Francisco
+94165,San Francisco
+94166,San Francisco
+94167,San Francisco
+94168,San Francisco
+94169,San Francisco
+94170,San Francisco
+94171,San Francisco
+94172,San Francisco
+94175,San Francisco
+94177,San Francisco
+94188,San Francisco
+94203,Sacramento
+94204,Sacramento
+94205,Sacramento
+94206,Sacramento
+94207,Sacramento
+94208,Sacramento
+94209,Sacramento
+94211,Sacramento
+94229,Sacramento
+94230,Sacramento
+94232,Sacramento
+94234,Sacramento
+94235,Sacramento
+94236,Sacramento
+94237,Sacramento
+94239,Sacramento
+94240,Sacramento
+94243,Sacramento
+94244,Sacramento
+94245,Sacramento
+94246,Sacramento
+94247,Sacramento
+94248,Sacramento
+94249,Sacramento
+94250,Sacramento
+94252,Sacramento
+94253,Sacramento
+94254,Sacramento
+94256,Sacramento
+94257,Sacramento
+94258,Sacramento
+94259,Sacramento
+94261,Sacramento
+94262,Sacramento
+94263,Sacramento
+94267,Sacramento
+94268,Sacramento
+94269,Sacramento
+94271,Sacramento
+94273,Sacramento
+94274,Sacramento
+94277,Sacramento
+94278,Sacramento
+94279,Sacramento
+94280,Sacramento
+94282,Sacramento
+94283,Sacramento
+94284,Sacramento
+94285,Sacramento
+94286,Sacramento
+94287,Sacramento
+94288,Sacramento
+94289,Sacramento
+94290,Sacramento
+94291,Sacramento
+94293,Sacramento
+94294,Sacramento
+94295,Sacramento
+94296,Sacramento
+94297,Sacramento
+94298,Sacramento
+94299,Sacramento
+94301,Palo Alto
+94302,Palo Alto
+94303,Palo Alto
+94304,Palo Alto
+94305,Stanford
+94306,Palo Alto
+94307,Palo Alto
+94308,Palo Alto
+94309,Palo Alto
+94310,Palo Alto
+94401,San Mateo
+94402,San Mateo
+94403,San Mateo
+94404,San Mateo
+94405,San Mateo
+94406,San Mateo
+94407,San Mateo
+94408,San Mateo
+94409,San Mateo
+94497,San Mateo
+94501,Alameda
+94502,Alameda
+94506,Danville
+94507,Alamo
+94508,Angwin
+94509,Antioch
+94510,Benicia
+94511,Bethel Island
+94512,Birds Landing
+94513,Brentwood
+94514,Byron
+94515,Calistoga
+94516,Canyon
+94517,Clayton
+94518,Concord
+94519,Concord
+94520,Concord
+94521,Concord
+94522,Concord
+94523,Pleasant Hill
+94524,Concord
+94525,Crockett
+94526,Danville
+94527,Concord
+94528,Diablo
+94529,Concord
+94530,El Cerrito
+94531,Antioch
+94533,Fairfield
+94535,Travis AFB
+94536,Fremont
+94537,Fremont
+94538,Fremont
+94539,Fremont
+94540,Hayward
+94541,Hayward
+94542,Hayward
+94543,Hayward
+94544,Hayward
+94545,Hayward
+94546,Castro Valley
+94547,Hercules
+94548,Knightsen
+94549,Lafayette
+94550,Livermore
+94551,Livermore
+94552,Castro Valley
+94553,Martinez
+94555,Fremont
+94556,Moraga
+94557,Hayward
+94558,Napa
+94559,Napa
+94560,Newark
+94561,Oakley
+94562,Oakville
+94563,Orinda
+94564,Pinole
+94565,Pittsburg
+94566,Pleasanton
+94567,Pope Valley
+94568,Dublin
+94569,Port Costa
+94570,Moraga
+94571,Rio Vista
+94572,Rodeo
+94573,Rutherford
+94574,Saint Helena
+94575,Moraga
+94576,Deer Park
+94577,San Leandro
+94578,San Leandro
+94579,San Leandro
+94580,San Lorenzo
+94581,Napa
+94583,San Ramon
+94585,Suisun City
+94586,Sunol
+94587,Union City
+94588,Pleasanton
+94589,Vallejo
+94590,Vallejo
+94591,Vallejo
+94592,Vallejo
+94595,Walnut Creek
+94596,Walnut Creek
+94597,Walnut Creek
+94598,Walnut Creek
+94599,Yountville
+94601,Oakland
+94602,Oakland
+94603,Oakland
+94604,Oakland
+94605,Oakland
+94606,Oakland
+94607,Oakland
+94608,Emeryville
+94609,Oakland
+94610,Oakland
+94611,Oakland
+94612,Oakland
+94613,Oakland
+94614,Oakland
+94615,Oakland
+94617,Oakland
+94618,Oakland
+94619,Oakland
+94620,Piedmont
+94621,Oakland
+94623,Oakland
+94624,Oakland
+94625,Oakland
+94626,Oakland
+94627,Oakland
+94643,Oakland
+94649,Oakland
+94659,Oakland
+94660,Oakland
+94661,Oakland
+94662,Emeryville
+94666,Oakland
+94701,Berkeley
+94702,Berkeley
+94703,Berkeley
+94704,Berkeley
+94705,Berkeley
+94706,Albany
+94707,Berkeley
+94708,Berkeley
+94709,Berkeley
+94710,Berkeley
+94712,Berkeley
+94720,Berkeley
+94801,Richmond
+94802,Richmond
+94803,El Sobrante
+94804,Richmond
+94805,Richmond
+94806,San Pablo
+94807,Richmond
+94808,Richmond
+94820,El Sobrante
+94850,Richmond
+94901,San Rafael
+94903,San Rafael
+94904,Greenbrae
+94912,San Rafael
+94913,San Rafael
+94914,Kentfield
+94915,San Rafael
+94920,Belvedere Tiburon
+94922,Bodega
+94923,Bodega Bay
+94924,Bolinas
+94925,Corte Madera
+94926,Cotati
+94927,Rohnert Park
+94928,Rohnert Park
+94929,Dillon Beach
+94930,Fairfax
+94931,Cotati
+94933,Forest Knolls
+94937,Inverness
+94938,Lagunitas
+94939,Larkspur
+94940,Marshall
+94941,Mill Valley
+94942,Mill Valley
+94945,Novato
+94946,Nicasio
+94947,Novato
+94948,Novato
+94949,Novato
+94950,Olema
+94951,Penngrove
+94952,Petaluma
+94953,Petaluma
+94954,Petaluma
+94955,Petaluma
+94956,Point Reyes Station
+94957,Ross
+94960,San Anselmo
+94963,San Geronimo
+94964,San Quentin
+94965,Sausalito
+94966,Sausalito
+94970,Stinson Beach
+94971,Tomales
+94972,Valley Ford
+94973,Woodacre
+94974,San Quentin
+94975,Petaluma
+94976,Corte Madera
+94977,Larkspur
+94978,Fairfax
+94979,San Anselmo
+94998,Novato
+94999,Petaluma
+95001,Aptos
+95002,Alviso
+95003,Aptos
+95004,Aromas
+95005,Ben Lomond
+95006,Boulder Creek
+95007,Brookdale
+95008,Campbell
+95009,Campbell
+95010,Capitola
+95011,Campbell
+95012,Castroville
+95013,Coyote
+95014,Cupertino
+95015,Cupertino
+95017,Davenport
+95018,Felton
+95019,Freedom
+95020,Gilroy
+95021,Gilroy
+95023,Hollister
+95024,Hollister
+95026,Holy City
+95030,Los Gatos
+95031,Los Gatos
+95032,Los Gatos
+95033,Los Gatos
+95035,Milpitas
+95036,Milpitas
+95037,Morgan Hill
+95038,Morgan Hill
+95039,Moss Landing
+95041,Mount Hermon
+95042,New Almaden
+95043,Paicines
+95044,Redwood Estates
+95045,San Juan Bautista
+95046,San Martin
+95050,Santa Clara
+95051,Santa Clara
+95052,Santa Clara
+95053,Santa Clara
+95054,Santa Clara
+95055,Santa Clara
+95056,Santa Clara
+95060,Santa Cruz
+95061,Santa Cruz
+95062,Santa Cruz
+95063,Santa Cruz
+95064,Santa Cruz
+95065,Santa Cruz
+95066,Scotts Valley
+95067,Scotts Valley
+95070,Saratoga
+95071,Saratoga
+95073,Soquel
+95075,Tres Pinos
+95076,Watsonville
+95077,Watsonville
+95101,San Jose
+95102,San Jose
+95103,San Jose
+95106,San Jose
+95108,San Jose
+95109,San Jose
+95110,San Jose
+95111,San Jose
+95112,San Jose
+95113,San Jose
+95114,San Jose
+95115,San Jose
+95116,San Jose
+95117,San Jose
+95118,San Jose
+95119,San Jose
+95120,San Jose
+95121,San Jose
+95122,San Jose
+95123,San Jose
+95124,San Jose
+95125,San Jose
+95126,San Jose
+95127,San Jose
+95128,San Jose
+95129,San Jose
+95130,San Jose
+95131,San Jose
+95132,San Jose
+95133,San Jose
+95134,San Jose
+95135,San Jose
+95136,San Jose
+95137,San Jose
+95138,San Jose
+95139,San Jose
+95140,Mount Hamilton
+95141,San Jose
+95142,San Jose
+95148,San Jose
+95150,San Jose
+95151,San Jose
+95152,San Jose
+95153,San Jose
+95154,San Jose
+95155,San Jose
+95156,San Jose
+95157,San Jose
+95158,San Jose
+95159,San Jose
+95160,San Jose
+95161,San Jose
+95164,San Jose
+95170,San Jose
+95171,San Jose
+95172,San Jose
+95173,San Jose
+95190,San Jose
+95191,San Jose
+95192,San Jose
+95193,San Jose
+95194,San Jose
+95196,San Jose
+95201,Stockton
+95202,Stockton
+95203,Stockton
+95204,Stockton
+95205,Stockton
+95206,Stockton
+95207,Stockton
+95208,Stockton
+95209,Stockton
+95210,Stockton
+95211,Stockton
+95212,Stockton
+95213,Stockton
+95215,Stockton
+95219,Stockton
+95220,Acampo
+95221,Altaville
+95222,Angels Camp
+95223,Arnold
+95224,Avery
+95225,Burson
+95226,Campo Seco
+95227,Clements
+95228,Copperopolis
+95229,Douglas Flat
+95230,Farmington
+95231,French Camp
+95232,Glencoe
+95233,Hathaway Pines
+95234,Holt
+95236,Linden
+95237,Lockeford
+95240,Lodi
+95241,Lodi
+95242,Lodi
+95245,Mokelumne Hill
+95246,Mountain Ranch
+95247,Murphys
+95248,Rail Road Flat
+95249,San Andreas
+95250,Sheep Ranch
+95251,Vallecito
+95252,Valley Springs
+95253,Victor
+95254,Wallace
+95255,West Point
+95257,Wilseyville
+95258,Woodbridge
+95267,Stockton
+95269,Stockton
+95290,Stockton
+95296,Lyoth
+95297,Stockton
+95298,Stockton
+95301,Atwater
+95303,Ballico
+95304,Banta
+95305,Big Oak Flat
+95306,Catheys Valley
+95307,Ceres
+95309,Chinese Camp
+95310,Columbia
+95311,Coulterville
+95312,Cressey
+95313,Crows Landing
+95314,Dardanelle
+95315,Delhi
+95316,Denair
+95317,El Nido
+95318,El Portal
+95319,Empire
+95320,Escalon
+95321,Groveland
+95322,Gustine
+95323,Hickman
+95324,Hilmar
+95325,Hornitos
+95326,Hughson
+95327,Jamestown
+95328,Keyes
+95329,La Grange
+95330,Lathrop
+95333,Le Grand
+95334,Livingston
+95335,Long Barn
+95336,Manteca
+95337,Manteca
+95338,Mariposa
+95340,Merced
+95341,Merced
+95342,Atwater
+95343,Merced
+95344,Merced
+95345,Midpines
+95346,Mi Wuk Village
+95347,Moccasin
+95348,Merced
+95350,Modesto
+95351,Modesto
+95352,Modesto
+95353,Modesto
+95354,Modesto
+95355,Modesto
+95356,Modesto
+95357,Modesto
+95358,Modesto
+95360,Newman
+95361,Oakdale
+95363,Patterson
+95364,Pinecrest
+95365,Planada
+95366,Ripon
+95367,Riverbank
+95368,Salida
+95369,Snelling
+95370,Sonora
+95372,Soulsbyville
+95373,Standard
+95374,Stevinson
+95375,Strawberry
+95376,Tracy
+95378,Tracy
+95379,Tuolumne
+95380,Turlock
+95381,Turlock
+95382,Turlock
+95383,Twain Harte
+95385,Vernalis
+95386,Waterford
+95387,Westley
+95388,Winton
+95389,Yosemite National Park
+95390,Riverbank
+95397,Modesto
+95401,Santa Rosa
+95402,Santa Rosa
+95403,Santa Rosa
+95404,Santa Rosa
+95405,Santa Rosa
+95406,Santa Rosa
+95407,Santa Rosa
+95408,Santa Rosa
+95409,Santa Rosa
+95410,Albion,74
+95412,Annapolis,90
+95415,Boonville,95
+95416,Boyes Hot Springs
+95417,Branscomb
+95418,Calpella
+95419,Camp Meeker
+95420,Caspar,71
+95421,Cazadero
+95422,Clearlake
+95423,Clearlake Oaks
+95424,Clearlake Park
+95425,Cloverdale
+95426,Cobb
+95427,Comptche,87
+95428,Covelo
+95429,Dos Rios
+95430,Duncans Mills
+95431,Eldridge
+95432,Elk,81
+95433,El Verano
+95435,Finley
+95436,Forestville
+95437,Fort Bragg,82
+95439,Fulton
+95441,Geyserville
+95442,Glen Ellen
+95443,Glenhaven
+95444,Graton
+95445,Gualala,86
+95446,Guerneville
+95448,Healdsburg
+95449,Hopland
+95450,Jenner,99
+95451,Kelseyville
+95452,Kenwood
+95453,Lakeport
+95454,Laytonville
+95456,Littleriver,68
+95457,Lower Lake
+95458,Lucerne
+95459,Manchester,71
+95460,Mendocino,73
+95461,Middletown
+95462,Monte Rio
+95463,Navarro,91
+95464,Nice
+95465,Occidental
+95466,Philo,84
+95468,Point Arena,78
+95469,Potter Valley
+95470,Redwood Valley
+95471,Rio Nido
+95472,Sebastopol
+95473,Sebastopol
+95476,Sonoma
+95480,Stewarts Point,81
+95481,Talmage
+95482,Ukiah
+95485,Upper Lake
+95486,Villa Grande
+95487,Vineburg
+95488,Westport,88
+95490,Willits
+95492,Windsor
+95493,Witter Springs
+95494,Yorkville
+95497,The Sea Ranch,82
+95501,Eureka
+95502,Eureka,91
+95503,Eureka,95
+95511,Alderpoint
+95514,Blocksburg
+95518,Arcata
+95519,McKinleyville
+95521,Arcata
+95524,Bayside
+95525,Blue Lake
+95526,Bridgeville
+95527,Burnt Ranch
+95528,Carlotta
+95531,Crescent City
+95532,Crescent City
+95534,Cutten,91
+95536,Ferndale,69
+95537,Fields Landing,83
+95538,Fort Dick
+95540,Fortuna,87
+95542,Garberville
+95543,Gasquet
+95545,Honeydew,72
+95546,Hoopa
+95547,Hydesville,93
+95548,Klamath
+95549,Kneeland
+95550,Korbel
+95551,Loleta,77
+95552,Mad River
+95553,Miranda,94
+95554,Myers Flat,99
+95555,Orick
+95556,Orleans
+95558,Petrolia,67
+95559,Phillipsville
+95560,Redway,84
+95562,Rio Dell,81
+95563,Salyer
+95564,Samoa,87
+95565,Scotia,93
+95567,Smith River
+95568,Somes Bar
+95569,Redcrest
+95570,Trinidad
+95571,Weott,96
+95573,Willow Creek
+95585,Leggett
+95587,Piercy
+95589,Whitethorn,77
+95595,Zenia
+95601,Amador City
+95602,Auburn
+95603,Auburn
+95604,Auburn
+95605,West Sacramento
+95606,Brooks
+95607,Capay
+95608,Carmichael
+95609,Carmichael
+95610,Citrus Heights
+95611,Citrus Heights
+95612,Clarksburg
+95613,Coloma
+95614,Cool
+95615,Courtland
+95616,Davis
+95617,Davis
+95618,El Macero
+95619,Diamond Springs
+95620,Dixon
+95621,Citrus Heights
+95623,El Dorado
+95624,Elk Grove
+95625,Elmira
+95626,Elverta
+95627,Esparto
+95628,Fair Oaks
+95629,Fiddletown
+95630,Folsom
+95631,Foresthill
+95632,Galt
+95633,Garden Valley
+95634,Georgetown
+95635,Greenwood
+95636,Grizzly Flats
+95637,Guinda
+95638,Herald
+95639,Hood
+95640,Ione
+95641,Isleton
+95642,Jackson
+95644,Kit Carson
+95645,Knights Landing
+95646,Kirkwood
+95648,Lincoln
+95650,Loomis
+95651,Lotus
+95652,McClellan AFB
+95653,Madison
+95654,Martell
+95655,Mather
+95656,Mount Aukum
+95658,Newcastle
+95659,Nicolaus
+95660,North Highlands
+95661,Roseville
+95662,Orangevale
+95663,Penryn
+95664,Pilot Hill
+95665,Pine Grove
+95666,Pioneer
+95667,Placerville
+95668,Pleasant Grove
+95669,Plymouth
+95670,Rancho Cordova
+95671,Represa
+95672,Rescue
+95673,Rio Linda
+95674,Rio Oso
+95675,River Pines
+95676,Robbins
+95677,Rocklin
+95678,Roseville
+95679,Rumsey
+95680,Ryde
+95681,Sheridan
+95682,Shingle Springs
+95683,Sloughhouse
+95684,Somerset
+95685,Sutter Creek
+95686,Thornton
+95687,Vacaville
+95688,Vacaville
+95689,Volcano
+95690,Walnut Grove
+95691,West Sacramento
+95692,Wheatland
+95693,Wilton
+95694,Winters
+95695,Woodland
+95696,Vacaville
+95697,Yolo
+95698,Zamora
+95699,Drytown
+95701,Alta
+95703,Applegate
+95709,Camino
+95712,Chicago Park
+95713,Colfax
+95714,Dutch Flat
+95715,Emigrant Gap
+95717,Gold Run
+95720,Kyburz
+95721,Echo Lake
+95722,Meadow Vista
+95724,Norden
+95726,Pollock Pines
+95728,Soda Springs
+95735,Twin Bridges
+95736,Weimar
+95741,Rancho Cordova
+95742,Rancho Cordova
+95743,Rancho Cordova
+95746,Granite Bay
+95747,Roseville
+95758,Elk Grove
+95759,Elk Grove
+95762,El Dorado Hills
+95763,Folsom
+95765,Rocklin
+95776,Woodland
+95798,West Sacramento
+95799,West Sacramento
+95812,Sacramento
+95813,Sacramento
+95814,Sacramento
+95815,Sacramento
+95816,Sacramento
+95817,Sacramento
+95818,Sacramento
+95819,Sacramento
+95820,Sacramento
+95821,Sacramento
+95822,Sacramento
+95823,Sacramento
+95824,Sacramento
+95825,Sacramento
+95826,Sacramento
+95827,Sacramento
+95828,Sacramento
+95829,Sacramento
+95830,Sacramento
+95831,Sacramento
+95832,Sacramento
+95833,Sacramento
+95834,Sacramento
+95835,Sacramento
+95836,Sacramento
+95837,Sacramento
+95838,Sacramento
+95840,Sacramento
+95841,Sacramento
+95842,Sacramento
+95843,Antelope
+95851,Sacramento
+95852,Sacramento
+95853,Sacramento
+95857,Sacramento
+95860,Sacramento
+95864,Sacramento
+95865,Sacramento
+95866,Sacramento
+95867,Sacramento
+95873,Sacramento
+95887,Sacramento
+95894,Sacramento
+95899,Sacramento
+95901,Marysville
+95903,Beale AFB
+95910,Alleghany
+95912,Arbuckle
+95913,Artois
+95914,Bangor
+95915,Belden
+95916,Berry Creek
+95917,Biggs
+95918,Browns Valley
+95919,Brownsville
+95920,Butte City
+95922,Camptonville
+95923,Canyondam
+95924,Cedar Ridge
+95925,Challenge
+95926,Chico
+95927,Chico
+95928,Chico
+95929,Chico
+95930,Clipper Mills
+95931,College City
+95932,Colusa
+95934,Crescent Mills
+95935,Dobbins
+95936,Downieville
+95937,Dunnigan
+95938,Durham
+95939,Elk Creek
+95940,Feather Falls
+95941,Forbestown
+95942,Forest Ranch
+95943,Glenn
+95944,Goodyears Bar
+95945,Grass Valley
+95946,Penn Valley
+95947,Greenville
+95948,Gridley
+95949,Grass Valley
+95950,Grimes
+95951,Hamilton City
+95953,Live Oak
+95954,Magalia
+95955,Maxwell
+95956,Meadow Valley
+95957,Meridian
+95958,Nelson
+95959,Nevada City
+95960,North San Juan
+95961,Olivehurst
+95962,Oregon House
+95963,Orland
+95965,Oroville
+95966,Oroville
+95967,Paradise
+95968,Palermo
+95969,Paradise
+95970,Princeton
+95971,Quincy
+95972,Rackerby
+95973,Chico
+95974,Richvale
+95975,Rough and Ready
+95976,Chico
+95977,Smartville
+95978,Stirling City
+95979,Stonyford
+95980,Storrie
+95981,Strawberry Valley
+95982,Sutter
+95983,Taylorsville
+95984,Twain
+95986,Washington
+95987,Williams
+95988,Willows
+95991,Yuba City
+95992,Yuba City
+95993,Yuba City
+96001,Redding
+96002,Redding
+96003,Redding
+96006,Adin
+96007,Anderson
+96008,Bella Vista
+96009,Bieber
+96010,Big Bar
+96011,Big Bend
+96013,Burney
+96014,Callahan
+96015,Canby
+96016,Cassel
+96017,Castella
+96019,Shasta Lake
+96020,Chester
+96021,Corning
+96022,Cottonwood
+96023,Dorris
+96024,Douglas City
+96025,Dunsmuir
+96027,Etna
+96028,Fall River Mills
+96029,Flournoy
+96031,Forks of Salmon
+96032,Fort Jones
+96033,French Gulch
+96034,Gazelle
+96035,Gerber
+96037,Greenview
+96038,Grenada
+96039,Happy Camp
+96040,Hat Creek
+96041,Hayfork
+96044,Hornbrook
+96046,Hyampom
+96047,Igo
+96048,Junction City
+96049,Redding
+96050,Klamath River
+96051,Lakehead
+96052,Lewiston
+96053,McArthur
+96054,Lookout
+96055,Los Molinos
+96056,McArthur
+96057,McCloud
+96058,Macdoel
+96059,Manton
+96061,Mill Creek
+96062,Millville
+96063,Mineral
+96064,Montague
+96065,Montgomery Creek
+96067,Mount Shasta
+96068,Nubieber
+96069,Oak Run
+96070,Obrien
+96071,Old Station
+96073,Palo Cedro
+96074,Paskenta
+96075,Paynes Creek
+96076,Platina
+96078,Proberta
+96079,Shasta Lake
+96080,Red Bluff
+96084,Round Mountain
+96085,Scott Bar
+96086,Seiad Valley
+96087,Shasta
+96088,Shingletown
+96089,Shasta Lake
+96090,Tehama
+96091,Trinity Center
+96092,Vina
+96093,Weaverville
+96094,Weed
+96095,Whiskeytown
+96096,Whitmore
+96097,Yreka
+96099,Redding
+96101,Alturas
+96103,Blairsden-Graeagle
+96104,Cedarville
+96105,Chilcoot
+96106,Clio
+96107,Coleville
+96108,Davis Creek
+96109,Doyle
+96110,Eagleville
+96111,Floriston
+96112,Fort Bidwell
+96113,Herlong
+96114,Janesville
+96115,Lake City
+96116,Likely
+96117,Litchfield
+96118,Loyalton
+96119,Madeline
+96120,Markleeville
+96121,Milford
+96122,Portola
+96123,Ravendale
+96124,Calpine
+96125,Sierra City
+96126,Sierraville
+96127,Susanville
+96128,Standish
+96129,Beckwourth
+96130,Susanville
+96132,Termo
+96133,Topaz
+96134,Tulelake
+96135,Vinton
+96136,Wendel
+96137,Westwood
+96140,Carnelian Bay
+96141,Homewood
+96142,Tahoma
+96143,Kings Beach
+96145,Tahoe City
+96146,Olympic Valley
+96148,Tahoe Vista
+96150,South Lake Tahoe
+96151,South Lake Tahoe
+96152,South Lake Tahoe
+96154,South Lake Tahoe
+96155,South Lake Tahoe
+96156,South Lake Tahoe
+96157,South Lake Tahoe
+96158,South Lake Tahoe
+96160,Truckee
+96161,Truckee
+96162,Truckee
+85001,Phoenix
+85002,Phoenix
+85003,Phoenix
+85004,Phoenix
+85005,Phoenix
+85006,Phoenix
+85007,Phoenix
+85008,Phoenix
+85009,Phoenix
+85010,Phoenix
+85011,Phoenix
+85012,Phoenix
+85013,Phoenix
+85014,Phoenix
+85015,Phoenix
+85016,Phoenix
+85017,Phoenix
+85018,Phoenix
+85019,Phoenix
+85020,Phoenix
+85021,Phoenix
+85022,Phoenix
+85023,Phoenix
+85024,Phoenix
+85025,Phoenix
+85026,Phoenix
+85027,Phoenix
+85028,Phoenix
+85029,Phoenix
+85030,Phoenix
+85031,Phoenix
+85032,Phoenix
+85033,Phoenix
+85034,Phoenix
+85035,Phoenix
+85036,Phoenix
+85037,Phoenix
+85038,Phoenix
+85039,Phoenix
+85040,Phoenix
+85041,Phoenix
+85043,Phoenix
+85044,Phoenix
+85045,Phoenix
+85046,Phoenix
+85048,Phoenix
+85050,Phoenix
+85051,Phoenix
+85053,Phoenix
+85054,Phoenix
+85055,Phoenix
+85060,Phoenix
+85061,Phoenix
+85062,Phoenix
+85063,Phoenix
+85064,Phoenix
+85065,Phoenix
+85066,Phoenix
+85067,Phoenix
+85068,Phoenix
+85069,Phoenix
+85070,Phoenix
+85071,Phoenix
+85072,Phoenix
+85073,Phoenix
+85074,Phoenix
+85075,Phoenix
+85076,Phoenix
+85077,Phoenix
+85078,Phoenix
+85079,Phoenix
+85080,Phoenix
+85082,Phoenix
+85085,Phoenix
+85086,Anthem
+85087,New River
+85097,Phoenix
+85098,Phoenix
+85099,Phoenix
+85201,Mesa
+85202,Mesa
+85203,Mesa
+85204,Mesa
+85205,Mesa
+85206,Mesa
+85207,Mesa
+85208,Mesa
+85210,Mesa
+85211,Mesa
+85212,Mesa
+85213,Mesa
+85214,Mesa
+85215,Mesa
+85216,Mesa
+85217,Apache Junction
+85219,Apache Junction
+85220,Apache Junction
+85221,Bapchule
+85222,Casa Grande
+85223,Arizona City
+85224,Chandler
+85225,Chandler
+85226,Chandler
+85227,Chandler Heights
+85228,Coolidge
+85230,Casa Grande
+85231,Eloy
+85232,Florence
+85233,Gilbert
+85234,Gilbert
+85235,Hayden
+85236,Higley
+85237,Kearny
+85239,Maricopa
+85241,Picacho
+85242,Queen Creek
+85244,Chandler
+85245,Red Rock
+85246,Chandler
+85247,Sacaton
+85248,Chandler
+85249,Chandler
+85250,Scottsdale
+85251,Scottsdale
+85252,Scottsdale
+85253,Paradise Valley
+85254,Scottsdale
+85255,Scottsdale
+85256,Scottsdale
+85257,Scottsdale
+85258,Scottsdale
+85259,Scottsdale
+85260,Scottsdale
+85261,Scottsdale
+85262,Scottsdale
+85263,Rio Verde
+85264,Fort McDowell
+85266,Scottsdale
+85267,Scottsdale
+85268,Fountain Hills
+85269,Fountain Hills
+85271,Scottsdale
+85272,Stanfield
+85273,Superior
+85274,Mesa
+85275,Mesa
+85277,Mesa
+85278,Apache Junction
+85279,Florence
+85280,Tempe
+85281,Tempe
+85282,Tempe
+85283,Tempe
+85284,Tempe
+85285,Tempe
+85287,Tempe
+85289,Tempe
+85290,Tortilla Flat
+85291,Valley Farms
+85292,Winkelman
+85296,Gilbert
+85299,Gilbert
+85301,Glendale
+85302,Glendale
+85303,Glendale
+85304,Glendale
+85305,Glendale
+85306,Glendale
+85307,Glendale
+85308,Glendale
+85309,Luke AFB
+85310,Glendale
+85311,Glendale
+85312,Glendale
+85313,Glendale
+85318,Glendale
+85320,Aguila
+85321,Ajo
+85322,Arlington
+85323,Avondale
+85324,Black Canyon City
+85325,Bouse
+85326,Buckeye
+85327,Cave Creek
+85328,Cibola
+85329,Cashion
+85331,Cave Creek
+85332,Congress
+85333,Dateland
+85334,Ehrenberg
+85335,El Mirage
+85336,Gadsden
+85337,Gila Bend
+85338,Goodyear
+85339,Laveen
+85340,Litchfield Park
+85341,Lukeville
+85342,Morristown
+85343,Palo Verde
+85344,Parker
+85345,Peoria
+85346,Quartzsite
+85347,Roll
+85348,Salome
+85349,San Luis
+85350,Somerton
+85351,Sun City
+85352,Tacna
+85353,Tolleson
+85354,Tonopah
+85355,Waddell
+85356,Wellton
+85357,Wenden
+85358,Wickenburg
+85359,Quartzsite
+85360,Wikieup
+85361,Wittmann
+85362,Yarnell
+85363,Youngtown
+85364,Yuma
+85365,Yuma
+85366,Yuma
+85367,Yuma
+85369,Yuma
+85371,Poston
+85372,Sun City
+85373,Sun City
+85374,Surprise
+85375,Sun City West
+85376,Sun City West
+85377,Carefree
+85378,Surprise
+85379,Surprise
+85380,Peoria
+85381,Peoria
+85382,Peoria
+85385,Peoria
+85387,Surprise
+85390,Wickenburg
+85501,Globe
+85502,Globe
+85530,Bylas
+85531,Central
+85532,Claypool
+85533,Clifton
+85534,Duncan
+85535,Eden
+85536,Fort Thomas
+85539,Miami
+85540,Morenci
+85541,Payson
+85542,Peridot
+85543,Pima
+85544,Pine
+85545,Roosevelt
+85546,Safford
+85547,Payson
+85548,Safford
+85550,San Carlos
+85551,Solomon
+85552,Thatcher
+85553,Tonto Basin
+85554,Young
+85601,Arivaca
+85602,Benson
+85603,Bisbee
+85605,Bowie
+85606,Cochise
+85607,Douglas
+85608,Douglas
+85609,Dragoon
+85610,Elfrida
+85611,Elgin
+85613,Fort Huachuca
+85614,Green Valley
+85615,Hereford
+85616,Huachuca City
+85617,Mc Neal
+85618,Mammoth
+85619,Mount Lemmon
+85620,Naco
+85621,Nogales
+85622,Green Valley
+85623,Oracle
+85624,Patagonia
+85625,Pearce
+85626,Pirtleville
+85627,Pomerene
+85628,Nogales
+85629,Sahuarita
+85630,Saint David
+85631,San Manuel
+85632,San Simon
+85633,Sasabe
+85634,Sells
+85635,Sierra Vista
+85636,Sierra Vista
+85637,Sonoita
+85638,Tombstone
+85639,Topawa
+85640,Tumacacori
+85641,Vail
+85643,Willcox
+85644,Willcox
+85645,Amado
+85646,Tubac
+85648,Rio Rico
+85650,Sierra Vista
+85652,Cortaro
+85653,Marana
+85654,Rillito
+85655,Douglas
+85662,Nogales
+85670,Fort Huachuca
+85671,Sierra Vista
+85701,Tucson
+85702,Tucson
+85703,Tucson
+85704,Tucson
+85705,Tucson
+85706,Tucson
+85707,Tucson
+85708,Tucson
+85709,Tucson
+85710,Tucson
+85711,Tucson
+85712,Tucson
+85713,Tucson
+85714,Tucson
+85715,Tucson
+85716,Tucson
+85717,Tucson
+85718,Tucson
+85719,Tucson
+85720,Tucson
+85721,Tucson
+85722,Tucson
+85723,Tucson
+85724,Tucson
+85725,Tucson
+85726,Tucson
+85728,Tucson
+85730,Tucson
+85731,Tucson
+85732,Tucson
+85733,Tucson
+85734,Tucson
+85735,Tucson
+85736,Tucson
+85737,Tucson
+85738,Catalina
+85739,Tucson
+85740,Tucson
+85741,Tucson
+85742,Tucson
+85743,Tucson
+85744,Tucson
+85745,Tucson
+85746,Tucson
+85747,Tucson
+85748,Tucson
+85749,Tucson
+85750,Tucson
+85751,Tucson
+85752,Tucson
+85754,Tucson
+85775,Tucson
+85777,Tucson
+85901,Show Low
+85902,Show Low
+85911,Cibecue
+85912,White Mountain Lake
+85920,Alpine
+85922,Blue
+85923,Clay Springs
+85924,Concho
+85925,Eagar
+85926,Fort Apache
+85927,Greer
+85928,Heber
+85929,Lakeside
+85930,McNary
+85931,Forest Lakes
+85932,Nutrioso
+85933,Overgaard
+85934,Pinedale
+85935,Pinetop
+85936,Saint Johns
+85937,Snowflake
+85938,Springerville
+85939,Taylor
+85940,Vernon
+85941,Whiteriver
+85942,Woodruff
+86001,Flagstaff
+86002,Flagstaff
+86003,Flagstaff
+86004,Flagstaff
+86011,Flagstaff
+86015,Bellemont
+86016,Gray Mountain
+86017,Munds Park
+86018,Parks
+86020,Cameron
+86021,Colorado City
+86022,Fredonia
+86023,Grand Canyon
+86024,Happy Jack
+86025,Holbrook
+86028,Petrified Forest Natl Pk
+86029,Sun Valley
+86030,Hotevilla
+86031,Indian Wells
+86032,Joseph City
+86033,Kayenta
+86034,Keams Canyon
+86035,Leupp
+86036,Marble Canyon
+86038,Mormon Lake
+86039,Kykotsmovi Village
+86040,Page
+86042,Polacca
+86043,Second Mesa
+86044,Tonalea
+86045,Tuba City
+86046,Williams
+86047,Winslow
+86052,North Rim
+86053,Kaibito
+86054,Shonto
+86301,Prescott
+86302,Prescott
+86303,Prescott
+86304,Prescott
+86305,Prescott
+86312,Prescott Valley
+86313,Prescott
+86314,Prescott Valley
+86320,Ash Fork
+86321,Bagdad
+86322,Camp Verde
+86323,Chino Valley
+86324,Clarkdale
+86325,Cornville
+86326,Cottonwood
+86327,Dewey
+86329,Humboldt
+86330,Iron Springs
+86331,Jerome
+86332,Kirkland
+86333,Mayer
+86334,Paulden
+86335,Rimrock
+86336,Sedona
+86337,Seligman
+86338,Skull Valley
+86339,Sedona
+86340,Sedona
+86341,Sedona
+86342,Lake Montezuma
+86343,Crown King
+86351,Sedona
+86401,Kingman
+86402,Kingman
+86403,Lake Havasu City
+86404,Lake Havasu City
+86405,Lake Havasu City
+86406,Lake Havasu City
+86411,Hackberry
+86412,Hualapai
+86413,Golden Valley
+86426,Fort Mohave
+86427,Fort Mohave
+86429,Bullhead City
+86430,Bullhead City
+86431,Chloride
+86432,Littlefield
+86433,Oatman
+86434,Peach Springs
+86435,Supai
+86436,Topock
+86437,Valentine
+86438,Yucca
+86439,Bullhead City
+86440,Mohave Valley
+86441,Dolan Springs
+86442,Bullhead City
+86443,Temple Bar Marina
+86444,Meadview
+86445,Willow Beach
+86446,Mohave Valley
+86502,Chambers
+86503,Chinle
+86504,Fort Defiance
+86505,Ganado
+86506,Houck
+86507,Lukachukai
+86508,Lupton
+86510,Pinon
+86511,Saint Michaels
+86512,Sanders
+86514,Teec Nos Pos
+86515,Window Rock
+86520,Blue Gap
+86535,Dennehotso
+86538,Many Farms
+86540,Nazlini
+86544,Red Valley
+86545,Rock Point
+86547,Round Rock
+86549,Sawmill
+86556,Tsaile
+96799,Pago Pago
+71601,Pine Bluff
+71602,White Hall
+71603,Pine Bluff
+71611,Pine Bluff
+71612,White Hall
+71613,Pine Bluff
+71630,Arkansas City
+71631,Banks
+71635,Crossett
+71638,Dermott
+71639,Dumas
+71640,Eudora
+71642,Fountain Hill
+71643,Gould
+71644,Grady
+71646,Hamburg
+71647,Hermitage
+71649,Jennie
+71650,Jerome
+71651,Jersey
+71652,Kingsland
+71653,Lake Village
+71654,Mc Gehee
+71655,Monticello
+71656,Monticello
+71657,Monticello
+71658,Montrose
+71659,Moscow
+71660,New Edinburg
+71661,Parkdale
+71662,Pickens
+71663,Portland
+71665,Rison
+71666,Mc Gehee
+71667,Star City
+71670,Tillar
+71671,Warren
+71674,Watson
+71675,Wilmar
+71676,Wilmot
+71677,Winchester
+71678,Yorktown
+71701,Camden
+71711,Camden
+71720,Bearden
+71721,Beirne
+71722,Bluff City
+71724,Calion
+71725,Carthage
+71726,Chidester
+71728,Curtis
+71730,El Dorado
+71731,El Dorado
+71740,Emerson
+71742,Fordyce
+71743,Gurdon
+71744,Hampton
+71745,Harrell
+71747,Huttig
+71748,Ivan
+71749,Junction City
+71750,Lawson
+71751,Louann
+71752,Mc Neil
+71753,Magnolia
+71754,Magnolia
+71758,Mount Holly
+71759,Norphlet
+71762,Smackover
+71763,Sparkman
+71764,Stephens
+71765,Strong
+71766,Thornton
+71767,Hampton
+71768,Urbana
+71769,Village
+71770,Waldo
+71772,Whelen Springs
+71801,Hope
+71802,Hope
+71820,Alleene
+71822,Ashdown
+71823,Ben Lomond
+71825,Blevins
+71826,Bradley
+71827,Buckner
+71828,Cale
+71831,Columbus
+71832,De Queen
+71833,Dierks
+71834,Doddridge
+71835,Emmet
+71836,Foreman
+71837,Fouke
+71838,Fulton
+71839,Garland City
+71840,Genoa
+71841,Gillham
+71842,Horatio
+71844,Laneburg
+71845,Lewisville
+71846,Lockesburg
+71847,Mc Caskill
+71851,Mineral Springs
+71852,Nashville
+71853,Ogden
+71854,Texarkana
+71855,Ozan
+71857,Prescott
+71858,Rosston
+71859,Saratoga
+71860,Stamps
+71861,Taylor
+71862,Washington
+71864,Willisville
+71865,Wilton
+71866,Winthrop
+71901,Hot Springs National Park
+71902,Hot Springs National Park
+71903,Hot Springs National Park
+71909,Hot Springs Village
+71910,Hot Springs Village
+71913,Hot Springs National Park
+71914,Hot Springs National Park
+71920,Alpine
+71921,Amity
+71922,Antoine
+71923,Arkadelphia
+71929,Bismarck
+71932,Board Camp
+71933,Bonnerdale
+71935,Caddo Gap
+71937,Cove
+71940,Delight
+71941,Donaldson
+71942,Friendship
+71943,Glenwood
+71944,Grannis
+71945,Hatfield
+71946,Hatton
+71949,Jessieville
+71950,Kirby
+71951,Hot Springs National Park
+71952,Langley
+71953,Mena
+71956,Mountain Pine
+71957,Mount Ida
+71958,Murfreesboro
+71959,Newhope
+71960,Norman
+71961,Oden
+71962,Okolona
+71964,Pearcy
+71965,Pencil Bluff
+71966,Oden
+71968,Royal
+71969,Sims
+71970,Story
+71971,Umpire
+71972,Vandervoort
+71973,Wickes
+71998,Arkadelphia
+71999,Arkadelphia
+72001,Adona
+72002,Alexander
+72003,Almyra
+72004,Altheimer
+72005,Amagon
+72006,Augusta
+72007,Austin
+72010,Bald Knob
+72011,Bauxite
+72012,Beebe
+72013,Bee Branch
+72014,Beedeville
+72015,Benton
+72016,Bigelow
+72017,Biscoe
+72018,Benton
+72020,Bradford
+72021,Brinkley
+72022,Bryant
+72023,Cabot
+72024,Carlisle
+72025,Casa
+72026,Casscoe
+72027,Center Ridge
+72028,Choctaw
+72029,Clarendon
+72030,Cleveland
+72031,Clinton
+72032,Conway
+72033,Conway
+72035,Conway
+72036,Cotton Plant
+72037,Coy
+72038,Crocketts Bluff
+72039,Damascus
+72040,Des Arc
+72041,De Valls Bluff
+72042,De Witt
+72043,Diaz
+72044,Edgemont
+72045,El Paso
+72046,England
+72047,Enola
+72048,Ethel
+72051,Fox
+72052,Garner
+72053,College Station
+72055,Gillett
+72057,Grapevine
+72058,Greenbrier
+72059,Gregory
+72060,Griffithville
+72061,Guy
+72063,Hattieville
+72064,Hazen
+72065,Hensley
+72066,Hickory Plains
+72067,Higden
+72068,Higginson
+72069,Holly Grove
+72070,Houston
+72071,Howell
+72072,Humnoke
+72073,Humphrey
+72074,Hunter
+72075,Jacksonport
+72076,Jacksonville
+72078,Jacksonville
+72079,Jefferson
+72080,Jerusalem
+72081,Judsonia
+72082,Kensett
+72083,Keo
+72084,Leola
+72085,Letona
+72086,Lonoke
+72087,Lonsdale
+72088,Fairfield Bay
+72089,Bryant
+72099,Little Rock Air Force Base
+72101,Mc Crory
+72102,Mc Rae
+72103,Mabelvale
+72104,Malvern
+72105,Jones Mill
+72106,Mayflower
+72107,Menifee
+72108,Monroe
+72110,Morrilton
+72111,Mount Vernon
+72112,Newport
+72113,Maumelle
+72114,North Little Rock
+72115,North Little Rock
+72116,North Little Rock
+72117,North Little Rock
+72118,North Little Rock
+72119,North Little Rock
+72120,Sherwood
+72121,Pangburn
+72122,Paron
+72123,Patterson
+72124,North Little Rock
+72125,Perry
+72126,Perryville
+72127,Plumerville
+72128,Poyen
+72129,Prattsville
+72130,Prim
+72131,Quitman
+72132,Redfield
+72133,Reydell
+72134,Roe
+72135,Roland
+72136,Romance
+72137,Rose Bud
+72139,Russell
+72140,Saint Charles
+72141,Scotland
+72142,Scott
+72143,Searcy
+72145,Searcy
+72149,Searcy
+72150,Sheridan
+72152,Sherrill
+72153,Shirley
+72156,Solgohachia
+72157,Springfield
+72158,Benton
+72160,Stuttgart
+72164,Sweet Home
+72165,Thida
+72166,Tichnor
+72167,Traskwood
+72168,Tucker
+72169,Tupelo
+72170,Ulm
+72173,Vilonia
+72175,Wabbaseka
+72176,Ward
+72178,West Point
+72179,Wilburn
+72180,Woodson
+72181,Wooster
+72182,Wright
+72183,Wrightsville
+72189,Mc Crory
+72190,North Little Rock
+72199,North Little Rock
+72201,Little Rock
+72202,Little Rock
+72203,Little Rock
+72204,Little Rock
+72205,Little Rock
+72206,Little Rock
+72207,Little Rock
+72209,Little Rock
+72210,Little Rock
+72211,Little Rock
+72212,Little Rock
+72214,Little Rock
+72215,Little Rock
+72216,Little Rock
+72217,Little Rock
+72219,Little Rock
+72221,Little Rock
+72222,Little Rock
+72223,Little Rock
+72225,Little Rock
+72227,Little Rock
+72231,Little Rock
+72295,Little Rock
+72301,West Memphis
+72303,West Memphis
+72310,Armorel
+72311,Aubrey
+72312,Barton
+72313,Bassett
+72314,Birdeye
+72315,Blytheville
+72316,Blytheville
+72319,Gosnell
+72320,Brickeys
+72321,Burdette
+72322,Caldwell
+72324,Cherry Valley
+72325,Clarkedale
+72326,Colt
+72327,Crawfordsville
+72328,Crumrod
+72329,Driver
+72330,Dyess
+72331,Earle
+72332,Edmondson
+72333,Elaine
+72335,Forrest City
+72336,Forrest City
+72338,Frenchmans Bayou
+72339,Gilmore
+72340,Goodwin
+72341,Haynes
+72342,Helena
+72346,Heth
+72347,Hickory Ridge
+72348,Hughes
+72350,Joiner
+72351,Keiser
+72352,La Grange
+72353,Lambrook
+72354,Lepanto
+72355,Lexa
+72358,Luxora
+72359,Madison
+72360,Marianna
+72364,Marion
+72365,Marked Tree
+72366,Marvell
+72367,Mellwood
+72368,Moro
+72369,Oneida
+72370,Osceola
+72372,Palestine
+72373,Parkin
+72374,Poplar Grove
+72376,Proctor
+72377,Rivervale
+72379,Snow Lake
+72381,Tomato
+72383,Turner
+72384,Turrell
+72385,Twist
+72386,Tyronza
+72387,Vanndale
+72389,Wabash
+72390,West Helena
+72391,West Ridge
+72392,Wheatley
+72394,Widener
+72395,Wilson
+72396,Wynne
+72397,Mc Crory
+72401,Jonesboro
+72402,Jonesboro
+72403,Jonesboro
+72404,Jonesboro
+72410,Alicia
+72411,Bay
+72412,Beech Grove
+72413,Biggers
+72414,Black Oak
+72415,Black Rock
+72416,Bono
+72417,Brookland
+72419,Caraway
+72421,Cash
+72422,Corning
+72424,Datto
+72425,Delaplaine
+72426,Dell
+72427,Egypt
+72428,Etowah
+72429,Fisher
+72430,Greenway
+72431,Grubbs
+72432,Harrisburg
+72433,Hoxie
+72434,Imboden
+72435,Knobel
+72436,Lafe
+72437,Lake City
+72438,Leachville
+72439,Light
+72440,Lynn
+72441,Mc Dougal
+72442,Manila
+72443,Marmaduke
+72444,Maynard
+72445,Minturn
+72447,Monette
+72449,O Kean
+72450,Paragould
+72451,Paragould
+72453,Peach Orchard
+72454,Piggott
+72455,Pocahontas
+72456,Pollard
+72457,Portia
+72458,Powhatan
+72459,Ravenden
+72460,Ravenden Springs
+72461,Rector
+72462,Reyno
+72464,Saint Francis
+72465,Sedgwick
+72466,Smithville
+72467,State University
+72469,Strawberry
+72470,Success
+72471,Swifton
+72472,Trumann
+72473,Tuckerman
+72474,Walcott
+72475,Waldenburg
+72476,Walnut Ridge
+72478,Warm Springs
+72479,Weiner
+72482,Williford
+72501,Batesville
+72503,Batesville
+72512,Horseshoe Bend
+72513,Ash Flat
+72515,Bexar
+72516,Boswell
+72517,Brockwell
+72519,Calico Rock
+72520,Camp
+72521,Cave City
+72522,Charlotte
+72523,Concord
+72524,Cord
+72525,Cherokee Village
+72526,Cushman
+72527,Desha
+72528,Dolph
+72529,Cherokee Village
+72530,Drasco
+72531,Elizabeth
+72532,Evening Shade
+72533,Fifty Six
+72534,Floral
+72536,Franklin
+72537,Gamaliel
+72538,Gepp
+72539,Glencoe
+72540,Guion
+72542,Hardy
+72543,Heber Springs
+72544,Henderson
+72545,Heber Springs
+72546,Ida
+72550,Locust Grove
+72553,Magness
+72554,Mammoth Spring
+72555,Marcella
+72556,Melbourne
+72557,Moko
+72560,Mountain View
+72561,Mount Pleasant
+72562,Newark
+72564,Oil Trough
+72565,Oxford
+72566,Pineville
+72567,Pleasant Grove
+72568,Pleasant Plains
+72569,Poughkeepsie
+72571,Rosie
+72572,Saffell
+72573,Sage
+72575,Salado
+72576,Salem
+72577,Sidney
+72578,Sturkie
+72579,Sulphur Rock
+72581,Tumbling Shoals
+72583,Viola
+72584,Violet Hill
+72585,Wideman
+72587,Wiseman
+72601,Harrison
+72602,Harrison
+72610,Alco
+72611,Alpena
+72613,Beaver
+72615,Bergman
+72616,Berryville
+72617,Big Flat
+72619,Bull Shoals
+72623,Clarkridge
+72624,Compton
+72626,Cotter
+72628,Deer
+72629,Dennard
+72630,Diamond City
+72631,Eureka Springs
+72632,Eureka Springs
+72633,Everton
+72634,Flippin
+72635,Gassville
+72636,Gilbert
+72638,Green Forest
+72639,Harriet
+72640,Hasty
+72641,Jasper
+72642,Lakeview
+72644,Lead Hill
+72645,Leslie
+72648,Marble Falls
+72650,Marshall
+72651,Midway
+72653,Mountain Home
+72654,Mountain Home
+72655,Mount Judea
+72657,Timbo
+72658,Norfork
+72659,Norfork
+72660,Oak Grove
+72661,Oakland
+72662,Omaha
+72663,Onia
+72666,Parthenon
+72668,Peel
+72669,Pindall
+72670,Ponca
+72672,Pyatt
+72675,Saint Joe
+72677,Summit
+72679,Tilly
+72680,Timbo
+72682,Valley Springs
+72683,Vendor
+72685,Western Grove
+72686,Witts Springs
+72687,Yellville
+72701,Fayetteville
+72702,Fayetteville
+72703,Fayetteville
+72704,Fayetteville
+72711,Avoca
+72712,Bentonville
+72714,Bella Vista
+72715,Bella Vista
+72716,Bentonville
+72717,Canehill
+72718,Cave Springs
+72719,Centerton
+72721,Combs
+72722,Decatur
+72727,Elkins
+72728,Elm Springs
+72729,Evansville
+72730,Farmington
+72732,Garfield
+72733,Gateway
+72734,Gentry
+72735,Goshen
+72736,Gravette
+72737,Greenland
+72738,Hindsville
+72739,Hiwasse
+72740,Huntsville
+72741,Johnson
+72742,Kingston
+72744,Lincoln
+72745,Lowell
+72747,Maysville
+72749,Morrow
+72751,Pea Ridge
+72752,Pettigrew
+72753,Prairie Grove
+72756,Rogers
+72757,Rogers
+72758,Rogers
+72760,Saint Paul
+72761,Siloam Springs
+72762,Springdale
+72764,Springdale
+72765,Springdale
+72766,Springdale
+72768,Sulphur Springs
+72769,Summers
+72770,Tontitown
+72773,Wesley
+72774,West Fork
+72776,Witter
+72801,Russellville
+72802,Russellville
+72811,Russellville
+72812,Russellville
+72820,Alix
+72821,Altus
+72823,Atkins
+72824,Belleville
+72826,Blue Mountain
+72827,Bluffton
+72828,Briggsville
+72829,Centerville
+72830,Clarksville
+72832,Coal Hill
+72833,Danville
+72834,Dardanelle
+72835,Delaware
+72837,Dover
+72838,Gravelly
+72839,Hagarville
+72840,Hartman
+72841,Harvey
+72842,Havana
+72843,Hector
+72845,Knoxville
+72846,Lamar
+72847,London
+72851,New Blaine
+72852,Oark
+72853,Ola
+72854,Ozone
+72855,Paris
+72856,Pelsor
+72857,Plainview
+72858,Pottsville
+72860,Rover
+72863,Scranton
+72865,Subiaco
+72901,Fort Smith
+72902,Fort Smith
+72903,Fort Smith
+72904,Fort Smith
+72905,Fort Smith
+72906,Fort Smith
+72908,Fort Smith
+72913,Fort Smith
+72914,Fort Smith
+72916,Fort Smith
+72917,Fort Smith
+72918,Fort Smith
+72919,Fort Smith
+72921,Alma
+72923,Barling
+72924,Bates
+72926,Boles
+72927,Booneville
+72928,Branch
+72930,Cecil
+72932,Cedarville
+72933,Charleston
+72934,Chester
+72935,Dyer
+72936,Greenwood
+72937,Hackett
+72938,Hartford
+72940,Huntington
+72941,Lavaca
+72943,Magazine
+72944,Mansfield
+72945,Midland
+72946,Mountainburg
+72947,Mulberry
+72948,Natural Dam
+72949,Ozark
+72950,Parks
+72951,Ratcliff
+72952,Rudy
+72955,Uniontown
+72956,Van Buren
+72957,Van Buren
+72958,Waldron
+72959,Winslow
+35004,Moody
+35005,Adamsville
+35006,Adger
+35007,Alabaster
+35010,Alexander City
+35011,Alexander City
+35013,Allgood
+35014,Alpine
+35015,Alton
+35016,Arab
+35019,Baileyton
+35020,Bessemer
+35021,Bessemer
+35022,Bessemer
+35023,Bessemer
+35031,Blountsville
+35032,Bon Air
+35033,Bremen
+35034,Brent
+35035,Brierfield
+35036,Brookside
+35038,Burnwell
+35040,Calera
+35041,Cardiff
+35042,Centreville
+35043,Chelsea
+35044,Childersburg
+35045,Clanton
+35046,Clanton
+35048,Clay
+35049,Cleveland
+35051,Columbiana
+35052,Cook Springs
+35053,Crane Hill
+35054,Cropwell
+35055,Cullman
+35056,Cullman
+35057,Cullman
+35058,Cullman
+35060,Docena
+35061,Dolomite
+35062,Dora
+35063,Empire
+35064,Fairfield
+35068,Fultondale
+35070,Garden City
+35071,Gardendale
+35072,Goodwater
+35073,Graysville
+35074,Green Pond
+35077,Hanceville
+35078,Harpersville
+35079,Hayden
+35080,Helena
+35082,Hollins
+35083,Holly Pond
+35085,Jemison
+35087,Joppa
+35089,Kellyton
+35091,Kimberly
+35094,Leeds
+35096,Lincoln
+35097,Locust Fork
+35098,Logan
+35111,Mc Calla
+35112,Margaret
+35114,Maylene
+35115,Montevallo
+35116,Morris
+35117,Mount Olive
+35118,Mulga
+35119,New Castle
+35120,Odenville
+35121,Oneonta
+35123,Palmerdale
+35124,Pelham
+35125,Pell City
+35126,Pinson
+35127,Pleasant Grove
+35128,Pell City
+35130,Quinton
+35131,Ragland
+35133,Remlap
+35135,Riverside
+35136,Rockford
+35137,Saginaw
+35139,Sayre
+35142,Shannon
+35143,Shelby
+35144,Siluria
+35146,Springville
+35147,Sterrett
+35148,Sumiton
+35149,Sycamore
+35150,Sylacauga
+35151,Sylacauga
+35160,Talladega
+35161,Talladega
+35171,Thorsby
+35172,Trafford
+35173,Trussville
+35175,Union Grove
+35176,Vandiver
+35178,Vincent
+35179,Vinemont
+35180,Warrior
+35181,Watson
+35182,Wattsville
+35183,Weogufka
+35184,West Blocton
+35185,Westover
+35186,Wilsonville
+35187,Wilton
+35188,Woodstock
+35201,Birmingham
+35202,Birmingham
+35203,Birmingham
+35204,Birmingham
+35205,Birmingham
+35206,Birmingham
+35207,Birmingham
+35208,Birmingham
+35209,Birmingham
+35210,Birmingham
+35211,Birmingham
+35212,Birmingham
+35213,Birmingham
+35214,Birmingham
+35215,Birmingham
+35216,Birmingham
+35217,Birmingham
+35218,Birmingham
+35219,Birmingham
+35220,Birmingham
+35221,Birmingham
+35222,Birmingham
+35223,Birmingham
+35224,Birmingham
+35225,Birmingham
+35226,Birmingham
+35228,Birmingham
+35229,Birmingham
+35230,Birmingham
+35231,Birmingham
+35232,Birmingham
+35233,Birmingham
+35234,Birmingham
+35235,Birmingham
+35236,Birmingham
+35237,Birmingham
+35238,Birmingham
+35240,Birmingham
+35242,Birmingham
+35243,Birmingham
+35244,Birmingham
+35245,Birmingham
+35246,Birmingham
+35249,Birmingham
+35253,Birmingham
+35254,Birmingham
+35255,Birmingham
+35259,Birmingham
+35260,Birmingham
+35261,Birmingham
+35263,Birmingham
+35266,Birmingham
+35277,Birmingham
+35278,Birmingham
+35279,Birmingham
+35280,Birmingham
+35281,Birmingham
+35282,Birmingham
+35283,Birmingham
+35285,Birmingham
+35286,Birmingham
+35287,Birmingham
+35288,Birmingham
+35289,Birmingham
+35290,Birmingham
+35291,Birmingham
+35292,Birmingham
+35293,Birmingham
+35294,Birmingham
+35295,Birmingham
+35296,Birmingham
+35297,Birmingham
+35298,Birmingham
+35299,Birmingham
+35401,Tuscaloosa
+35402,Tuscaloosa
+35403,Tuscaloosa
+35404,Tuscaloosa
+35405,Tuscaloosa
+35406,Tuscaloosa
+35407,Tuscaloosa
+35440,Abernant
+35441,Akron
+35442,Aliceville
+35443,Boligee
+35444,Brookwood
+35446,Buhl
+35447,Carrollton
+35448,Clinton
+35449,Coaling
+35452,Coker
+35453,Cottondale
+35456,Duncanville
+35457,Echola
+35458,Elrod
+35459,Emelle
+35460,Epes
+35461,Ethelsville
+35462,Eutaw
+35463,Fosters
+35464,Gainesville
+35466,Gordo
+35468,Kellerman
+35469,Knoxville
+35470,Livingston
+35471,Mc Shan
+35473,Northport
+35474,Moundville
+35475,Northport
+35476,Northport
+35477,Panola
+35478,Peterson
+35480,Ralph
+35481,Reform
+35482,Samantha
+35485,Tuscaloosa
+35486,Tuscaloosa
+35487,Tuscaloosa
+35490,Vance
+35491,West Greene
+35501,Jasper
+35502,Jasper
+35503,Jasper
+35504,Jasper
+35540,Addison
+35541,Arley
+35542,Bankston
+35543,Bear Creek
+35544,Beaverton
+35545,Belk
+35546,Berry
+35548,Brilliant
+35549,Carbon Hill
+35550,Cordova
+35551,Delmar
+35552,Detroit
+35553,Double Springs
+35554,Eldridge
+35555,Fayette
+35559,Glen Allen
+35560,Goodsprings
+35563,Guin
+35564,Hackleburg
+35565,Haleyville
+35570,Hamilton
+35571,Hodges
+35572,Houston
+35573,Kansas
+35574,Kennedy
+35575,Lynn
+35576,Millport
+35577,Natural Bridge
+35578,Nauvoo
+35579,Oakman
+35580,Parrish
+35581,Phil Campbell
+35582,Red Bay
+35584,Sipsey
+35585,Spruce Pine
+35586,Sulligent
+35587,Townley
+35592,Vernon
+35593,Vina
+35594,Winfield
+35601,Decatur
+35602,Decatur
+35603,Decatur
+35609,Decatur
+35610,Anderson
+35611,Athens
+35612,Athens
+35613,Athens
+35614,Athens
+35615,Belle Mina
+35616,Cherokee
+35617,Cloverdale
+35618,Courtland
+35619,Danville
+35620,Elkmont
+35621,Eva
+35622,Falkville
+35630,Florence
+35631,Florence
+35632,Florence
+35633,Florence
+35634,Florence
+35640,Hartselle
+35643,Hillsboro
+35645,Killen
+35646,Leighton
+35647,Lester
+35648,Lexington
+35649,Mooresville
+35650,Moulton
+35651,Mount Hope
+35652,Rogersville
+35653,Russellville
+35654,Russellville
+35660,Sheffield
+35661,Muscle Shoals
+35662,Muscle Shoals
+35670,Somerville
+35671,Tanner
+35672,Town Creek
+35673,Trinity
+35674,Tuscumbia
+35677,Waterloo
+35699,Decatur
+35739,Ardmore
+35740,Bridgeport
+35741,Brownsboro
+35742,Capshaw
+35744,Dutton
+35745,Estillfork
+35746,Fackler
+35747,Grant
+35748,Gurley
+35749,Harvest
+35750,Hazel Green
+35751,Hollytree
+35752,Hollywood
+35754,Laceys Spring
+35755,Langston
+35756,Madison
+35757,Madison
+35758,Madison
+35759,Meridianville
+35760,New Hope
+35761,New Market
+35762,Normal
+35763,Owens Cross Roads
+35764,Paint Rock
+35765,Pisgah
+35766,Princeton
+35767,Ryland
+35768,Scottsboro
+35769,Scottsboro
+35771,Section
+35772,Stevenson
+35773,Toney
+35774,Trenton
+35775,Valhermoso Springs
+35776,Woodville
+35801,Huntsville
+35802,Huntsville
+35803,Huntsville
+35804,Huntsville
+35805,Huntsville
+35806,Huntsville
+35807,Huntsville
+35808,Huntsville
+35809,Huntsville
+35810,Huntsville
+35811,Huntsville
+35812,Huntsville
+35813,Huntsville
+35814,Huntsville
+35815,Huntsville
+35816,Huntsville
+35824,Huntsville
+35893,Huntsville
+35894,Huntsville
+35895,Huntsville
+35896,Huntsville
+35897,Huntsville
+35898,Huntsville
+35899,Huntsville
+35901,Gadsden
+35902,Gadsden
+35903,Gadsden
+35904,Gadsden
+35905,Gadsden
+35906,Rainbow City
+35907,Gadsden
+35950,Albertville
+35951,Albertville
+35952,Altoona
+35953,Ashville
+35954,Attalla
+35956,Boaz
+35957,Boaz
+35958,Bryant
+35959,Cedar Bluff
+35960,Centre
+35961,Collinsville
+35962,Crossville
+35963,Dawson
+35964,Douglas
+35966,Flat Rock
+35967,Fort Payne
+35968,Fort Payne
+35971,Fyffe
+35972,Gallant
+35973,Gaylesville
+35974,Geraldine
+35975,Groveoak
+35976,Guntersville
+35978,Henagar
+35979,Higdon
+35980,Horton
+35981,Ider
+35983,Leesburg
+35984,Mentone
+35986,Rainsville
+35987,Steele
+35988,Sylvania
+35989,Valley Head
+35990,Walnut Grove
+36003,Autaugaville
+36005,Banks
+36006,Billingsley
+36008,Booth
+36009,Brantley
+36010,Brundidge
+36013,Cecil
+36015,Chapman
+36016,Clayton
+36017,Clio
+36020,Coosada
+36022,Deatsville
+36023,East Tallassee
+36024,Eclectic
+36025,Elmore
+36026,Equality
+36027,Eufaula
+36028,Dozier
+36029,Fitzpatrick
+36030,Forest Home
+36031,Fort Davis
+36032,Fort Deposit
+36033,Georgiana
+36034,Glenwood
+36035,Goshen
+36036,Grady
+36037,Greenville
+36038,Gantt
+36039,Hardaway
+36040,Hayneville
+36041,Highland Home
+36042,Honoraville
+36043,Hope Hull
+36045,Kent
+36046,Lapine
+36047,Letohatchee
+36048,Louisville
+36049,Luverne
+36051,Marbury
+36052,Mathews
+36053,Midway
+36054,Millbrook
+36057,Mount Meigs
+36061,Perote
+36062,Petrey
+36064,Pike Road
+36065,Pine Level
+36066,Prattville
+36067,Prattville
+36068,Prattville
+36069,Ramer
+36071,Rutledge
+36072,Eufaula
+36075,Shorter
+36078,Tallassee
+36079,Troy
+36080,Titus
+36081,Troy
+36082,Troy
+36083,Tuskegee
+36087,Tuskegee Institute
+36088,Tuskegee Institute
+36089,Union Springs
+36091,Verbena
+36092,Wetumpka
+36093,Wetumpka
+36101,Montgomery
+36102,Montgomery
+36103,Montgomery
+36104,Montgomery
+36105,Montgomery
+36106,Montgomery
+36107,Montgomery
+36108,Montgomery
+36109,Montgomery
+36110,Montgomery
+36111,Montgomery
+36112,Montgomery
+36113,Montgomery
+36114,Montgomery
+36115,Montgomery
+36116,Montgomery
+36117,Montgomery
+36118,Montgomery
+36119,Montgomery
+36120,Montgomery
+36121,Montgomery
+36123,Montgomery
+36124,Montgomery
+36125,Montgomery
+36130,Montgomery
+36131,Montgomery
+36132,Montgomery
+36133,Montgomery
+36134,Montgomery
+36135,Montgomery
+36140,Montgomery
+36141,Montgomery
+36142,Montgomery
+36177,Montgomery
+36191,Montgomery
+36201,Anniston
+36202,Anniston
+36203,Oxford
+36204,Anniston
+36205,Anniston
+36206,Anniston
+36207,Anniston
+36250,Alexandria
+36251,Ashland
+36253,Bynum
+36254,Choccolocco
+36255,Cragford
+36256,Daviston
+36257,De Armanville
+36258,Delta
+36260,Eastaboga
+36261,Edwardsville
+36262,Fruithurst
+36263,Graham
+36264,Heflin
+36265,Jacksonville
+36266,Lineville
+36267,Millerville
+36268,Munford
+36269,Muscadine
+36270,Newell
+36271,Ohatchee
+36272,Piedmont
+36273,Ranburne
+36274,Roanoke
+36275,Spring Garden
+36276,Wadley
+36277,Weaver
+36278,Wedowee
+36279,Wellington
+36280,Woodland
+36301,Dothan
+36302,Dothan
+36303,Dothan
+36304,Dothan
+36305,Dothan
+36310,Abbeville
+36311,Ariton
+36312,Ashford
+36313,Bellwood
+36314,Black
+36316,Chancellor
+36317,Clopton
+36318,Coffee Springs
+36319,Columbia
+36320,Cottonwood
+36321,Cowarts
+36322,Daleville
+36323,Elba
+36330,Enterprise
+36331,Enterprise
+36340,Geneva
+36343,Gordon
+36344,Hartford
+36345,Headland
+36346,Jack
+36349,Malvern
+36350,Midland City
+36351,New Brockton
+36352,Newton
+36353,Newville
+36360,Ozark
+36361,Ozark
+36362,Fort Rucker
+36370,Pansey
+36371,Pinckard
+36373,Shorterville
+36374,Skipperville
+36375,Slocomb
+36376,Webb
+36401,Evergreen
+36420,Andalusia
+36425,Beatrice
+36426,Brewton
+36427,Brewton
+36429,Brooklyn
+36431,Burnt Corn
+36432,Castleberry
+36435,Coy
+36436,Dickinson
+36439,Excel
+36441,Flomaton
+36442,Florala
+36444,Franklin
+36445,Frisco City
+36446,Fulton
+36449,Goodway
+36451,Grove Hill
+36453,Kinston
+36454,Lenox
+36455,Lockhart
+36456,Mc Kenzie
+36457,Megargel
+36458,Mexia
+36460,Monroeville
+36461,Monroeville
+36462,Monroeville
+36467,Opp
+36470,Perdue Hill
+36471,Peterman
+36473,Range
+36474,Red Level
+36475,Repton
+36476,River Falls
+36477,Samson
+36480,Uriah
+36481,Vredenburgh
+36482,Whatley
+36483,Wing
+36501,Alma
+36502,Atmore
+36503,Atmore
+36504,Atmore
+36505,Axis
+36507,Bay Minette
+36509,Bayou La Batre
+36511,Bon Secour
+36512,Bucks
+36513,Calvert
+36515,Carlton
+36518,Chatom
+36521,Chunchula
+36522,Citronelle
+36523,Coden
+36524,Coffeeville
+36525,Creola
+36526,Daphne
+36527,Spanish Fort
+36528,Dauphin Island
+36529,Deer Park
+36530,Elberta
+36532,Fairhope
+36533,Fairhope
+36535,Foley
+36536,Foley
+36538,Frankville
+36539,Fruitdale
+36540,Gainestown
+36541,Grand Bay
+36542,Gulf Shores
+36543,Huxford
+36544,Irvington
+36545,Jackson
+36547,Gulf Shores
+36548,Leroy
+36549,Lillian
+36550,Little River
+36551,Loxley
+36553,Mc Intosh
+36555,Magnolia Springs
+36556,Malcolm
+36558,Millry
+36559,Montrose
+36560,Mount Vernon
+36561,Orange Beach
+36562,Perdido
+36564,Point Clear
+36567,Robertsdale
+36568,Saint Elmo
+36569,Saint Stephens
+36570,Salitpa
+36571,Saraland
+36572,Satsuma
+36574,Seminole
+36575,Semmes
+36576,Silverhill
+36577,Spanish Fort
+36578,Stapleton
+36579,Stockton
+36580,Summerdale
+36581,Sunflower
+36582,Theodore
+36583,Tibbie
+36584,Vinegar Bend
+36585,Wagarville
+36586,Walker Springs
+36587,Wilmer
+36590,Theodore
+36601,Mobile
+36602,Mobile
+36603,Mobile
+36604,Mobile
+36605,Mobile
+36606,Mobile
+36607,Mobile
+36608,Mobile
+36609,Mobile
+36610,Mobile
+36611,Mobile
+36612,Mobile
+36613,Eight Mile
+36614,Mobile
+36615,Mobile
+36616,Mobile
+36617,Mobile
+36618,Mobile
+36619,Mobile
+36621,Mobile
+36622,Mobile
+36623,Mobile
+36625,Mobile
+36626,Mobile
+36628,Mobile
+36630,Mobile
+36631,Mobile
+36633,Mobile
+36640,Mobile
+36641,Mobile
+36644,Mobile
+36652,Mobile
+36660,Mobile
+36663,Mobile
+36670,Mobile
+36671,Mobile
+36675,Mobile
+36685,Mobile
+36688,Mobile
+36689,Mobile
+36690,Mobile
+36691,Mobile
+36693,Mobile
+36695,Mobile
+36701,Selma
+36702,Selma
+36703,Selma
+36720,Alberta
+36721,Annemanie
+36722,Arlington
+36723,Boykin
+36726,Camden
+36727,Campbell
+36728,Catherine
+36732,Demopolis
+36736,Dixons Mills
+36738,Faunsdale
+36740,Forkland
+36741,Furman
+36742,Gallion
+36744,Greensboro
+36745,Jefferson
+36748,Linden
+36749,Jones
+36750,Maplesville
+36751,Lower Peach Tree
+36752,Lowndesboro
+36753,Mc Williams
+36754,Magnolia
+36756,Marion
+36758,Plantersville
+36759,Marion Junction
+36761,Minter
+36762,Morvin
+36763,Myrtlewood
+36764,Nanafalia
+36765,Newbern
+36766,Oak Hill
+36767,Orrville
+36768,Pine Apple
+36769,Pine Hill
+36773,Safford
+36775,Sardis
+36776,Sawyerville
+36778,Snow Hill
+36779,Sprott
+36782,Sweet Water
+36783,Thomaston
+36784,Thomasville
+36785,Tyler
+36786,Uniontown
+36790,Stanton
+36792,Randolph
+36793,Lawley
+36801,Opelika
+36802,Opelika
+36803,Opelika
+36804,Opelika
+36830,Auburn
+36831,Auburn
+36832,Auburn
+36849,Auburn University
+36850,Camp Hill
+36851,Cottonton
+36852,Cusseta
+36853,Dadeville
+36854,Valley
+36855,Five Points
+36856,Fort Mitchell
+36858,Hatchechubbee
+36859,Holy Trinity
+36860,Hurtsboro
+36861,Jacksons Gap
+36862,Lafayette
+36863,Lanett
+36865,Loachapoka
+36866,Notasulga
+36867,Phenix City
+36868,Phenix City
+36869,Phenix City
+36870,Phenix City
+36871,Pittsview
+36872,Valley
+36874,Salem
+36875,Seale
+36877,Smiths
+36879,Waverly
+36901,Bellamy
+36904,Butler
+36906,Cromwell
+36907,Cuba
+36908,Gilbertown
+36910,Jachin
+36912,Lisman
+36913,Melvin
+36915,Needham
+36916,Pennington
+36919,Silas
+36921,Toxey
+36922,Ward
+36925,York
+99501,Anchorage
+99502,Anchorage
+99503,Anchorage
+99504,Anchorage
+99505,Fort Richardson
+99506,Elmendorf AFB
+99507,Anchorage
+99508,Anchorage
+99509,Anchorage
+99510,Anchorage
+99511,Anchorage
+99512,Anchorage
+99513,Anchorage
+99514,Anchorage
+99515,Anchorage
+99516,Anchorage
+99517,Anchorage
+99518,Anchorage
+99519,Anchorage
+99520,Anchorage
+99521,Anchorage
+99522,Anchorage
+99523,Anchorage
+99524,Anchorage
+99540,Indian
+99546,Adak
+99547,Atka
+99548,Chignik Lake
+99549,Port Heiden
+99550,Port Lions
+99551,Akiachak
+99552,Akiak
+99553,Akutan
+99554,Alakanuk
+99555,Aleknagik
+99556,Anchor Point
+99557,Aniak
+99558,Anvik
+99559,Bethel
+99561,Chefornak
+99563,Chevak
+99564,Chignik
+99565,Chignik Lagoon
+99566,Chitina
+99567,Chugiak
+99568,Clam Gulch
+99569,Clarks Point
+99571,Cold Bay
+99572,Cooper Landing
+99573,Copper Center
+99574,Cordova
+99575,Crooked Creek
+99576,Dillingham
+99577,Eagle River
+99578,Eek
+99579,Egegik
+99580,Ekwok
+99581,Emmonak
+99583,False Pass
+99584,Flat
+99585,Marshall
+99586,Gakona
+99587,Girdwood
+99588,Glennallen
+99589,Goodnews Bay
+99590,Grayling
+99591,Saint George Island
+99599,Anchorage
+99602,Holy Cross
+99603,Homer
+99604,Hooper Bay
+99605,Hope
+99606,Iliamna
+99607,Kalskag
+99608,Karluk
+99609,Kasigluk
+99610,Kasilof
+99611,Kenai
+99612,King Cove
+99613,King Salmon
+99614,Kipnuk
+99615,Kodiak
+99619,Kodiak
+99620,Kotlik
+99621,Kwethluk
+99622,Kwigillingok
+99624,Larsen Bay
+99625,Levelock
+99626,Lower Kalskag
+99627,Mc Grath
+99628,Manokotak
+99630,Mekoryuk
+99631,Moose Pass
+99632,Mountain Village
+99633,Naknek
+99634,Napakiak
+99635,Nikiski
+99636,New Stuyahok
+99637,Toksook Bay
+99638,Nikolski
+99639,Ninilchik
+99640,Nondalton
+99641,Nunapitchuk
+99643,Old Harbor
+99644,Ouzinkie
+99645,Palmer
+99647,Pedro Bay
+99648,Perryville
+99649,Pilot Point
+99650,Pilot Station
+99651,Platinum
+99652,Big Lake
+99653,Port Alsworth
+99654,Wasilla
+99655,Quinhagak
+99656,Red Devil
+99657,Russian Mission
+99658,Saint Marys
+99659,Saint Michael
+99660,Saint Paul Island
+99661,Sand Point
+99662,Scammon Bay
+99663,Seldovia
+99664,Seward
+99665,Shageluk
+99666,Sheldon Point
+99667,Skwentna
+99668,Sleetmute
+99669,Soldotna
+99670,South Naknek
+99671,Stebbins
+99672,Sterling
+99674,Sutton
+99675,Takotna
+99676,Talkeetna
+99677,Tatitlek
+99678,Togiak
+99679,Tuluksak
+99680,Tuntutuliak
+99681,Tununak
+99682,Tyonek
+99683,Trapper Creek
+99684,Unalakleet
+99685,Unalaska
+99686,Valdez
+99687,Wasilla
+99688,Willow
+99689,Yakutat
+99690,Nightmute
+99691,Nikolai
+99692,Dutch Harbor
+99693,Whittier
+99694,Houston
+99695,Anchorage
+99697,Kodiak
+99701,Fairbanks
+99702,Eielson AFB
+99703,Fort Wainwright
+99704,Clear
+99705,North Pole
+99706,Fairbanks
+99707,Fairbanks
+99708,Fairbanks
+99709,Fairbanks
+99710,Fairbanks
+99711,Fairbanks
+99712,Fairbanks
+99714,Salcha
+99716,Two Rivers
+99720,Allakaket
+99721,Anaktuvuk Pass
+99722,Arctic Village
+99723,Barrow
+99724,Beaver
+99725,Ester
+99726,Bettles Field
+99727,Buckland
+99729,Cantwell
+99730,Central
+99732,Chicken
+99733,Circle
+99734,Prudhoe Bay
+99736,Deering
+99737,Delta Junction
+99738,Eagle
+99739,Elim
+99740,Fort Yukon
+99741,Galena
+99742,Gambell
+99743,Healy
+99744,Anderson
+99745,Hughes
+99746,Huslia
+99747,Kaktovik
+99748,Kaltag
+99749,Kiana
+99750,Kivalina
+99751,Kobuk
+99752,Kotzebue
+99753,Koyuk
+99754,Koyukuk
+99755,Denali National Park
+99756,Manley Hot Springs
+99757,Lake Minchumina
+99758,Minto
+99759,Point Lay
+99760,Nenana
+99761,Noatak
+99762,Nome
+99763,Noorvik
+99764,Northway
+99765,Nulato
+99766,Point Hope
+99767,Rampart
+99768,Ruby
+99769,Savoonga
+99770,Selawik
+99771,Shaktoolik
+99772,Shishmaref
+99773,Shungnak
+99774,Stevens Village
+99775,Fairbanks
+99776,Tanacross
+99777,Tanana
+99778,Teller
+99779,Tetlin
+99780,Tok
+99781,Venetie
+99782,Wainwright
+99783,Wales
+99784,White Mountain
+99785,Brevig Mission
+99786,Ambler
+99788,Chalkyitsik
+99789,Nuiqsut
+99790,Fairbanks
+99791,Atqasuk
+99801,Juneau
+99802,Juneau
+99803,Juneau
+99811,Juneau
+99820,Angoon
+99821,Auke Bay
+99824,Douglas
+99825,Elfin Cove
+99826,Gustavus
+99827,Haines
+99829,Hoonah
+99830,Kake
+99832,Pelican
+99833,Petersburg
+99835,Sitka
+99836,Port Alexander
+99840,Skagway
+99841,Tenakee Springs
+99850,Juneau
+99901,Ketchikan
+99903,Meyers Chuck
+99918,Coffman Cove
+99919,Thorne Bay
+99921,Craig
+99922,Hydaburg
+99923,Hyder
+99925,Klawock
+99926,Metlakatla
+99927,Point Baker
+99928,Ward Cove
+99929,Wrangell
+99950,Ketchikan
\ No newline at end of file
diff --git a/src/site/resources/download_jcs.cgi b/src/site/resources/download_jcs.cgi
new file mode 100644
index 0000000..02a8b88
--- /dev/null
+++ b/src/site/resources/download_jcs.cgi
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# 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.
+
+
+# Just call the standard mirrors.cgi script. It will use download.html
+# as the input template.
+exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $*
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..2360683
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<project name="JCS">
+  <bannerRight>
+    <name>Commons JCS™</name>
+    <href>/index.html</href>
+  </bannerRight>
+
+  <body>
+    <menu name="JCS">
+      <item name="Overview" href="/index.html"/>
+      <item name="JCS and JCACHE" href="/JCSandJCACHE.html"/>
+      <item name="Downloads" href="/download_jcs.cgi"/>
+      <item name="FAQ" href="/faq.html"/>
+    </menu>
+
+    <menu name="Development">
+      <item name="Release Notes" href="/changes-report.html"/>
+      <item name="Mailing Lists" href="/mail-lists.html"/>
+      <item name="Issue Tracking" href="/issue-tracking.html"/>
+      <item name="Source Repository" href="/source-repository.html"/>
+      <item name="Javadoc (SVN latest)" href="apidocs/index.html"/>
+    </menu>
+
+    <menu name="Getting Started">
+      <item name="Overview" href="/getting_started/intro.html"/>
+      <item name="Basic JCS Config" href="/BasicJCSConfiguration.html"/>
+      <item name="Plugin Overview" href="/JCSPlugins.html"/>
+      <item name="Basic Web Example" href="/UsingJCSBasicWeb.html"/>
+    </menu>
+
+    <menu name="JCS User's Guide">
+      <item name="Core" href="/LocalCacheConfig.html" collapse="true">
+        <item name="Basic JCS Config" href="/BasicJCSConfiguration.html"/>
+        <item name="Element Config" href="/ElementAttributes.html"/>
+        <item name="Element Event Handling" href="/ElementEventHandling.html"/>
+        <item name="Region Properties" href="/RegionProperties.html"/>
+        <item name="Basic Web Example" href="/UsingJCSBasicWeb.html"/>
+        <item name="Project History" href="/ProjectHistory.html"/>
+      </item>
+      <item name="Auxiliary" href="/JCSPlugins.html" collapse="true">
+        <item name="Cache Event Logging" href="/CacheEventLogging.html"/>
+        <item name="Indexed Disk Cache" href="/IndexedDiskAuxCache.html"/>
+        <item name="Indexed Disk Properties" href="/IndexedDiskCacheProperties.html"/>
+        <item name="Block Disk Cache" href="/BlockDiskCache.html"/>
+        <item name="JDBC Disk Cache" href="/JDBCDiskCache.html"/>
+        <item name="JDBC Disk Properties" href="/JDBCDiskCacheProperties.html"/>
+        <item name="MySQL Disk Properties" href="/MySQLDiskCacheProperties.html"/>
+        <item name="Remote Cache" href="/RemoteAuxCache.html"/>
+        <item name="Remote Cache Properties" href="/RemoteCacheProperties.html"/>
+        <item name="Remote Http Cache Properties" href="/RemoteHttpCacheProperties.html"/>
+        <item name="Lateral TCP Cache" href="/LateralTCPAuxCache.html"/>
+        <item name="Lateral TCP Properties" href="/LateralTCPProperties.html"/>
+        <item name="Lateral UDP Discovery" href="/LateralUDPDiscovery.html"/>
+        <item name="Lateral JGroups Cache" href="/LateralJavaGroupsAuxCache.html"/>
+      </item>
+    </menu>
+
+  </body>
+
+</project>
diff --git a/xdocs/BDBJEDiskCache.ccf b/xdocs/BDBJEDiskCache.ccf
new file mode 100644
index 0000000..6799893
--- /dev/null
+++ b/xdocs/BDBJEDiskCache.ccf
@@ -0,0 +1,84 @@
+# 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.
+<?xml version="1.0"?>
+
+<document>
+  <properties>
+    <title>Berkeley DB Disk Auxiliary Cache</title>
+    <author email="ASmuts at yahoo.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Berkeley DB Disk Auxiliary Cache">
+      <p>
+        The Berkeley DB Disk Auxiliary Cache is an optional plugin for the
+        JCS.  It is primarily intended to provide a secondary store to
+        ease the memory burden of the cache.  When the memory cache
+  	    exceeds its maximum size it tells the cache hub that the item
+	      to be removed from memory should be spooled to disk.  The cache
+	      checks to see if any auxiliaries of type "disk" have been
+	      configured for the region.  If the "Berkeley DB Disk Auxiliary Cache"
+	      is used, the item will be spooled to disk.
+      </p>
+      <p>
+		The Berkeley DB is far slower than the Indexed Disk Cache, especially for puts.
+		This is partially due to the fact that the BDB store its keys on disk.
+		However, any items stored in the BDB will be available on restart, even if
+		the cache is not shutdown properly.
+      </p>
+      <p>
+		The Berkeley DB requires jdk1.4 and above.  As such, it is distributed
+		in the jdk14-ext jar.
+      </p>
+
+      <subsection name="Configuration">
+        <p>
+          The configuration is simple and is done in the auxiliary
+          cache section of the <code>cache.ccf</code> configuration file.
+          In the example below, I created a Berkeley DB Auxiliary Cache
+          referenced by <code>BDBDC</code>.  It uses files located in the
+          "DiskPath" directory.
+         </p>
+
+            <source><![CDATA[
+# #############################################################
+# #### Default Region Configuration
+jcs.default=BDBDC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# #### CACHE REGIONS
+jcs.region.myRegion1=BDBDC
+jcs.region.myRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.myRegion1.cacheattributes.MaxObjects=1000
+jcs.region.myRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# #############################################################
+# #### AUXILIARY CACHES
+# Berkeley DB JE
+jcs.auxiliary.BDBDC=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheFactory
+jcs.auxiliary.BDBDC.attributes=org.apache.commons.jcs.auxiliary.disk.bdbje.BDBJECacheAttributes
+jcs.auxiliary.BDBDC.attributes.DiskPath=target/
+jcs.auxiliary.BDBDC.attributes.MaxPurgatorySize=100000
+        ]]></source>
+          </subsection>
+
+    </section>
+  </body>
+</document>
diff --git a/xdocs/BasicJCSConfiguration.xml b/xdocs/BasicJCSConfiguration.xml
new file mode 100644
index 0000000..89142b9
--- /dev/null
+++ b/xdocs/BasicJCSConfiguration.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Basic JCS Configuration</title>
+    <author email="ASmuts at therealm.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Basic JCS Configuration">
+      <p>
+        The following document illustrates several basic JCS
+        configurations.  As you'll see, using JCS can be as simple as
+        creating a single memory cache for you application.  However,
+        with a few configuration changes, you can quickly enable some
+        distributed caching features that can scale your application
+        even further.
+      </p>
+      <subsection name="Building a cache.ccf file">
+        <p>
+          Configuring the JCS can be as simple as your needs.  The most
+          basic configuration would be a pure memory cache where every
+          region takes the default values.  The complete configuration
+          file (cache.ccf) could look like this:
+        </p>
+        <source><![CDATA[
+# DEFAULT CACHE REGION
+
+jcs.default=
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+        ]]></source>
+        <p>
+          If you want to add memory shrinking then you can add these
+          lines:
+        </p>
+        <source><![CDATA[
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.cacheattributes.MaxSpoolPerRun=500
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+        ]]></source>
+        <p>
+          Adding a <a href="IndexedDiskAuxCache.html">disk cache</a> is
+          as simple as telling it what folder to use.  It is recommended
+          that you add a disk cache.  If you want to add a disk cache to
+          your default parameters, then (1) add this to the bottom of
+          the file to create the auxiliary:
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=g:/dev/jcs/raf
+        ]]></source>
+        <p>
+          and (2) change the first line to:
+        </p>
+        <source><![CDATA[
+jcs.default=DC
+        ]]></source>
+        <p>
+          If you want to predefine a specific region, say called
+          <code>testCache1</code>, then add these lines:
+        </p>
+        <source><![CDATA[
+jcs.region.testCache1=DC
+jcs.region.testCache1.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTime=3600
+jcs.region.testCache1.cacheattributes.ShrinkerInterval=60
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=500
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+
+        ]]></source>
+        <p>
+          If you want to add a lateral cache for distribution (the <a
+            href="LateralTCPAuxCache.html">TCP Lateral Auxiliary</a> is
+          recommended), then add these lines to the bottom of the file
+          to define the auxiliary:
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.LTCP=
+    org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=
+    org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=false
+        ]]></source>
+        <p>
+          See the TCP Lateral documentation for more information.  If you
+          want to set up <code>testCache1</code> to use this, then change
+          the definition to:
+        </p>
+        <source><![CDATA[
+jcs.region.testCache1=DC,LTCP
+        ]]></source>
+      </subsection>
+      <subsection name="A few comments on configuration">
+        <p>
+          Auxiliary definitions are like log4j appenders, they are defined
+          and then associated with a region like a log4j category.
+        </p>
+        <p>
+          The order of configuration file is unimportant, though you
+          should try to keep it organized for your own sake.
+        </p>
+        <p>
+          Configuration is being refactored and is subject to change.  It
+          should only become easier.
+        </p>
+      </subsection>
+      <subsection name="The complete file">
+        <p>
+          The complete file from above would look like this:
+        </p>
+        <source><![CDATA[
+# DEFAULT CACHE REGION
+
+jcs.default=DC,LTCP
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+# PRE-DEFINED CACHE REGIONS
+
+jcs.region.testCache1=DC,LTCP
+jcs.region.testCache1.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTime=3600
+jcs.region.testCache1.cacheattributes.ShrinkerInterval=60
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=500
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+
+
+# AVAILABLE AUXILIARY CACHES
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=g:/dev/jcs/raf
+jcs.auxiliary.DC.attributes.maxKeySize=100000
+
+jcs.auxiliary.LTCP=
+    org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LTCP.attributes=
+    org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TransmissionTypeName=TCP
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=false
+        ]]></source>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/BlockDiskCache.xml b/xdocs/BlockDiskCache.xml
new file mode 100644
index 0000000..a3c0745
--- /dev/null
+++ b/xdocs/BlockDiskCache.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Block Disk Cache</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Block Disk Auxiliary Cache">
+			<p>
+				The Block Disk Cache stores cached values on disk. Like
+				the Indexed Disk Cache, the Block Disk Cache keeps the
+				keys in memory. The Block Disk Cache stores the values
+				in a group of fixed size blocks, whereas the Indexed
+				Disk Cache writes items to disk in one chunk.
+			</p>
+			<p>
+				The Block Disk Cache has advantages over the normal
+				indexed model for regions where the size of the items
+				varies. Since all the blocks are the same size, the
+				recycle bin is very simple. It is just a list of block
+				numbers. Also, the Block Disk Cache will never need to
+				be optimized. Once the maximum number of keys is
+				reached, blocks will be reused.
+			</p>
+
+			<subsection name="Example cache.ccf">
+				<source>
+					<![CDATA[
+##############################################################
+##### DEFAULT REGION  ########################################
+
+jcs.default=blockDiskCache
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=0
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+##############################################################
+##### AUXILIARY CACHES  ######################################
+
+# Block Disk Cache
+jcs.auxiliary.blockDiskCache=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory
+jcs.auxiliary.blockDiskCache.attributes=org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes
+jcs.auxiliary.blockDiskCache.attributes.DiskPath=target/test-sandbox/block-disk-cache-huge
+jcs.auxiliary.blockDiskCache.attributes.MaxPurgatorySize=300000
+jcs.auxiliary.blockDiskCache.attributes.MaxKeySize=1000000
+jcs.auxiliary.blockDiskCache.attributes.blockSizeBytes=500
+jcs.auxiliary.blockDiskCache.attributes.EventQueueType=SINGLE
+#jcs.auxiliary.blockDiskCache.attributes.EventQueuePoolName=disk_cache_event_queue
+
+##############################################################
+################## THREAD POOL CONFIGURATION #################
+
+# Default thread pool config
+thread_pool.default.boundarySize=2000
+thread_pool.default.maximumPoolSize=150
+thread_pool.default.minimumPoolSize=4
+thread_pool.default.keepAliveTime=350000
+#RUN ABORT WAIT BLOCK DISCARDOLDEST
+thread_pool.default.whenBlockedPolicy=RUN
+thread_pool.default.startUpSize=4
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.minimumPoolSize=2
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=10
+        ]]>
+				</source>
+			</subsection>
+		</section>
+	</body>
+</document>
diff --git a/xdocs/CacheEventLogging.xml b/xdocs/CacheEventLogging.xml
new file mode 100644
index 0000000..e540fce
--- /dev/null
+++ b/xdocs/CacheEventLogging.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Cache Event Logging</title>
+		<author email="ASmuts at apache.com">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="Cache Event Logging">
+			<p> JCS allows you to implement custom event loggers. Most of the
+				auxiliaries will log ICacheEvents (eg. update, get, getMultiple, remove,
+				removeAll, and dispose) to an injected event logger. By default the
+				log calls balk. But if you inject a logger, you can add monitoring
+				to any auxiliary. Most auxiliaries also log key application events
+				and critical errors to the same logger.</p>
+			<p>
+				To inject a custom event logger, you simply need to implement the
+				<code>org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger
+				</code>
+				interface and add a couple of lines to the cache.ccf file.
+			</p>
+			<p> During configuration, JCS will look for event loggers configured
+				for each auxiliary. JCS will set any custom properties. For
+				instance, to add debug event logging to a remote cache client, you
+				could do the following:</p>
+			<source><![CDATA[
+			. . .
+			jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+# jcs.auxiliary.RC.attributes.RemoteServiceName=RemoteCache
+# -1 means no timeout, this is the default
+# if the timeout is -1, no threadpool will be used.
+jcs.auxiliary.RC.attributes.GetTimeoutMillis=500
+jcs.auxiliary.RC.attributes.ThreadPoolName=remote_cache_client
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.cacheeventlogger=org.apache.commons.jcs.engine.logging.CacheEventLoggerDebugLogger
+jcs.auxiliary.RC.cacheeventlogger.attributes.logCategoryName=test.RCCEventLogCategory
+			. . .
+        ]]></source>
+			<p> The attribute "logCateoryName" is a property of this
+				implementation. You can configure any properties on your
+				implementation in the same way.</p>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/DownloadPage.xml b/xdocs/DownloadPage.xml
new file mode 100644
index 0000000..366fc9e
--- /dev/null
+++ b/xdocs/DownloadPage.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE xdoc [
+    <!ENTITY nbsp " ">
+]>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Downloading JCS</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+		<author email="tv at apache.org">Thomas Vandahl</author>
+	</properties>
+	<body>
+		<section name="Releases">
+			<p> The latest release version of JCS is 1.3. Grab it here. The core
+				JCS jar is compiled using JDK 1.3.</p>
+			<subsection name="Binary versions">
+				<p>
+					<ul>
+						<li>
+							<a
+								href="http://www.apache.org/dyn/closer.cgi/commons/jcs/binaries/jcs-1.3.tar.gz"> JCS 1.3 Binary Distribution in TAR format</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/binaries/jcs-1.3.tar.gz.asc">Signature</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/binaries/jcs-1.3.tar.gz.md5">MD5</a>
+						</li>
+						<li>
+							<a
+								href="http://www.apache.org/dyn/closer.cgi/commons/jcs/binaries/jcs-1.3.zip"> JCS 1.3 Binary Distribution in ZIP format</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/binaries/jcs-1.3.zip.asc">Signature</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/binaries/jcs-1.3.zip.md5">MD5</a>
+						</li>
+					</ul>
+				</p>
+			</subsection>
+			<subsection name="Source versions">
+				<p>
+					<ul>
+						<li>
+							<a
+								href="http://www.apache.org/dyn/closer.cgi/commons/jcs/source/jcs-1.3-src.tar.gz"> JCS 1.3 Source Distribution in TAR format</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/source/jcs-1.3-src.tar.gz.asc">Signature</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/source/jcs-1.3-src.tar.gz.md5">MD5</a>
+						</li>
+						<li>
+							<a
+								href="http://www.apache.org/dyn/closer.cgi/commons/jcs/source/jcs-1.3-src.zip"> JCS 1.3 Source Distribution in ZIP format</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/source/jcs-1.3-src.zip.asc">Signature</a> 
+							<a
+								href="http://www.apache.org/dist/commons/jcs/source/jcs-1.3-src.zip.md5">MD5</a>
+						</li>
+					</ul>
+				</p>
+			</subsection>
+		</section>
+		<section name="Getting The Development Source From SVN">
+			<p>
+				You can check out the latest source from the Commons SVN module.
+				<br />
+				JCS resides in the commons/proper/jcs module.
+				<source><![CDATA[svn checkout http://svn.apache.org/repos/asf/commons/proper/jcs/trunk]]></source>
+				or browse the source code through
+				<a href="http://svn.apache.org/viewvc/commons/proper/jcs">ViewVC</a>
+				.
+			</p>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/ElementAttributes.xml b/xdocs/ElementAttributes.xml
new file mode 100644
index 0000000..7005169
--- /dev/null
+++ b/xdocs/ElementAttributes.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Element Attribute Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Element Attribute Configuration">
+			<p>
+				The following document describes the various
+				configuration options available for cache elements. Each
+				element put into the cache can be configured
+				independently. You can define element behavior in three
+				ways: as a default setting, as a region setting, or at
+				the element level.
+			</p>
+
+			<subsection name="Setting the defaults">
+				<p>
+					The configuration below can be put in the cache.ccf
+					configuration file. It establishes the default
+					behavior for all regions. A region can override
+					these defaults and an individual element can override
+					these defaults and the region settings.
+				</p>
+				<source>
+					<![CDATA[
+# DEFAULT CACHE REGION
+
+jcs.default=DC
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+        ]]>
+				</source>
+				<p>
+					The default and region configuration settings have
+					three components. They define what auxiliaries are
+					available, how the cache should control the memory,
+					and how the elements should behave. This
+					configuration tells all regions to use an auxiliary
+					called DC by default. It also establishes several
+					settings for memory management (see
+					<a href="BasicJCSConfiguration.html">
+						Basic JCS Configuration
+					</a>
+					for more information on the cacheattribute
+					settings). In addition, by default all regions will
+					take these element configuration settings.
+				</p>
+				<p>
+					These settings specify that elements are not
+					eternal, i.e. they can expire. By default elements
+					are considered eternal.
+				</p>
+				<p>
+					You can define the maximum life of an item by
+					setting the
+					<code>MaxLife</code>
+					parameter. If an item has been in the cache for
+					longer than the set number of seconds it will not be
+					retrieved on a get request. If you use the memory
+					shrinker the item will be actively removed from
+					memory. Currently there is no background disk
+					shrinker, but the disk cache does allow for a
+					maximum number of keys (see
+					<a href="IndexedDiskAuxCache.html">
+						Indexed Disk Cache
+					</a>
+					for more information on the disk cache settings).
+				</p>
+				<p>
+					You can define the maximum time an item can live
+					without being accessed by setting the
+					<code>IdleTime</code>
+					parameter. This is different than the
+					<code>MaxMemoryIdleTime</code>
+					parameter, which just specifies how long an object
+					can be in memory before it is subjected to removal
+					or being spooled to a disk cache if it is available.
+					Note: the
+					<code>IdleTime</code>
+					parameter may not function properly for items
+					retrieved from disk, if you have a memory size of 0.
+				</p>
+			</subsection>
+
+			<p>
+				<code>IsSpool</code>
+				determines whether or not the element can go to disk, if
+				a disk cache is configured for the region.
+			</p>
+			<p>
+				<code>IsRemote</code>
+				determines whether or not the element can be sent to a
+				remote server, if one is configured for the region.
+			</p>
+			<p>
+				<code>IsLateral</code>
+				determines whether or not the element can be laterally
+				distributed, if a lateral auxiliary is configured for
+				the region.
+			</p>
+
+
+			<subsection name="Programmatic Configuration">
+				<p>
+					Every element put into the cache has its own set of
+					attributes. By default elements are given a copy of
+					the default element attributes associated with a
+					region. You can also specify the attributes to use
+					for an element when you put it in the cache.
+				</p>
+				<source>
+				<![CDATA[
+    CacheAccess<String, String> jcs = JCS.getInstance( "myregion" );
+
+    . . .
+
+    // jcs.getDefaultElementAttributes returns a copy not a reference
+    IElementAttributes attributes = jcs.getDefaultElementAttributes();
+
+    // set some special value
+    attributes.setIsEternal( true );
+    jcs.put( "key", "data", attributes );
+        		]]>
+				</source>
+
+				<p>
+					You can also programmatically modify the default
+					element attributes.
+				</p>
+
+				<source>
+					<![CDATA[
+    CacheAccess<String, String> jcs = JCS.getInstance( "myregion" );
+
+    . . .
+
+    // jcs.getDefaultElementAttributes returns a copy not a reference
+    IElementAttributes attributes = jcs.getDefaultElementAttributes();
+
+    // set some special value
+    attributes.setIsEternal( true );
+    jcs.setDefaultElementAttributes( attributes );
+        		]]>
+				</source>
+			</subsection>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/ElementEventHandling.xml b/xdocs/ElementEventHandling.xml
new file mode 100644
index 0000000..4b7ff1c
--- /dev/null
+++ b/xdocs/ElementEventHandling.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Element Event Handling</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Element Event Handling">
+			<p>
+				JCS allows you to attach event handlers to elements in
+				the local memory cache.
+			</p>
+			<p>
+				There are several events that you can listen for. All of
+				the events are local memory related events. Element
+				event handlers are not transmitted to other caches via
+				lateral or remote auxiliaries, nor are they spooled to
+				disk.
+			</p>
+			<p>
+				You can register multiple handlers for a single item.
+				Although the handlers are associated with particular
+				items, you can also setup default handlers for any
+				region. Each item put into the region, that will take
+				the default element attributes, will be assigned the
+				event default event handlers.
+			</p>
+			<p>
+				The various events that you can handle have all been
+				assigned integer codes. The codes are defined in the
+				org.apache.commons.jcs.engine.control.event.behavior.IElementEventConstants
+				interface. The events are named descriptively and
+				include:
+			</p>
+			<table>
+				<tr>
+					<th>Name</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_EXCEEDED_MAXLIFE_BACKGROUND</td>
+					<td>
+						The element exceeded its max life. This was
+						detected in a background cleanup.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_EXCEEDED_MAXLIFE_ONREQUEST</td>
+					<td>
+						The element exceeded its max life. This was
+						detected on request.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_EXCEEDED_IDLETIME_BACKGROUND</td>
+					<td>
+						The element exceeded its max idle. This was
+						detected in a background cleanup.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_EXCEEDED_IDLETIME_ONREQUEST</td>
+					<td>
+						The element exceeded its max idle time. This was
+						detected on request.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_SPOOLED_DISK_AVAILABLE</td>
+					<td>
+						The element was pushed out of the memory store,
+						there is a disk store available for the region,
+						and the element is marked as spoolable.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_SPOOLED_DISK_NOT_AVAILABLE</td>
+					<td>
+						The element was pushed out of the memory store,
+						and there is not a disk store available for the
+						region.
+					</td>
+				</tr>
+				<tr>
+					<td>ELEMENT_EVENT_SPOOLED_NOT_ALLOWED</td>
+					<td>
+						The element was pushed out of the memory store,
+						there is a disk store available for the region,
+						but the element is marked as not spoolable.
+					</td>
+				</tr>
+			</table>
+			<p>
+				To create an event handler you must implement the
+				org.apache.commons.jcs.engine.control.event.behavior.IElementEventHandler
+				interface. This interface contains only one method:
+			</p>
+			<source>
+				<![CDATA[
+    public void handleElementEvent( IElementEvent event );
+        		]]>
+			</source>
+			<p>
+				The IElementEvent object contains both the event code
+				and the source. The source is the element for which the
+				event occurred. The code is the type of event. If you
+				have an event handler registered, it will be called
+				whenever any event occurs. It is up to the handler to
+				decide what it would like to do for the particular
+				event. Since there are not that many events, this does
+				not create too much activity. Also, the event handling
+				is done asynchronously. Events are added to an event
+				queue and processed by background threads.
+			</p>
+            <p>
+                Here is how to extract the event and source from the 
+                IElementEvent:
+            </p>
+            <source>
+                <![CDATA[
+    public void handleElementEvent( IElementEvent event )
+    {
+        int eventType = event.getElementEvent();
+        CacheElement element = (CacheElement)((EventObject)event).getSource();
+        . . .
+    }
+                ]]>
+            </source>
+			<p>
+				Once you have an IElementEventHandler implementation,
+				you can attach it to an element via the Element
+				Attributes. You can either add it to the element
+				attributes when you put an item into the cache, add it
+				to the attributes of an item that exist in the cache
+				(which just results in a re-put), or add the event
+				handler to the default element attributes for a region.
+				If you add it to the default attributes, then all
+				elements subsequently added to the region that do not
+				define their own element attributes will be assigned the
+				default event handlers.
+			</p>
+			<source>
+				<![CDATA[
+    CacheAccess<String, String> jcs = JCS.getInstance( "myregion" );
+
+    . . .
+
+    MyEventHandler meh = new MyEventHandler();
+
+    // jcs.getDefaultElementAttributes returns a copy not a reference
+    IElementAttributes attributes = jcs.getDefaultElementAttributes();
+    attributes.addElementEventHandler( meh );
+    jcs.put( "key", "data", attributes );
+        		]]>
+			</source>
+			<p>
+				Here is how to setup an event handler as a default
+				setting for a region:
+			</p>
+			<source>
+				<![CDATA[
+    CacheAccess<String, String> jcs = JCS.getInstance( "myregion" );
+
+    . . .
+
+    MyEventHandler meh = new MyEventHandler();
+
+    // this should add the event handler to all items as
+    //they are created.
+    // jcs.getDefaultElementAttributes returns a copy not a reference
+    IElementAttributes attributes = jcs.getDefaultElementAttributes();
+    attributes.addElementEventHandler( meh );
+    jcs.setDefaultElementAttributes( attributes );
+        		]]>
+			</source>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/IndexedDiskAuxCache.xml b/xdocs/IndexedDiskAuxCache.xml
new file mode 100644
index 0000000..b3ba7e5
--- /dev/null
+++ b/xdocs/IndexedDiskAuxCache.xml
@@ -0,0 +1,307 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Indexed Disk Auxiliary Cache</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Indexed Disk Auxiliary Cache">
+			<p>
+				The Indexed Disk Auxiliary Cache is an optional plugin
+				for the JCS. It is primarily intended to provide a
+				secondary store to ease the memory burden of the cache.
+				When the memory cache exceeds its maximum size it tells
+				the cache hub that the item to be removed from memory
+				should be spooled to disk. The cache checks to see if
+				any auxiliaries of type "disk" have been configured for
+				the region. If the "Indexed Disk Auxiliary Cache" is
+				used, the item will be spooled to disk.
+			</p>
+
+			<subsection name="Disk Indexing">
+				<p>
+					The Indexed Disk Auxiliary Cache follows the fastest
+					pattern of disk caching. Items are stored at the end
+					of a file dedicated to the cache region. The first
+					byte of each disk entry specifies the length of the
+					entry. The start position in the file is saved in
+					memory, referenced by the item's key. Though this
+					still requires memory, it is insignificant given the
+					performance trade off. Depending on the key size,
+					500,000 disk entries will probably only require
+					about 3 MB of memory. Locating the position of an
+					item is as fast as a map lookup and the retrieval of
+					the item only requires 2 disk accesses.
+				</p>
+				<p>
+					When items are removed from the disk cache, the
+					location of the available block on the storage file
+					is recorded in a sorted preferential array of a size
+					not to exceed the maximum number of keys allowed in
+					memory. This allows the disk cache to reuse empty
+					spots, thereby keeping the file size to a minimum.
+				</p>
+			</subsection>
+
+			<subsection name="Purgatory">
+				<p>
+					Writing to the disk cache is asynchronous and made
+					efficient by using a memory staging area called
+					purgatory. Retrievals check purgatory then disk for
+					an item. When items are sent to purgatory they are
+					simultaneously queued to be put to disk. If an item
+					is retrieved from purgatory it will no longer be
+					written to disk, since the cache hub will move it
+					back to memory. Using purgatory insures that there
+					is no wait for disk writes, unecessary disk writes
+					are avoided for borderline items, and the items are
+					always available.
+				</p>
+			</subsection>
+
+			<subsection name="Persistence">
+				<p>
+					When the disk cache is properly shutdown, the memory
+					index is written to disk and the value file is
+					defragmented. When the cache starts up, the disk
+					cache can be configured to read or delete the index
+					file. This provides an unreliable persistence
+					mechanism.
+				</p>
+			</subsection>
+
+			<subsection name="Configuration">
+				<p>
+					Configuration is simple and is done in the
+					auxiliary cache section of the
+					<code>cache.ccf</code>
+					configuration file. In the example below, I created
+					an Indexed Disk Auxiliary Cache referenced by
+					<code>DC</code>
+					. It uses files located in the "DiskPath" directory.
+				</p>
+				<p>
+					The Disk indexes are equipped with an LRU storage
+					limit. The maximum number of keys is configured by
+					the maxKeySize parameter. If the maximum key size is
+					less than 0, no limit will be placed on the number
+					of keys. By default, the max key size is 5000.
+				</p>
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=g:\dev\jakarta-turbine-stratum\raf
+jcs.auxiliary.DC.attributes.MaxKeySize=100000
+        ]]>
+				</source>
+			</subsection>
+
+			<subsection name="Additional Configuration Options">
+				<p>
+					The indexed disk cache provides some additional
+					configuration options.
+				</p>
+				<p>
+					The purgatory size of the Disk cache is equipped
+					with an LRU storage limit. The maximum number of
+					elements allowed in purgatory is configured by the
+					MaxPurgatorySize parameter. By default, the max
+					purgatory size is 5000.
+				</p>
+				<p>
+					Initial testing indicates that the disk cache
+					performs better when the key and purgatory sizes are
+					limited.
+				</p>
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+        ]]>
+				</source>
+				<p>
+					Slots in the data file become empty when items are
+					removed from the disk cache. The indexed disk cache
+					keeps track of empty slots in the data file, so they
+					can be reused. The slot locations are stored in a
+					sorted preferential array -- the recycle bin. The
+					smallest items are removed from the recycle bin when
+					it reaches the specified limit. The
+					MaxRecycleBinSize cannot be larger than the
+					MaxKeySize. If the MaxKeySize is less than 0, the
+					recycle bin will default to 5000.
+				</p>
+				<p>
+					If all the items put on disk are the same size, then
+					the recycle bin will always return perfect matches.
+					However, if the items are of various sizes, the disk
+					cache will use the free spot closest in size but not
+					smaller than the item being written to disk. Since
+					some recycled spots will be larger than the items
+					written to disk, unusable gaps will result.
+					Optimization is intended to remove these gaps.
+				</p>
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=10000
+        ]]>
+				</source>
+				<p>
+					The Disk cache can be configured to defragment the
+					data file at runtime. Since defragmentation is only
+					necessary if items have been removed, the
+					deframentation interval is determined by the number
+					of removes. Currently there is no way to schedule
+					defragmentation to run at a set time. If you set the
+					OptimizeAtRemoveCount to -1, no optimizations of the
+					data file will occur until shutdown. By default the
+					value is -1.
+				</p>
+				<p>
+					In version 1.2.7.9 of JCS, the optimization routine
+					was significantly improved. It now occurs in place,
+					without the aid of a temporary file.
+				</p>
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=30000
+        ]]>
+				</source>
+			</subsection>
+
+			<subsection name="A Complete Configuration Example">
+				<p>
+					In this sample cache.ccf file, I configured the
+					cache to use a disk cache, called DC, by default.
+					Also, I explicitly set a cache region called
+					myRegion1 to use DC. I specified custom settings for
+					all of the Indexed Disk Cache configuration
+					parameters.
+				</p>
+				<source>
+					<![CDATA[
+##############################################################
+##### Default Region Configuration
+jcs.default=DC
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+##############################################################
+##### CACHE REGIONS
+jcs.region.myRegion1=DC
+jcs.region.myRegion1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.myRegion1.cacheattributes.MaxObjects=1000
+jcs.region.myRegion1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+
+##############################################################
+##### AUXILIARY CACHES
+# Indexed Disk Cache
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/indexed-disk-cache
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.OptimizeOnShutdown=true
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=7500
+        ]]>
+				</source>
+			</subsection>
+
+			<subsection name="Using Thread Pools to Reduce Threads">
+				<p>
+					The Indexed Disk Cache allows you to use fewer
+					threads than active regions. By default the disk
+					cache will use the standard cache event queue which
+					has a dedicated thread. Although the standard queue
+					kills its worker thread after a minute of
+					inactivity, you may want to restrict the total
+					number of threads. You can accomplish this by using
+					a pooled event queue.
+				</p>
+				<p>
+					The configuration file below defines a disk cache
+					called DC2. It uses an event queue of type POOLED.
+					The queue is named disk_cache_event_queue. The
+					disk_cache_event_queue is defined in the bottom of
+					the file.
+				</p>
+				<source>
+					<![CDATA[
+##############################################################
+################## DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=DC2
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+
+# Disk Cache Using a Pooled Event Queue -- this allows you
+# to control the maximum number of threads it will use.
+# Each region uses 1 thread by default in the SINGLE model.
+# adding more threads than regions does not help performance.
+# If you want to use a separate pool for each disk cache, either use
+# the single model or define a different auxiliary for each region and use the Pooled type.
+# SINGLE is generally best unless you ahve a huge # of regions.
+jcs.auxiliary.DC2=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC2.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC2.attributes.DiskPath=target/test-sandbox/raf
+jcs.auxiliary.DC2.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC2.attributes.MaxKeySize=10000
+jcs.auxiliary.DC2.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC2.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.OptimizeOnShutdown=true
+jcs.auxiliary.DC2.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC2.attributes.EventQueuePoolName=disk_cache_event_queue
+
+##############################################################
+################## OPTIONAL THREAD POOL CONFIGURATION ########
+
+# Disk Cache Event Queue Pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.remote_cache_client.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+        ]]>
+				</source>
+			</subsection>
+		</section>
+	</body>
+</document>
diff --git a/xdocs/IndexedDiskCacheProperties.xml b/xdocs/IndexedDiskCacheProperties.xml
new file mode 100644
index 0000000..a6de811
--- /dev/null
+++ b/xdocs/IndexedDiskCacheProperties.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Indexed Disk Cache Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="Indexed Disk Auxiliary Cache Configuration">
+			<p> The following properties apply to the Indexed Disk Cache plugin.
+			</p>
+			<subsection name="Indexed Disk Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>DiskPath</td>
+						<td> The directory where the disk cache should write its files.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+					<tr>
+						<td>MaxPurgatorySize</td>
+						<td> The maximum number of items allowed in the queue of items to
+							be written to disk.</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+					<tr>
+						<td>MaxKeySize</td>
+						<td> The maximum number of keys that the indexed disk cache can
+							have. Since the keys are stored in memory, you may want to limit
+							this number to something reasonable. The default is a bit small.
+						</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+					<tr>
+						<td>OptimizeAtRemoveCount</td>
+						<td> At how many removes should the cache try to defragment the
+							data file. Since we recycle empty spots, defragmentation is
+							usually not needed. To prevent the cache from defragmenting the
+							data file, you can set this to -1. This is the default value.
+						</td>
+						<td>N</td>
+						<td>-1</td>
+					</tr>
+					<tr>
+						<td>OptimizeOnShutdown</td>
+						<td> By default the Indexed Disk Cache will optimize on shutdown
+							if the free data size is greater than 0. If you want to prevent
+							this behavior, you can set this parameter to false.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>ClearDiskOnStartup</td>
+						<td> By default the Indexed Disk Cache will use items found on
+							disk on startup. If you set this value to true, the old key and
+							data files will be cleared.</td>
+						<td>N</td>
+						<td>false</td>
+					</tr>
+					<tr>
+						<td>MaxRecycleBinSize</td>
+						<td> The maximum number of empty spots the cache will keep track
+							of. The smallest are removed when the maximum size is reached.
+							Keeping track of empty spots on disk allows us to reuse spots,
+							thereby keeping the file from growing unncessarily.</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+				</table>
+			</subsection>
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/indexed-disk-cache
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.OptimizeOnShutdown=true
+jcs.auxiliary.DC.attributes.ClearDiskOnStartup=false
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=7500
+        ]]>
+				</source>
+			</subsection>
+			<subsection name="Indexed Disk Event Queue Configuration">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>EventQueueType</td>
+						<td> This should be either SINGLE or POOLED. By default the single
+							style pool is used. The single style pool uses a single thread
+							per event queue. That thread is killed whenever the queue is
+							inactive for 30 seconds. Since the disk cache uses an event queue
+							for every region, if you have many regions and they are all
+							active, you will be using many threads. To limit the number of
+							threads, you can configure the disk cache to use the pooled event
+							queue. Using more threads than regions will not add any benefit
+							for the indexed disk cache, since only one thread can read or
+							write at a time for a single region.</td>
+						<td>N</td>
+						<td>SINGLE</td>
+					</tr>
+					<tr>
+						<td>EventQueuePoolName</td>
+						<td> This is the name of the pool to use. It is required if you
+							choose the POOLED event queue type, otherwise it is ignored.</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+				</table>
+			</subsection>
+			<subsection name="Example Configuration Using Thread Pool">
+				<source>
+					<![CDATA[
+jcs.auxiliary.DC=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=target/test-sandbox/indexed-disk-cache
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.OptimizeOnShutdown=true
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=7500
+jcs.auxiliary.DC.attributes.EventQueueType=POOLED
+jcs.auxiliary.DC.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.boundarySize=50
+thread_pool.disk_cache_event_queue.useBoundary=true
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=1
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.startUpSize=1
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+        ]]>
+				</source>
+			</subsection>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/JCSPlugins.xml b/xdocs/JCSPlugins.xml
new file mode 100644
index 0000000..45446d3
--- /dev/null
+++ b/xdocs/JCSPlugins.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>JCS Plugin Overview</title>
+		<author email="ASmuts at therealm.com">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="JCS Plugin Overview">
+			<p> JCS provides multiple auxiliaries which can be plugged into a
+				cache region, in a manner similar to adding Log4j appenders to a
+				logger. JCS auxiliaries are defined in the cache.ccf file. You can
+				specify which plugins a particular cache region should use.</p>
+			<p> There are four types of auxiliaries: (1) memory, (2) disk, (3)
+				lateral, and (4) remote. Each region is required to have one and
+				only one memory auxiliary. No other auxiliaries are required and any
+				possible combination of disk, lateral, and remote auxiliaries is
+				allowed. If you do not want to store items in memory, then the
+				maximum size for the memory caches can be set to 0 on a per region
+				basis.</p>
+		</section>
+		<section name="Memory Plugins">
+			<p> Currently, JCS provides five memory management options: (1)
+				LRUMemoryCache, (2) LHMLRUMemoryCache, (3) MRUMemoryCache, (5)
+				FIFOMemoryCache, and (5) ARCMemoryCache. All memory caches restrict
+				the number of items that can be stored in memory per region. If a
+				disk cache is configured for the region, the items will be spooled
+				to disk when the memory capacity is reached. JCS enforces
+				configurable parameters such as time to live and maximum idle time.
+				Expired elements can be cleaned up by the ShrinkerThread, otherwise
+				they will be removed at the next retrieval attempt or when the
+				capacity is reached.</p>
+			<p> The LRUMemoryCache is the currently recommended plugin. Upon
+				misconfiguration it is used as the default. The LRUMemoryCache
+				removes the least recently used items when the cache is full.</p>
+			<p> The ARCMemoryCache is currently experimental. It implements an
+				adaptive replacement caching algorithm that combines an LRU and an
+				LFU that adapt to usage patterns.</p>
+		</section>
+		<section name="Disk Plugins">
+			<p> JCS provides several disk swap options: indexed disk, HSQL, JISP,
+				and Berkeley DB JE. The IndexedDiskCache is the recommended disk
+				cache. It maintains the cached data on disk and the keys in memory
+				for the fastest possible lookup times. Writing to disk is done
+				asynchronously. Items are typically put in purgatory and queued for
+				background disk writing. While in purgatory, the items remain
+				available.</p>
+			<p> In addition, JCS provides a disk auxiliary that uses the Berkeley
+				DB Java Edition for disk storage. JCS can effectively function as an
+				expiration manager and distribution mechanism on top of a Berkeley
+				DB JE.</p>
+		</section>
+		<section name="Lateral Plugins">
+			<p> JCS provides two recommended lateral distribution options: TCP
+				socket server distribution and JGroups (or JavaGroups). There are
+				also several other experimental lateral distribution auxiliaries
+				using servlets, UDP, and xmlrpc.</p>
+		</section>
+		<section name="Remote Plugins">
+			<p> JCS provides both an RMI and HTTP remote servers to manage
+				distribution of cached data. These can be paired for failover.</p>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/JCSShortDescription.xml b/xdocs/JCSShortDescription.xml
new file mode 100644
index 0000000..957cc42
--- /dev/null
+++ b/xdocs/JCSShortDescription.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Java Caching System</title>
+    <author email="pete at kazmier.com">Pete Kazmier</author>
+    <author email="asmuts at apache.org">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Java Caching System">
+      <p>
+        JCS is a distributed caching system written in java.  It is intended to speed up
+        applications by providing a means to manage cached
+        data of various dynamic natures.  Like any caching system, the
+        JCS is most useful for high read, low put applications.  Dynamic
+        content and reporting systems can benefit most.  However, any
+        site that repeatedly constructs pages, dropdowns, or common
+        search results from a database that is updated at intervals
+        (rather than across categories continuously) can improve
+        performance and scalability by implementing caching. Latency
+        times drop sharply and bottlenecks move away from the database
+        in an effectively cached system.
+      </p>
+      <p>
+        The JCS goes beyond simply caching objects in memory.  It
+        provides several important features, necessary for any
+        Enterprise level caching system:
+      </p>
+      <ul>
+        <li>Memory management</li>
+        <li>Disk overflow (and defragmentation)</li>
+        <li>Thread pool controls</li>
+        <li>Element grouping</li>
+        <li>Quick nested categorical removal</li>
+        <li>Data expiration (idle time and max life)</li>
+        <li>Extensible framework</li>
+        <li>Fully configurable runtime parameters</li>
+        <li>Region data separation and configuration</li>
+        <li>Fine grained element configuration options</li>
+        <li>Remote synchronization</li>
+        <li>Remote store recovery</li>
+        <li>Non-blocking "zombie" (balking facade) pattern</li>
+        <li>Lateral distribution of elements via HTTP, TCP, or
+          UDP</li>
+        <li>UDP Discovery of other caches</li>
+        <li>Element event handling</li>
+        <li>Remote server chaining (or clustering) and failover</li>
+      </ul>
+    </section>
+  </body>
+</document>
+
diff --git a/xdocs/JCSandJCACHE.xml b/xdocs/JCSandJCACHE.xml
new file mode 100644
index 0000000..a13024b
--- /dev/null
+++ b/xdocs/JCSandJCACHE.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>JCS and JCACHE (JSR-107)</title>
+    <author email="pete at kazmier.com">Pete Kazmier</author>
+    <author email="ASmuts at therealm.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="JCACHE (JSR-107)">
+      <p>
+        Since version 2.x, Apache Commons JCS implements
+        JCache specification (and a few more providing some basic utilities
+        in its extras module and a basic integration with apache OpenJPA).
+      </p>
+      <p>
+        Next section is about main differences between JCache design and original JCS one.
+        These are still globally valid and are kept to let you get a bit more food for thoughts
+        on Caching and JCS.
+      </p>
+    </section>
+    <section name="JCS and JCACHE (JSR-107)">
+      <p>
+        The JCS is an attempt to build a system close to JCACHE , <a
+            href="http://jcp.org/jsr/detail/107.jsp">JSR-107</a>, a
+        description of the caching system used in Oracle9i.  JCS grew
+        out of my work over the past two years to build an enterprise
+        level caching system.  Though it is replete with good ideas,
+        there are some aspects of the JCACHE architecture that could
+        lead to inefficiency (ex, the lateral distribution and net
+        searches) and a few programming preferences that I found
+        cumbersome (ex, the use of exceptions to report the common
+        place).  Subsequently there are a few differences between the
+        two systems.  In some cases I have moved my original system
+        closer to the JCACHE model where it presented a better idea.
+        Briefly:
+      </p>
+      <subsection name="Element vs. Region Attributes">
+        <p>
+          My original cache was regionally defined.  Each entry required
+          a very minimal wrapper.  The osc4j specification is an element
+          driven model where each element is fully configurable.  This
+          could lead to a slight performance penalty, but it is a richer
+          model, where elements can inherit or have their own
+          attributes.  So, I converted the entire system into element
+          centered framework.
+        </p>
+      </subsection>
+      <subsection name="Lateral Broadcast vs. Remote Consistency">
+        <p>
+          The oracle model is a laterally distributed framework with no
+          centralized control.  The JCS model has the option for lateral
+          broadcast (which will need to be made more efficient) and a
+          remote store that coordinates consistency. In the JCS Local
+          caches send data to the remote store which then notifies other
+          local caches of changes to "regions" (caches) that are
+          registered.  In JCACHE's lateral model an update is never
+          broadcast from the remote, rather updates come via the lateral
+          caches.  If you broadcast changes to all servers then every
+          server must be ready for every user.  The usage patterns of a
+          user on one box can affect the whole.  Also, the lateral model
+          can make it difficult to synchronize combinations of updates
+          and invalidations.
+        </p>
+        <p>
+          With a remote store the local caches are primed to take on
+          similar patterns by talking to the remote store, but aren't
+          flooded with the elements from another machine.  This
+          significantly cuts down on traffic.  This way each local cache
+          is a relatively separate realm with remotely configurable
+          regions that stay in synch without overriding the user habits
+          of any machine.  It also allows for an efficient mechanism of
+          retrieval, where searching for an element involves, at
+          maximum, only as many steps as there are remote servers in the
+          cluster.  In the lateral model a failed net search could take
+          an extremely long time to complete, making it necessary for
+          the programmer to decide how long of a wait is acceptable.
+        </p>
+        <p>
+          Though this is by and large a poor model, the JCS will include
+          the ability to perform full lateral searches.  A more
+          important feature is remote failover and remote server
+          clustering.  With clustering any concerns about the remote
+          server being a single point of failure vanish and the remote
+          server model is significantly more robust.
+        </p>
+      </subsection>
+      <subsection name="Put vs. Replace">
+        <p>
+          The difference between put and replace is not present in the
+          JCS by default.  The overhead associated with this distinction
+          is tremendous.  However, there will be an alternate "safe-put"
+          method to deal with special caches.
+        </p>
+      </subsection>
+      <subsection name="Nulls vs. Errors">
+        <p>
+          I started to support <code>ObjectNotFoundExceptions</code> for
+          failed gets but the overhead and cumbersome coding needed to
+          surround a simple get method is ridiculous.  Instead the JCS
+          returns null.
+        </p>
+      </subsection>
+      <subsection name="Cache Loaders">
+        <p>
+          I'm not supporting cache loaders at this time.  They seem
+          unnecessary, but may be useful in a smart portal.
+        </p>
+      </subsection>
+      <subsection name="Groups vs. Hierarchy">
+        <p>
+          The JCS provides feature rich grouping mechanism, where groups
+          of elements can be invalidated and whose attributes can be
+          listed.  The grouping feature is much like the session API.
+          In addition, the JCS provides a mechanism for hierarchical
+          removal without the overhead of keeping track of all the
+          elements of a group across machines.  Element keys with
+          "<code>:</code>" separators (a value that will be fully
+          configurable) can be arranged in a hierarchy.  A remove
+          command ending in a "<code>:</code>" will issue a removal of
+          all child elements.  I can associate search and menu drop down
+          lists for a particular company in a multi-company system by
+          starting each key in disparate caches with the company id
+          followed by "<code>:</code>" and then the normal key.
+          Invalidating this data when a change is made to data affecting
+          something falling under that company can be removed by simply
+          calling <code>cacheAccess.remove(comp_id + ":")</code>.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/JCSvsEHCache.xml b/xdocs/JCSvsEHCache.xml
new file mode 100644
index 0000000..cf15ee1
--- /dev/null
+++ b/xdocs/JCSvsEHCache.xml
@@ -0,0 +1,507 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>JCS vs EHCache Performance</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="JCS vs EHCache Memory Performance">
+			<subsection name="Initial Test Results">
+				<p>
+					I just built both EHCache (1.2-beta4) and JCS
+					(1.2.7.0) from head, configured both similarly and
+					ran 20 rounds of 50,000 puts and gets, that is
+					1,000,000 puts and gets in total. Using the default
+					LRU Memory Cache, the same algorithm that EHCache
+					uses by default,
+					<b>
+						JCS proved to be nearly twice as fast as EHCache
+					</b>
+					in multiple trials for both puts and gets. I have
+					the log levels for both set at info. I would like to
+					further verify my results, since they completely
+					contradict the information on the EHCache site.
+				</p>
+				<p>
+					From what I can tell so far, JCS is significantly
+					faster than EHCache when you are retrieving items
+					that exist in the cache and when you are putting
+					items into a cache that has not reached its size
+					limit.
+				</p>
+				<p>
+					Additional testing shows that when the size limit it
+					reached, JCS and EHCache perform similarly for puts
+					and gets. Although JCS gets are significantly faster
+					when the items are present, they are almost exactly
+					the same when the items are not in the cache. My
+					initial tests revealed a less than 1% difference,
+					but subsequent runs showed JCS as 20% faster. More
+					tests are needed before the results are conclusive.
+				</p>
+				<p>
+					Since, neither cache will be a relevant bottleneck
+					in any application where a cache would be useful,
+					the differences in performance may be beside the
+					point. Nevertheless, it is important to note that
+					the EHCache web site provides, what appears to be,
+					false test data.
+				</p>
+				<p>
+					The peculiar result is that a few years back EHCache
+					took the JCS source code, removed most of its
+					features, and ended up with something that performs
+					worse.
+				</p>
+			</subsection>
+
+
+			<subsection name="Test Data">
+				<p>Here is the data from the first test:</p>
+				<p>
+					JCS put time for 50000 = 651; millis per = 0.01302
+					JCS get time for 50000 = 160; millis per = 0.0032
+					EHCache put time for 50000 = 481; millis per =
+					0.00962 EHCache get time for 50000 = 110; millis per
+					= 0.0022
+				</p>
+				<p>
+					JCS put time for 50000 = 240; millis per = 0.0048
+					JCS get time for 50000 = 90; millis per = 0.0018
+					EHCache put time for 50000 = 491; millis per =
+					0.00982 EHCache get time for 50000 = 120; millis per
+					= 0.0024
+				</p>
+				<p>
+					JCS put time for 50000 = 241; millis per = 0.00482
+					JCS get time for 50000 = 80; millis per = 0.0016
+					EHCache put time for 50000 = 551; millis per =
+					0.01102 EHCache get time for 50000 = 110; millis per
+					= 0.0022
+				</p>
+				<p>
+					JCS put time for 50000 = 240; millis per = 0.0048
+					JCS get time for 50000 = 90; millis per = 0.0018
+					EHCache put time for 50000 = 481; millis per =
+					0.00962 EHCache get time for 50000 = 130; millis per
+					= 0.0026
+				</p>
+				<p>
+					JCS put time for 50000 = 230; millis per = 0.0046
+					JCS get time for 50000 = 181; millis per = 0.00362
+					EHCache put time for 50000 = 520; millis per =
+					0.0104 EHCache get time for 50000 = 101; millis per
+					= 0.00202
+				</p>
+				<p>
+					JCS put time for 50000 = 220; millis per = 0.0044
+					JCS get time for 50000 = 90; millis per = 0.0018
+					EHCache put time for 50000 = 641; millis per =
+					0.01282 EHCache get time for 50000 = 110; millis per
+					= 0.0022
+				</p>
+				<p>
+					JCS put time for 50000 = 250; millis per = 0.0050
+					JCS get time for 50000 = 121; millis per = 0.00242
+					EHCache put time for 50000 = 590; millis per =
+					0.0118 EHCache get time for 50000 = 101; millis per
+					= 0.00202
+				</p>
+				<p>
+					JCS put time for 50000 = 260; millis per = 0.0052
+					JCS get time for 50000 = 100; millis per = 0.0020
+					EHCache put time for 50000 = 581; millis per =
+					0.01162 EHCache get time for 50000 = 100; millis per
+					= 0.0020
+				</p>
+				<p>
+					JCS put time for 50000 = 290; millis per = 0.0058
+					JCS get time for 50000 = 121; millis per = 0.00242
+					EHCache put time for 50000 = 570; millis per =
+					0.0114 EHCache get time for 50000 = 121; millis per
+					= 0.00242
+				</p>
+				<p>
+					JCS put time for 50000 = 210; millis per = 0.0042
+					JCS get time for 50000 = 120; millis per = 0.0024
+					EHCache put time for 50000 = 561; millis per =
+					0.01122 EHCache get time for 50000 = 130; millis per
+					= 0.0026
+				</p>
+				<p>
+					JCS put time for 50000 = 250; millis per = 0.0050
+					JCS get time for 50000 = 151; millis per = 0.00302
+					EHCache put time for 50000 = 560; millis per =
+					0.0112 EHCache get time for 50000 = 111; millis per
+					= 0.00222
+				</p>
+				<p>
+					JCS put time for 50000 = 250; millis per = 0.0050
+					JCS get time for 50000 = 100; millis per = 0.0020
+					EHCache put time for 50000 = 711; millis per =
+					0.01422 EHCache get time for 50000 = 100; millis per
+					= 0.0020
+				</p>
+				<p>
+					JCS put time for 50000 = 251; millis per = 0.00502
+					JCS get time for 50000 = 90; millis per = 0.0018
+					EHCache put time for 50000 = 511; millis per =
+					0.01022 EHCache get time for 50000 = 90; millis per
+					= 0.0018
+				</p>
+				<p>
+					JCS put time for 50000 = 220; millis per = 0.0044
+					JCS get time for 50000 = 100; millis per = 0.0020
+					EHCache put time for 50000 = 491; millis per =
+					0.00982 EHCache get time for 50000 = 90; millis per
+					= 0.0018
+				</p>
+				<p>
+					JCS put time for 50000 = 230; millis per = 0.0046
+					JCS get time for 50000 = 80; millis per = 0.0016
+					EHCache put time for 50000 = 201; millis per =
+					0.00402 EHCache get time for 50000 = 390; millis per
+					= 0.0078
+				</p>
+				<p>
+					JCS put time for 50000 = 201; millis per = 0.00402
+					JCS get time for 50000 = 120; millis per = 0.0024
+					EHCache put time for 50000 = 180; millis per =
+					0.0036 EHCache get time for 50000 = 411; millis per
+					= 0.00822
+				</p>
+				<p>
+					JCS put time for 50000 = 210; millis per = 0.0042
+					JCS get time for 50000 = 100; millis per = 0.0020
+					EHCache put time for 50000 = 210; millis per =
+					0.0042 EHCache get time for 50000 = 381; millis per
+					= 0.00762
+				</p>
+				<p>
+					JCS put time for 50000 = 240; millis per = 0.0048
+					JCS get time for 50000 = 90; millis per = 0.0018
+					EHCache put time for 50000 = 211; millis per =
+					0.00422 EHCache get time for 50000 = 410; millis per
+					= 0.0082
+				</p>
+				<p>
+					JCS put time for 50000 = 221; millis per = 0.00442
+					JCS get time for 50000 = 80; millis per = 0.0016
+					EHCache put time for 50000 = 210; millis per =
+					0.0042 EHCache get time for 50000 = 411; millis per
+					= 0.00822
+				</p>
+				<p>
+					JCS put time for 50000 = 220; millis per = 0.0044
+					JCS get time for 50000 = 80; millis per = 0.0016
+					EHCache put time for 50000 = 190; millis per =
+					0.0038 EHCache get time for 50000 = 411; millis per
+					= 0.00822
+				</p>
+				<p>Finished 20 loops of 50000 gets and puts</p>
+				<p>
+					Put average for JCS = 256 Put average for EHCache =
+					447 JCS puts took 0.57270694 times the EHCache , the
+					goal is less than 1.0x
+				</p>
+				<p>
+					Get average for JCS = 107 Get average for EHCache =
+					196 JCS gets took 0.54591835 times the EHCache , the
+					goal is less than 1.0x
+				</p>
+			</subsection>
+
+			<subsection name="A Test Class">
+				<p>Here is the test class:</p>
+
+				<source>
+					<![CDATA[
+package org.apache.commons.jcs;
+
+import junit.framework.TestCase;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.jcs.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.utils.struct.LRUMap;
+
+/**
+ * Compare JCS vs ehcache performance.
+ *
+ * @author Aaron Smuts
+ *
+ */
+public class JCSvsEHCachePerformanceTest
+    extends TestCase
+{
+
+    float ratioPut = 0;
+
+    float ratioGet = 0;
+
+    // the jcs to competitor
+    float target = 1.0f;
+
+    int loops = 20;
+
+    int tries = 50000;
+
+    /**
+     * Compare performance between JCS and EHCache. Fail if JCS is not as fast.
+     * Print the ratio.
+     *
+     * @throws Exception
+     *
+     */
+    public void testJCSvsEHCache()
+        throws Exception
+    {
+
+        Log log = LogFactory.getLog( LRUMap.class );
+        if ( log.isDebugEnabled() )
+        {
+            System.out.println( "The log level must be at info or above for the a performance test." );
+            return;
+        }
+
+        doWork();
+
+        assertTrue( this.ratioPut < target );
+        assertTrue( this.ratioGet < target );
+
+    }
+
+    /**
+     * This runs a series of gets and puts for both JCS and EHCache. The test
+     * will fail if JCS is not faster.
+     *
+     * @throws Exception
+     *
+     */
+    public void doWork()
+        throws Exception
+    {
+
+        int maxSize = 1000000;
+
+        // create the two caches.
+        CacheManager ehMgr = CacheManager.getInstance();
+        // Create an ehcache with a max size of maxSize, no swap, with items
+        // that can expire, with maximum idle time to live of 500 seconds, and
+        // maximum idel time of 500 seconds.
+        Cache eh = new Cache( "testJCSvsEHCache", maxSize, false, false, 500, 500 );
+        ehMgr.addCache( eh );
+
+        // Create a similarly configured JCS that uses the LRU memory cache.
+        // maxSize elements that are not eternal. No disk cache is configured.
+        ICompositeCacheAttributes cattr = new CompositeCacheAttributes();
+        cattr.setMaxObjects( maxSize );
+        CacheAccess<String, String> jcs = JCS.getInstance( "testJCSvsEHCache", cattr );
+
+        // run settings
+        long start = 0;
+        long end = 0;
+        long time = 0;
+        float tPer = 0;
+
+        long putTotalJCS = 0;
+        long getTotalJCS = 0;
+        long putTotalEHCache = 0;
+        long getTotalEHCache = 0;
+
+        String jcsDisplayName = "JCS";
+        String ehCacheDisplayName = "";
+
+        try
+        {
+            for ( int j = 0; j < loops; j++ )
+            {
+
+                jcsDisplayName = "JCS      ";
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    jcs.put( "key:" + i, "data" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out
+                    .println( jcsDisplayName + " put time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    jcs.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalJCS += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out
+                    .println( jcsDisplayName + " get time for " + tries + " = " + time + "; millis per = " + tPer );
+
+                // /////////////////////////////////////////////////////////////
+                ehCacheDisplayName = "EHCache  ";
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    Element ehElm = new Element( "key:" + i, "data" + i );
+
+                    eh.put( ehElm );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                putTotalEHCache += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( ehCacheDisplayName + " put time for " + tries + " = " + time + "; millis per = "
+                    + tPer );
+
+                start = System.currentTimeMillis();
+                for ( int i = 0; i < tries; i++ )
+                {
+                    eh.get( "key:" + i );
+                }
+                end = System.currentTimeMillis();
+                time = end - start;
+                getTotalEHCache += time;
+                tPer = Float.intBitsToFloat( (int) time ) / Float.intBitsToFloat( tries );
+                System.out.println( ehCacheDisplayName + " get time for " + tries + " = " + time + "; millis per = "
+                    + tPer );
+
+                System.out.println( "\n" );
+            }
+
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace( System.out );
+            System.out.println( e );
+        }
+
+        long putAvJCS = putTotalJCS / loops;
+        long getAvJCS = getTotalJCS / loops;
+        long putAvHashtable = putTotalEHCache / loops;
+        long getAvHashtable = getTotalEHCache / loops;
+
+        System.out.println( "Finished " + loops + " loops of " + tries + " gets and puts" );
+
+        System.out.println( "\n" );
+        System.out.println( "Put average for " + jcsDisplayName + "  = " + putAvJCS );
+        System.out.println( "Put average for " + ehCacheDisplayName + " = " + putAvHashtable );
+        ratioPut = Float.intBitsToFloat( (int) putAvJCS ) / Float.intBitsToFloat( (int) putAvHashtable );
+        System.out.println( jcsDisplayName + " puts took " + ratioPut + " times the " + ehCacheDisplayName
+            + ", the goal is <" + target + "x" );
+
+        System.out.println( "\n" );
+        System.out.println( "Get average for  " + jcsDisplayName + "  = " + getAvJCS );
+        System.out.println( "Get average for " + ehCacheDisplayName + " = " + getAvHashtable );
+        ratioGet = Float.intBitsToFloat( (int) getAvJCS ) / Float.intBitsToFloat( (int) getAvHashtable );
+        System.out.println( jcsDisplayName + " gets took " + ratioGet + " times the " + ehCacheDisplayName
+            + ", the goal is <" + target + "x" );
+
+    }
+
+}
+
+        ]]>
+				</source>
+			</subsection>
+		</section>
+
+
+		<section name="JCS vs EHCache Disk Cache">
+			<p>
+				It is very difficult to compare the ehcache disk store
+				and the JCS Indexed Disk Cache.
+			</p>
+			<p>The JCS version is much more sophisticated.</p>
+			<p>
+				JCS puts items into a queue called purgatory. While they
+				are in this queue, they are still accessible. This queue
+				gets worked when items are in it. The number of threads
+				used in the system as a whole for disk caches is
+				configurable using the thread pool configuration options
+				in JCS. I could have 1000 regions and only use 3 threads
+				to work the disk queues. From what I can tell EH will
+				use 1 thread per region. This is worse than the JCS
+				default, which uses a queue that kills its threads when
+				they are not used. . . . and much worse than using JCS
+				with a thread pool.
+			</p>
+			<p>
+				The size of JCS purgatory is configurable, so you can
+				avoid catastrophe if something goes wrong with the queue
+				worker. EH doesn't have any such safety.
+			</p>
+			<p>
+				JCS limits the number of keys that can be kept for the
+				disk cache. EH cannot do this.
+			</p>
+			<p>
+				The ehcache disk version is very simple. It puts an
+				unlimited number of items in a temporary store. You can
+				easily fill this up and run out of memory. You can put
+				items into JCS purgatory faster than they can be gc'd
+				but it is much more difficult. The EH store is then
+				flushed to disk every 200ms. While EH is flushing the
+				entire disk cache blocks!
+			</p>
+			<p>
+				JCS disk cache is based on a continuous spooling model,
+				not a stop the world model like EH. In most cases the EH
+				model will work out, but not if you put a lot of big
+				items on disk at once. If you want an even distribution
+				of disk cache response times, then you should use JCS.
+			</p>
+			<p>
+				The EH disk store also seems to just keep growing. After
+				several tests, the size of the data file was 10 times
+				that of JCS and EH was taking 10 times as long.
+			</p>
+			<p>
+				You can saturate the EH version much more quickly, since
+				it will hold as many items as you can put in in 200 ms.
+			</p>
+			<p>
+				I tried with 100k and JCS could handle it, but EH died
+				with an out of memory exception.
+			</p>
+			<p>
+				EH cache developed its disk store in response to a bug
+				in the JCS version. This bug was fixed a few years ago .
+				. . The nice thing about JCS is that it is completely
+				pluggable. It would take about 30 minutes to plug a
+				different disk cache implementation into JCS if you so
+				pleased . . . .
+			</p>
+		</section>
+
+	</body>
+</document>
diff --git a/xdocs/JDBCDiskCache.xml b/xdocs/JDBCDiskCache.xml
new file mode 100644
index 0000000..e8758c7
--- /dev/null
+++ b/xdocs/JDBCDiskCache.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>JDBC Disk Cache</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="JDBC Disk Auxiliary Cache">
+			<p> The JDBC disk cache uses a relational database such as MySQL as a
+				persistent store. It works with Oracle, MySQL and HSQL. The cache
+				elements are serialized and written into a BLOB. Multiple regions
+				can share a single table. You can define multiple, differently
+				configured JDBC disk caches in one JCS instance. This allows you to
+				use different tables for different cache regions.</p>
+			<p> If you want to use numerous JDBC disk cache instances that talk
+				to the same database, you can configure them to share a connection
+				pool. You might want to use several different tables to partition
+				the data. Some operations, such as index building on a MyISAM
+				storage engine take longer if there are more items in the table.</p>
+			<subsection name="Example #1 cache.ccf (MySQL)">
+				<source>
+					<![CDATA[
+##############################################################
+################## DEFAULT CACHE REGION  #####################
+# sets the default aux value for any non configured caches
+jcs.default=MYSQL,RCluster
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=5000
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTime=7200
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=14400
+jcs.default.elementattributes.IdleTime=14400
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+##############################################################
+################## CACHE REGIONS AVAILABLE ###################
+
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+# MYSQL disk cache used for flight options
+jcs.auxiliary.MYSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.MYSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.MYSQL.attributes.userName=myUsername
+jcs.auxiliary.MYSQL.attributes.password=myPassword
+jcs.auxiliary.MYSQL.attributes.url=jdbc:mysql://localhost:3306/YOURDBNAME?autoReconnect=true
+jcs.auxiliary.MYSQL.attributes.driverClassName=com.mysql.jdbc.Driver
+jcs.auxiliary.MYSQL.attributes.tableName=JCS_STORE
+jcs.auxiliary.MYSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.MYSQL.attributes.maxActive=100
+jcs.auxiliary.MYSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.MYSQL.attributes.UseDiskShrinker=true
+jcs.auxiliary.MYSQL.attributes.ShrinkerInterval=1800
+jcs.auxiliary.MYSQL.attributes.allowRemoveAll=false
+jcs.auxiliary.MYSQL.attributes.EventQueueType=POOLED
+jcs.auxiliary.MYSQL.attributes.EventQueuePoolName=disk_cache_event_queue
+
+##############################################################
+################## OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=true
+thread_pool.disk_cache_event_queue.boundarySize=1000
+thread_pool.disk_cache_event_queue.maximumPoolSize=50
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
+        ]]>
+				</source>
+			</subsection>
+			
+			<subsection name="Example #2 cache.ccf (MySQL)">
+				<p>
+				This example uses two JDBC Disck Cache instances and a shared connection pool.
+				</p>
+				<source>
+					<![CDATA[
+jcs.default=JDBC_0
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# #############################################################
+# ################# CONFIGURED REGIONS ########################
+
+jcs.region.testCache1=JDBC_1
+jcs.region.testCache1.cacheattributes.MaxObjects=10000
+
+# #############################################################
+# ################# AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC_0=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC_0.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC_0.attributes.tableName=JCS_STORE_0
+jcs.auxiliary.JDBC_0.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC_0.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC_0.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC_0.attributes.connectionPoolName=MySharedPool
+jcs.auxiliary.JDBC_0.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC_0.attributes.EventQueuePoolName=disk_cache_event_queue
+
+jcs.auxiliary.JDBC_1=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC_1.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC_1.attributes.tableName=JCS_STORE_1
+jcs.auxiliary.JDBC_1.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC_1.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC_1.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC_1.attributes.connectionPoolName=MySharedPool
+jcs.auxiliary.JDBC_1.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC_1.attributes.EventQueuePoolName=disk_cache_event_queue
+
+# #############################################################
+# ######## OPTIONAL SHARED CONNECTION POOL CONFIGURATION ######
+# My Shared Pool
+
+jcs.jdbcconnectionpool.MySharedPool.attributes.userName=sa
+jcs.jdbcconnectionpool.MySharedPool.attributes.password=
+jcs.jdbcconnectionpool.MySharedPool.attributes.url=jdbc:hsqldb:target/cache_hsql_db
+jcs.jdbcconnectionpool.MySharedPool.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.jdbcconnectionpool.MySharedPool.attributes.maxActive=15
+
+# #############################################################
+# ################# OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
+        ]]>
+				</source>
+			</subsection>			
+			
+			<subsection name="Table Creation Script (MySQL)">
+				<source>
+					<![CDATA[
+drop TABLE JCS_STORE;
+
+CREATE TABLE JCS_STORE
+(
+  CACHE_KEY                   VARCHAR(250)          NOT NULL,
+  REGION                      VARCHAR(250)          NOT NULL,
+  ELEMENT                     BLOB,
+  CREATE_TIME                 DATETIME,
+  CREATE_TIME_SECONDS         BIGINT,
+  MAX_LIFE_SECONDS            BIGINT,
+  SYSTEM_EXPIRE_TIME_SECONDS  BIGINT,
+  IS_ETERNAL                  CHAR(1),
+  PRIMARY KEY (CACHE_KEY, REGION)
+);
+
+alter table JCS_STORE MAX_ROWS = 10000000;
+
+alter table JCS_STORE AVG_ROW_LENGTH = 2100;
+
+create index JCS_STORE_DELETE_IDX on JCS_STORE (SYSTEM_EXPIRE_TIME_SECONDS,IS_ETERNAL,REGION);
+        ]]>
+				</source>
+			</subsection>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/JDBCDiskCacheProperties.xml b/xdocs/JDBCDiskCacheProperties.xml
new file mode 100644
index 0000000..7040f35
--- /dev/null
+++ b/xdocs/JDBCDiskCacheProperties.xml
@@ -0,0 +1,250 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>JDBC Disk Cache Configuration</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="JDBC Disk Auxiliary Cache Configuration">
+
+			<p>
+				The following properties apply to the JDBC Disk Cache
+				plugin.
+			</p>
+
+			<subsection name="JDBC Disk Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>MaxPurgatorySize</td>
+						<td>
+							The maximum number of items allowed in the
+							queue of items to be written to disk.
+						</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+
+					<tr>
+						<td>url</td>
+						<td>
+							The database url. The database name will be
+							added to this value to create the full
+							database url.
+						</td>
+						<td>Y</td>
+						<td></td>
+					</tr>
+
+					<tr>
+						<td>database</td>
+						<td>This is appended to the url.</td>
+						<td>Y</td>
+						<td></td>
+					</tr>
+
+					<tr>
+						<td>driverClassName</td>
+						<td>
+							The class name of the driver to talk to your
+							database.
+						</td>
+						<td>Y</td>
+						<td></td>
+					</tr>
+
+					<tr>
+						<td>tableName</td>
+						<td>The name of the table.</td>
+						<td>N</td>
+						<td>JCS_STORE</td>
+					</tr>
+
+					<tr>
+						<td>testBeforeInsert</td>
+						<td>
+							Should the disk cache do a select before
+							trying to insert new element on update, or
+							should it try to insert and handle the
+							error.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+
+					<tr>
+						<td>maxActive</td>
+						<td>
+							This sets the maximum number of connections
+							allowed.
+						</td>
+						<td>Y</td>
+						<td></td>
+					</tr>
+
+					<tr>
+						<td>allowRemoveAll</td>
+						<td>
+							Should the disk cache honor remove all (i.e.
+							clear) requests. You might set this to false
+							to prevent someone from accidentally
+							clearing out an entire database.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+
+					<tr>
+						<td>UseDiskShrinker</td>
+						<td>
+							Should the disk cache try to delete expired
+							items from the database.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+
+					<tr>
+						<td>ShrinkerInterval</td>
+						<td>How often should the disk shrinker run.</td>
+						<td>N</td>
+						<td>300</td>
+					</tr>
+
+				</table>
+			</subsection>
+
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC.attributes.userName=sa
+jcs.auxiliary.JDBC.attributes.password=
+jcs.auxiliary.JDBC.attributes.url=jdbc:hsqldb:
+jcs.auxiliary.JDBC.attributes.database=target/cache_hsql_db
+jcs.auxiliary.JDBC.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.JDBC.attributes.tableName=JCS_STORE2
+jcs.auxiliary.JDBC.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC.attributes.maxActive=15
+jcs.auxiliary.JDBC.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC.attributes.UseDiskShrinker=true
+jcs.auxiliary.JDBC.attributes.ShrinkerInterval=300
+        ]]>
+				</source>
+			</subsection>
+
+			<subsection name="JDBC Disk Event Queue Configuration">
+
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>EventQueueType</td>
+						<td>
+							This should be either SINGLE or POOLED. By
+							default the single style pool is used. The
+							single style pool uses a single thread per
+							event queue. That thread is killed whenever
+							the queue is inactive for 30 seconds. Since
+							the disk cache uses an event queue for every
+							region, if you have many regions and they
+							are all active, you will be using many
+							threads. To limit the number of threads, you
+							can configure the disk cache to use the
+							pooled event queue. Using more threads than
+							regions will not add any benefit for the
+							indexed disk cache, since only one thread
+							can read or write at a time for a single
+							region.
+						</td>
+						<td>N</td>
+						<td>SINGLE</td>
+					</tr>
+					<tr>
+						<td>EventQueuePoolName</td>
+						<td>
+							This is the name of the pool to use. It is
+							required if you choose the POOLED event
+							queue type, otherwise it is ignored.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection
+				name="Example Configuration Using Thread Pool">
+				<source>
+					<![CDATA[
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+# JDBC disk cache
+jcs.auxiliary.JDBC=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory
+jcs.auxiliary.JDBC.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes
+jcs.auxiliary.JDBC.attributes.userName=sa
+jcs.auxiliary.JDBC.attributes.password=
+jcs.auxiliary.JDBC.attributes.url=jdbc:hsqldb:
+jcs.auxiliary.JDBC.attributes.database=target/cache_hsql_db
+jcs.auxiliary.JDBC.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.JDBC.attributes.tableName=JCS_STORE2
+jcs.auxiliary.JDBC.attributes.testBeforeInsert=false
+jcs.auxiliary.JDBC.attributes.maxActive=15
+jcs.auxiliary.JDBC.attributes.allowRemoveAll=true
+jcs.auxiliary.JDBC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.JDBC.attributes.UseDiskShrinker=true
+jcs.auxiliary.JDBC.attributes.ShrinkerInterval=300
+jcs.auxiliary.JDBC.attributes.EventQueueType=POOLED
+jcs.auxiliary.JDBC.attributes.EventQueuePoolName=disk_cache_event_queueue
+
+##############################################################
+################## OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
+        ]]>
+				</source>
+			</subsection>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/LateralJavaGroupsAuxCache.xml b/xdocs/LateralJavaGroupsAuxCache.xml
new file mode 100644
index 0000000..988bc6a
--- /dev/null
+++ b/xdocs/LateralJavaGroupsAuxCache.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Lateral JGroups Auxiliary Cache</title>
+    <author email="ASmuts at apache.org">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Lateral JGroups Auxiliary Cache">
+      <p>
+        The Lateral JGroups Auxiliary Cache is an optional plug in for
+        JCS.  It is primarily intended to broadcast puts and removals to
+        other local caches, though it can also get cached objects.  It uses JGroups
+        for distribution.
+      </p>
+      <p>
+		The Lateral Lateral JGroups Auxiliary Cache is far slower than
+		that Lateral TCP Auxiliary Cache.  Since the Lateral TCP Auxiliary
+		is faster and has UDP discovery built in, the TCP auxiliary is the recommended form of lateral
+		distribution.  However, the JGroups Auxiliary requires fewer socket connections than
+		the TCP lateral.
+      </p>
+      <p>
+		A functional configuration example is below:
+      </p>
+
+        <source><![CDATA[
+# Lateral JavaGroups Distribution
+jcs.auxiliary.LJG=org.apache.commons.jcs.auxiliary.lateral.LateralCacheFactory
+jcs.auxiliary.LJG.attributes=org.apache.commons.jcs.auxiliary.lateral.LateralCacheAttributes
+jcs.auxiliary.LJG.attributes.TransmissionTypeName=JAVAGROUPS
+jcs.auxiliary.LJG.attributes.PutOnlyMode=true
+jcs.auxiliary.LJG.attributes.JGChannelProperties=UDP(mcast_addr=224.0.0.100;mcast_port=7501):PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:QUEUE
+        ]]></source>
+
+
+    </section>
+  </body>
+</document>
diff --git a/xdocs/LateralTCPAuxCache.xml b/xdocs/LateralTCPAuxCache.xml
new file mode 100644
index 0000000..ba5a4c5
--- /dev/null
+++ b/xdocs/LateralTCPAuxCache.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Lateral TCP Auxiliary Cache</title>
+    <author email="pete at kazmier.com">Pete Kazmier</author>
+    <author email="ASmuts at therealm.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Lateral TCP Auxiliary Cache">
+      <p>
+        The TCP Lateral Auxiliary Cache is an optional plug in for the
+        JCS.  It is primarily intended to broadcast puts and removals to
+        other local caches, though it can also get cached objects.  It
+        functions by opening up a <code>SocketServer</code> that
+        listens to a configurable port and by creating
+        <code>Socket</code> connections with other local cache
+        <code>SocketServers</code>.  It can be configured to connect to
+        any number of servers.
+      </p>
+      <p>
+        If there is an error connecting to another server or if an error
+        occurs in transmission, it will move into a recovery mode.  In
+        recovery mode the TCP Lateral Auxiliary Cache will continue to
+        communicate with healthy servers while it tries to restore the
+        connection with the server that is in error.
+      </p>
+      <p>
+        The cache hub communicates with a facade that implements a
+        zombie pattern (balking facade) to prevent blocking.  Puts and
+        removals are queued and occur synchronously in the background.
+        Get requests are synchronous and can potentially block for a
+        configurable interval if there is a communication problem.
+      </p>
+      <subsection name="Non-UDP Discovery Configuration">
+        <p>
+          The configuration is fairly straightforward and is done in the
+          auxiliary cache section of the <code>cache.ccf</code>
+          configuration file.  In the example below, I created a TCP
+          Lateral Auxiliary Cache referenced by <code>LTCP</code>.  It
+          connects to two servers defined in a comma separated list in
+          the <code>TcpServers</code> attribute.  It listens to port
+          <code>1110</code> and does <code>AllowGet</code>.
+			Setting <code>AllowGet</code>
+          equal to <code>false</code> would cause the auxiliary cache to
+          return <code>null</code> from any get request.  In most cases this
+          attribute should be set to <code>false</code>, since if the
+          lateral caches were properly configured, the elements in one
+          would be present in all.
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpServers=localhost:1111,localhost:1112
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP.attributes.AllowGet=true
+        ]]></source>
+        <p>
+		  A mostly configurationless mode is available for the TCP
+		  lateral cache if you use the <a href="LateralUDPDiscovery.html">UDP Discovery</a>
+		   mechanism.
+        </p>
+      </subsection>
+      <subsection name="Send Only Configuration">
+        <p>
+          You can configure the TCP lateral cache to operate
+          in send only mode by setting the <code>Receive</code> attribute
+          to false.  By default the receive attribute is true.
+          When it is set to false, the lateral cache will not
+          establish a socket server.
+        </p>
+        <p>
+           Setting receive to false allows you to broadcast puts
+           and removes, but not receive any.  This is useful for
+           nodes of an application that produce data, but are not
+           involved in data retrieval.
+        </p>
+        <p>
+          The configuration below is the same as above, except the
+          <code>Receive</code> attribute is set to false.  It also uses UDP
+          discovery to find the servers, rather than listing them in the
+          servers attribute.
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+#jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1118
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=6780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+jcs.auxiliary.LTCP.attributes.Receive=true
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=false
+jcs.auxiliary.LTCP.attributes.FilterRemoveByHashCode=false
+        ]]></source>
+      </subsection>
+
+      <subsection name="Potential Issues">
+        <p>
+          The TCP Lateral Auxiliary Cache can provide a high level of
+          consistency but it does not guarantee consistency between
+          caches.  A put for the same object could be issued in two
+          different local caches. Since the transmission is queued, a
+          situation could occur where the item put last in one cache is
+          overridden by a put request from another local cache.  The two
+          local caches could potentially have different versions of the
+          same item.  Like most caches, this is intended for high get
+          and low put utilization, and this occurrence would hint at
+          improper usage.  The RMI Remote cache makes this situation a
+          bit less likely to occur, since the default behavior is to
+          remove local copies on put operations.  If either local cache
+          needed the item put in the above situation, it would have to
+          go remote to retrieve it.  Both local copies would have been
+          expired and would end up using the same version, though it is
+          possible that the version stored remotely would not be the
+          last version created.  The OCS4J tries to implement a locking
+          system to prevent this from occurring, but the locking system
+          itself could suffer from similar problems (when granting locks
+          from two roughly simultaneous lock requests) and it would
+          create a significant burden on all the caches involved.  Since
+          this situation would be extremely rare and is nearly
+          impossible to solve practically, for now JCS will not offer
+          any type of locking.
+        </p>
+      </subsection>
+      <subsection name="Recent">
+        <p>
+          I added a <code>IssueRemoveOnPut</code> attribute that
+          causes the lateral cache to remove an element from the
+          cache rather than inserting it when a put.  This allows the local caches to
+          dictate their own memory usage pattern.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/LateralTCPProperties.xml b/xdocs/LateralTCPProperties.xml
new file mode 100644
index 0000000..9444d8e
--- /dev/null
+++ b/xdocs/LateralTCPProperties.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Lateral TCP Auxiliary Cache Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="Lateral TCP Auxiliary Cache Configuration">
+			<p> The following properties apply to the TCP Lateral Cache plugin.
+			</p>
+			<subsection name="TCP Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>TcpServers</td>
+						<td> This is the list of servers this cache should try to connect
+							to. With UDP discovery this is not necessary.</td>
+						<td>N</td>
+						<td>none</td>
+					</tr>
+					<tr>
+						<td>TcpListenerPort</td>
+						<td> This is the port this cache should listen on.</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+					<tr>
+						<td>AllowGet</td>
+						<td> Should this cache be allowed to get from other laterals.
+							False means that it can only put, i.e. send updates and remove
+							requests to other laterals. Lateral gets are not recommended for
+							performance reasons. This used to be controlled by the attribute
+							PutOnlyMode.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>Receive</td>
+						<td> Should this cache receive or only send to other laterals. You
+							may want to set receive to false if you just need to broadcast to
+							other caches. If you have a feed data parser, that doesn't need
+							to receive updates, but you do want it to send invalidation
+							messages, then you would set receive to false. If receive is
+							false, the discovery service, if enabled, will only listen.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>IssueRemoveOnPut</td>
+						<td> If this is set to true, then the lateral client will send a
+							remove command rather than a put command to any registered
+							listeners.</td>
+						<td>N</td>
+						<td>false</td>
+					</tr>
+					<tr>
+						<td>FilterRemoveByHashCode</td>
+						<td> If this is true, and IssueRemoveOnPut is true, the client
+							will include the hashCode of the element to remove. If it is also
+							true on the receiving end, the receiver will check to see if the
+							element exists. If the element exists, and the hashCodes are the
+							same, the item will not be removed.</td>
+						<td>N</td>
+						<td>false</td>
+					</tr>
+					<tr>
+						<td>SocketTimeOut</td>
+						<td> This allows you to set the socket (read) timeout.</td>
+						<td>N</td>
+						<td>1000</td>
+					</tr>
+					<tr>
+						<td>OpenTimeOut</td>
+						<td> This allows you to set the socket open timeout.</td>
+						<td>N</td>
+						<td>2000</td>
+					</tr>
+					<tr>
+						<td>UdpDiscoveryAddr</td>
+						<td> The address the UDP discovery process should broadcast
+							messages to.</td>
+						<td>N</td>
+						<td>228.5.6.7</td>
+					</tr>
+					<tr>
+						<td>UdpDiscoveryPort</td>
+						<td> The port the UDP discovery process should send messages to.
+						</td>
+						<td>N</td>
+						<td>6789</td>
+					</tr>
+					<tr>
+						<td>UdpDiscoveryEnabled</td>
+						<td> Whether or not the UDP discovery service should be used to
+							locate other lateral caches.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>ZombieQueueMaxSize</td>
+						<td> The number of elements the zombie queue will hold. This queue
+							is used to store events if we lose our connection with the
+							server.</td>
+						<td>N</td>
+						<td>1000</td>
+					</tr>
+				</table>
+			</subsection>
+			<subsection name="Example Configuration">
+				<source><![CDATA[
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+#jcs.auxiliary.LTCP.attributes.TcpServers=
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1118
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=6780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+jcs.auxiliary.LTCP.attributes.Receive=true
+jcs.auxiliary.LTCP.attributes.AllowGet=false
+jcs.auxiliary.LTCP.attributes.IssueRemoveOnPut=false
+jcs.auxiliary.LTCP.attributes.FilterRemoveByHashCode=false
+jcs.auxiliary.LTCP.attributes.SocketTimeoOt=1001
+jcs.auxiliary.LTCP.attributes.OpenTimeOut=2002
+jcs.auxiliary.LTCP.attributes.ZombieQueueMaxSize=2000
+        ]]></source>
+			</subsection>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/LateralUDPDiscovery.xml b/xdocs/LateralUDPDiscovery.xml
new file mode 100644
index 0000000..cf41eee
--- /dev/null
+++ b/xdocs/LateralUDPDiscovery.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Lateral UDP Discovery</title>
+    <author email="asmuts at apache.org">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Lateral UDP Discovery">
+      <p>
+		Rather than list all the other lateral servers in the configuration
+		file, you can configure the TCP lateral to use UDP
+		discovery.  In discovery mode, lateral TCP caches will broadcast
+		to a multicast address and port, letting all listeners know where they are.
+  	  </p>
+	  <p>
+		On startup each lateral will issue a special message requesting a
+		broadcast from the other caches.  Normal broadcasts occur every
+		30 seconds. (This is to be made configurable.)  Regions that don't
+		receive, are running in send only mode, don't broadcast anything but requests.
+  	  </p>
+      <p>
+	    When a lateral receives a discovery message it will try to add
+	    the lateral to the nowait facade for the region.  If it already exists
+	    nothing happens.  If a region is not configured to send laterally, nothing
+		happens,  since it doesn't have a no wait.
+  	  </p>
+  	  <p>
+  		This allows you to have the same configuration on every machine.
+      </p>
+      <subsection name="Configuration">
+        <p>
+          The configuration is fairly straightforward and is done in the
+          auxiliary cache section of the <code>cache.ccf</code>
+          configuration file.  In the example below, I created a TCP
+          Lateral Auxiliary Cache referenced by <code>LTCP</code>.  It uses
+          UDP Discovery to locate other servers.  It broadcasts to
+          multicast address <code>228.5.6.8</code> and port <code>6780</code>.
+          It listens to port <code>1110</code>.
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.LTCP=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.LateralTCPCacheFactory
+jcs.auxiliary.LTCP.attributes=org.apache.commons.jcs.auxiliary.lateral.socket.tcp.TCPLateralCacheAttributes
+jcs.auxiliary.LTCP.attributes.TcpListenerPort=1110
+jcs.auxiliary.LTCP.attributes.PutOnlyMode=true
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryAddr=228.5.6.8
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryPort=6780
+jcs.auxiliary.LTCP.attributes.UdpDiscoveryEnabled=true
+        ]]></source>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/LocalCacheConfig.xml b/xdocs/LocalCacheConfig.xml
new file mode 100644
index 0000000..1988b6e
--- /dev/null
+++ b/xdocs/LocalCacheConfig.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Configuring the Local Cache</title>
+    <author email="pete at kazmier.com">Pete Kazmier</author>
+    <author email="ASmuts at therealm.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Configuring the Local Cache">
+      <p>
+        This document is intended to provide various answers to
+        questions regarding the configuration of a local cache.  The
+        document is presented in a question / answer format.
+      </p>
+      <subsection name="Where is the configuration information?">
+        <p>
+          Configuration of local caches involves editing the cache
+          configuration file, named <code>cache.ccf</code>.  The
+          classpath should include the directory where this file is
+          located or the file should be placed at the root of the
+          classpath, since it is discovered automatically.
+        </p>
+      </subsection>
+      <subsection name="What is in the cache.ccf file?">
+        <p>
+          The <code>cache.ccf</code> file contains default configuration
+          information for cache regions and specific configuration
+          information for regions that you predefine.  Regions not using
+          default behaviors should generally be configured via the
+          <code>cache.ccf</code> file.  If you can put configuration
+          information in a class, you can edit a props file just as
+          easily.  This makes modification of the regional setting more
+          efficient and allows for startup error checking.
+        </p>
+        <p>
+          There are three main sections of the <code>cache.ccf</code>
+          file:
+        </p>
+        <ul>
+          <li> the default and system settings </li>
+          <li> the region specific settings </li>
+          <li> the auxiliary cache definitions </li>
+        </ul>
+      </subsection>
+      <subsection name="How do I set up default values for regions?">
+        <p>
+          You can establish default values that any non-preconfigured
+          region will inherit.  The non-predefined region will be
+          created when you call
+          <code>CacheAccess.getAccess("cacheName")</code>.  The default
+          setting look like this:
+        </p>
+        <source><![CDATA[
+# DEFAULT CACHE REGION
+
+# sets the default aux value for any non configured caches
+jcs.default=DC,RFailover
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+        ]]></source>
+        <p>
+          The most important line is
+          <code>jcs.default=DC,Rfailover</code>. This tells the cache
+          what auxiliary caches should be used.  Auxiliary caches are
+          configured in the third section of the <code>cache.ccf</code>
+          and are referenced in a comma separated list.  You can add as
+          many auxiliary caches as you want, but the behavior of remote
+          and lateral auxiliaries may conflict.  This allows you to
+          define different configurations for auxiliary caches and to
+          use these different configurations for different regions.
+        </p>
+      </subsection>
+      <subsection name="How do I define a region?">
+        <p>
+          Defining a region involves specifying which auxiliary caches
+          it will use and how many objects it will store in memory.  A
+          typical region definition looks like:
+        </p>
+        <source><![CDATA[
+jcs.region.testCache=DC,RFailover
+jcs.region.testCache.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache.cacheattributes.MaxObjects=1000
+        ]]></source>
+        <p>
+          The region name is <code>testCache</code>.  It will have a
+          1000 item memory limit and will use the DC and RFailover
+          auxiliary caches.  If a typical element for this region was
+          very large, you might want to lower the number of items stored
+          in memory.  The size of the memory storage is dependent on the
+          priority of the cache, the size of its elements, and the
+          amount of RAM on the machine.
+        </p>
+      </subsection>
+      <subsection name="How do I configure an auxiliary cache?">
+        <p>
+          Each auxiliary cache is created through a factory that passes
+          an attribute object to the constructor.  The attributes are
+          set via reflection and should be fairly simple to understand.
+          Each auxiliary cache will be fully documented.  Plugging in
+          your own auxiliary cache become a simple matter given the
+          reflexive manner of initialization.
+        </p>
+        <p>
+          The most important settings for common usage are the disk path
+          and the remote cache location.  It is recommended that only
+          disk and remote auxiliaries be used.  The lateral caches are
+          functional but not as efficient.
+        </p>
+        <p>
+          The default configuration code above specifies that
+          non-preconfigured caches use the auxiliary cache by the name
+          DC.  This cache is defined in the third section of the file:
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.DiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.DiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=c:/dev/cache/raf
+        ]]></source>
+        <p>
+          The only thing that needs to be set here is the
+          <code>DiskPath</code> value.  Change it to wherever you want
+          the cache to persist unused items.
+        </p>
+        <p>
+          The default region is also set to use an auxiliary called
+          <code>RFailover</code>.  This is a remote cache that is
+          designed to failover to other remote servers in a cluster:
+        </p>
+        <source><![CDATA[
+jcs.auxiliary.RFailover=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=
+    localhost:1102,localhost:1101
+        ]]></source>
+        <p>
+          If you don't have more than one remote server running, just
+          specify it by itself in the <code>FailoverServers</code>
+          attribute.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/MySQLDiskCacheProperties.xml b/xdocs/MySQLDiskCacheProperties.xml
new file mode 100644
index 0000000..5815695
--- /dev/null
+++ b/xdocs/MySQLDiskCacheProperties.xml
@@ -0,0 +1,195 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>MySQL Disk Cache Configuration</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="MySQL Disk Auxiliary Cache Configuration">
+
+			<p>
+				The MySQL Disk Cache uses all of the JDBC Disk Cache
+				properties. It adds a few of its own. The following
+				properties only apply to the MySQL Disk Cache plugin.
+			</p>
+
+			<subsection name="MySQL Disk Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>optimizationSchedule</td>
+						<td>
+							For now this is a simple comma delimited
+							list of HH:MM:SS times to optimize the
+							table. If none is supplied, then no
+							optimizations will be performed.
+
+							In the future we can add a cron like
+							scheduling system. This was created to meet
+							a pressing need to optimize fragmented
+							MyISAM tables. When the table becomes
+							fragmented, it starts to take a long time to
+							run the shrinker that deletes expired
+							elements.
+
+							Setting the value to "03:01,15:00" will
+							cause the optimizer to run at 3 am and at 3
+							pm.
+						</td>
+						<td>N</td>
+						<td>null</td>
+					</tr>
+
+					<tr>
+						<td>balkDuringOptimization</td>
+						<td>
+							If this is true, then when JCS is optimizing
+							the table it will return null from get
+							requests and do nothing for put requests.
+
+							If you are using the remote cache and have a
+							failover server configured in a remote cache
+							cluster, and you allow clustered gets, the
+							primary server will act as a proxy to the
+							failover. This way, optimization should have
+							no impact for clients of the remote cache.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+
+				</table>
+			</subsection>
+
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+# MYSQL disk cache
+jcs.auxiliary.MYSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheFactory
+jcs.auxiliary.MYSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheAttributes
+jcs.auxiliary.MYSQL.attributes.userName=sa
+jcs.auxiliary.MYSQL.attributes.password=
+jcs.auxiliary.MYSQL.attributes.url=jdbc:hsqldb:target/cache_hsql_db
+jcs.auxiliary.MYSQL.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.MYSQL.attributes.tableName=JCS_STORE_MYSQL
+jcs.auxiliary.MYSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.MYSQL.attributes.maxActive=15
+jcs.auxiliary.MYSQL.attributes.allowRemoveAll=true
+jcs.auxiliary.MYSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.MYSQL.attributes.optimizationSchedule=12:34:56,02:34:54
+jcs.auxiliary.MYSQL.attributes.balkDuringOptimization=true
+        ]]>
+				</source>
+			</subsection>
+
+			<subsection name="MySQL Disk Event Queue Configuration">
+
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>EventQueueType</td>
+						<td>
+							This should be either SINGLE or POOLED. By
+							default the single style pool is used. The
+							single style pool uses a single thread per
+							event queue. That thread is killed whenever
+							the queue is inactive for 30 seconds. Since
+							the disk cache uses an event queue for every
+							region, if you have many regions and they
+							are all active, you will be using many
+							threads. To limit the number of threads, you
+							can configure the disk cache to use the
+							pooled event queue. Using more threads than
+							regions will not add any benefit for the
+							indexed disk cache, since only one thread
+							can read or write at a time for a single
+							region.
+						</td>
+						<td>N</td>
+						<td>SINGLE</td>
+					</tr>
+					<tr>
+						<td>EventQueuePoolName</td>
+						<td>
+							This is the name of the pool to use. It is
+							required if you choose the POOLED event
+							queue type, otherwise it is ignored.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection
+				name="Example Configuration Using Thread Pool">
+				<source>
+					<![CDATA[
+##############################################################
+################## AUXILIARY CACHES AVAILABLE ################
+# MYSQL disk cache
+jcs.auxiliary.MYSQL=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheFactory
+jcs.auxiliary.MYSQL.attributes=org.apache.commons.jcs.auxiliary.disk.jdbc.mysql.MySQLDiskCacheAttributes
+jcs.auxiliary.MYSQL.attributes.userName=sa
+jcs.auxiliary.MYSQL.attributes.password=
+jcs.auxiliary.MYSQL.attributes.url=jdbc:hsqldb:target/cache_hsql_db
+jcs.auxiliary.MYSQL.attributes.driverClassName=org.hsqldb.jdbcDriver
+jcs.auxiliary.MYSQL.attributes.tableName=JCS_STORE_MYSQL
+jcs.auxiliary.MYSQL.attributes.testBeforeInsert=false
+jcs.auxiliary.MYSQL.attributes.maxActive=15
+jcs.auxiliary.MYSQL.attributes.allowRemoveAll=true
+jcs.auxiliary.MYSQL.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.MYSQL.attributes.optimizationSchedule=12:34:56,02:34:54
+jcs.auxiliary.MYSQL.attributes.balkDuringOptimization=true
+jcs.auxiliary.MYSQL.attributes.EventQueueType=POOLED
+jcs.auxiliary.MYSQL.attributes.EventQueuePoolName=disk_cache_event_queue
+
+##############################################################
+################## OPTIONAL THREAD POOL CONFIGURATION #########
+# Disk Cache pool
+thread_pool.disk_cache_event_queue.useBoundary=false
+thread_pool.disk_cache_event_queue.boundarySize=500
+thread_pool.disk_cache_event_queue.maximumPoolSize=15
+thread_pool.disk_cache_event_queue.minimumPoolSize=10
+thread_pool.disk_cache_event_queue.keepAliveTime=3500
+thread_pool.disk_cache_event_queue.whenBlockedPolicy=RUN
+thread_pool.disk_cache_event_queue.startUpSize=10
+        ]]>
+				</source>
+			</subsection>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/ProjectHistory.xml b/xdocs/ProjectHistory.xml
new file mode 100644
index 0000000..e849d6b
--- /dev/null
+++ b/xdocs/ProjectHistory.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Project History</title>
+    <author email="rmannibucau at apache.org">Romain Manni-Bucau</author>
+  </properties>
+
+  <body>
+    <section name="Project History">
+
+      <p>
+        Project was created in 2002. It was first released under
+        maven coordinates org.apache.jcs:jcs[:1.3].
+      </p>
+
+      <p>
+        Since 2014 and its version 2 it is released under coordinates
+        org.apache.commons:commons-jcs-[core|jcache|*][:2.x].
+      </p>
+
+    </section>
+  </body>
+</document>
diff --git a/xdocs/RegionProperties.xml b/xdocs/RegionProperties.xml
new file mode 100644
index 0000000..bba48ea
--- /dev/null
+++ b/xdocs/RegionProperties.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Cache Region Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Cache Region Configuration">
+
+			<p>
+				The following properties apply to any cache region. They
+				can be specified as default values and specified on a
+				region by region basis. There are three types of
+				settings: auxiliary, cache, and element. The cache
+				settings define the memory management for the region.
+				The element settings define default element behavior
+				within the region.
+			</p>
+
+			<subsection name="Region (Auxiliary) Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td></td>
+						<td>
+							You can specify the list of auxiliaries that
+							regions can use. This has no attribute name.
+							The list can be empty, otherwise it should
+							be comma delimited.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection name="Region (Cache) Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>MaxObjects</td>
+						<td>
+							The maximum number of items allowed in
+							memory. Eviction of elements in excess of
+							this number is determined by the memory
+							cache. By default JCS uses the LRU memory
+							cache.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+					<tr>
+						<td>MemoryCacheName</td>
+						<td>
+							This property allows you to specify what
+							memory manager you would like to use. You
+							can create your own memory manager by
+							implementing the
+							org.apache.commons.jcs.engine.memory.MemoryCache
+							interface. Alternatively, you can extend the
+							org.apache.commons.jcs.engine.memory.AbstractMemoryCache
+							class. Several different memory caches are
+							available: two LRU implementations, an LFU,
+							and an adaptive replacement algorithm.
+						</td>
+						<td>N</td>
+						<td>
+							org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+						</td>
+					</tr>
+					<tr>
+						<td>UseMemoryShrinker</td>
+						<td>
+							By default, the memory shrinker is shared by
+							all regions that use the LRU memory cache.
+							The memory shrinker iterates through the
+							items in memory, looking for items that have
+							expired or that have exceeded their max
+							memory idle time.
+						</td>
+						<td>N</td>
+						<td>false</td>
+					</tr>
+					<tr>
+						<td>MaxMemoryIdleTime</td>
+						<td>
+							This is only used if you are using the
+							memory shrinker. If this value is set above
+							-1, then if an item has not been accessed in
+							this number of seconds, it will be spooled
+							to disk if the disk is available. You can
+							register an event handler on this event.
+						</td>
+						<td>N</td>
+						<td>7200</td>
+					</tr>
+					<tr>
+						<td>ShrinkerInterval</td>
+						<td>
+							This specifies how often the shrinker should
+							run, if it has been activated. If you set
+							UseMemoryShrinker to false, then this
+							setting has no effect.
+						</td>
+						<td>N</td>
+						<td>60</td>
+					</tr>
+					<tr>
+						<td>DiskUsagePatternName</td>
+						<td>
+							SWAP is the default. Under the swap pattern,
+							data is only put to disk when the max memory
+							size is reached. Since items puled from disk
+							are put into memory, if the memory cache is
+							full and you get an item off disk, the lest
+							recently used item will be spooled to disk.
+							If you have a low memory hit ration, you end
+							up thrashing.
+
+							The UPDATE usage pattern allows items to go
+							to disk on an update. It disables the swap.
+							This allows you to persist all items to
+							disk. If you are using the JDBC disk cache
+							for instance, you can put all the items on
+							disk while using the memory cache for
+							performance, and not worry about losing
+							data from a system crash or improper
+							shutdown. Also, since all items are on disk,
+							there is no need to swap to disk. This
+							prevents the possibility of thrashing.
+						</td>
+						<td>N</td>
+						<td>SWAP</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection name="Region (Element) Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>IsEternal</td>
+						<td>
+							If an element is specified as eternal, then
+							it will never be subject to removal for
+							exceeding its max life.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>MaxLife</td>
+						<td>
+							If you specify that elements within a region
+							are not eternal, then you can set the max
+							life seconds. If this is exceeded the
+							elements will be removed passively when a
+							client tries to retrieve them. If you are
+							using a memory shrinker, then the items can
+							be removed actively.
+						</td>
+						<td>N</td>
+						<td>-1</td>
+					</tr>
+					<tr>
+						<td>IsSpool</td>
+						<td>
+							By default, can elements in this region be
+							sent to a disk cache if one is available.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+                        <td>IsLateral</td>
+						<td>
+							By default, can elements in this region be
+							sent to a lateral cache if one is available.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+                        <td>IsRemote</td>
+						<td>
+							By default, can elements in this region be
+							sent to a remote cache if one is available.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+jcs.default=
+jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=200001
+jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=true
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=700
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# optional region "testCache1" specific configuration settings
+jcs.region.testCache1=
+jcs.region.testCache1.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=123456
+jcs.region.testCache1.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=true
+jcs.region.testCache1.cacheattributes.ShrinkerInterval=30
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTime=300
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=100
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+jcs.region.testCache1.elementattributes.MaxLife=60000
+jcs.region.testCache1.elementattributes.IsSpool=true
+jcs.region.testCache1.elementattributes.IsLateral=true
+jcs.region.testCache1.elementattributes.IsRemote=true
+        ]]>
+				</source>
+			</subsection>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/RemoteAuxCache.xml b/xdocs/RemoteAuxCache.xml
new file mode 100644
index 0000000..b63aadd
--- /dev/null
+++ b/xdocs/RemoteAuxCache.xml
@@ -0,0 +1,356 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Remote Auxiliary Cache Client / Server</title>
+		<author email="pete at kazmier.com">Pete Kazmier</author>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Remote Auxiliary Cache Client / Server">
+			<p>
+				The Remote Auxiliary Cache is an optional plug in for
+				JCS. It is intended for use in multi-tiered systems to
+				maintain cache consistency. It uses a highly reliable
+				RMI client server framework that currently allows for
+				any number of clients. Using a listener id allows
+				multiple clients running on the same machine to connect
+				to the remote cache server. All cache regions on one
+				client share a listener per auxiliary, but register
+				separately. This minimizes the number of connections
+				necessary and still avoids unnecessary updates for
+				regions that are not configured to use the remote cache.
+			</p>
+			<p>
+				Local remote cache clients connect to the remote cache
+				on a configurable port and register a listener to
+				receive cache update callbacks at a configurable port.
+			</p>
+			<p>
+				If there is an error connecting to the remote server or
+				if an error occurs in transmission, the client will
+				retry for a configurable number of tries before moving
+				into a failover-recovery mode. If failover servers are
+				configured the remote cache clients will try to register
+				with other failover servers in a sequential order. If a
+				connection is made, the client will broadcast all
+				relevant cache updates to the failover server while
+				trying periodically to reconnect with the primary
+				server. If there are no failovers configured the client
+				will move into a zombie mode while it tries to
+				re-establish the connection. By default, the cache
+				clients run in an optimistic mode and the failure of the
+				communication channel is detected by an attempted update
+				to the server. A pessimistic mode is configurable so
+				that the clients will engage in active status checks.
+			</p>
+			<p>
+				The remote cache server broadcasts updates to listeners
+				other than the originating source. If the remote cache
+				fails to propagate an update to a client, it will retry
+				for a configurable number of tries before de-registering
+				the client.
+			</p>
+			<p>
+				The cache hub communicates with a facade that implements
+				a zombie pattern (balking facade) to prevent blocking.
+				Puts and removals are queued and occur asynchronously in
+				the background. Get requests are synchronous and can
+				potentially block if there is a communication problem.
+			</p>
+			<p>
+				By default client updates are light weight. The client
+				listeners are configured to remove elements form the
+				local cache when there is a put order from the remote.
+				This allows the client memory store to control the
+				memory size algorithm from local usage, rather than
+				having the usage patterns dictated by the usage patterns
+				in the system at large.
+			</p>
+			<p>
+				When using a remote cache the local cache hub will
+				propagate elements in regions configured for the remote
+				cache if the element attributes specify that the item to
+				be cached can be sent remotely. By default there are no
+				remote restrictions on elements and the region will
+				dictate the behavior. The order of auxiliary requests is
+				dictated by the order in the configuration file. The
+				examples are configured to look in memory, then disk,
+				then remote caches. Most elements will only be retrieved
+				from the remote cache once, when they are not in memory
+				or disk and are first requested, or after they have been
+				invalidated.
+			</p>
+			<subsection name="Client Configuration">
+				<p>
+					The configuration is fairly straightforward and is
+					done in the auxiliary cache section of the
+					<code>cache.ccf</code>
+					configuration file. In the example below, I created
+					a Remote Auxiliary Cache Client referenced by
+					<code>RFailover</code>
+					.
+				</p>
+				<p>
+					This auxiliary cache will use
+					<code>localhost:1102</code>
+					as its primary remote cache server and will attempt
+					to failover to
+					<code>localhost:1103</code>
+					if the primary is down.
+				</p>
+				<p>
+					Setting
+					<code>RemoveUponRemotePut</code>
+					to
+					<code>false</code>
+					would cause remote puts to be translated into put
+					requests to the client region. By default it is
+					<code>true</code>
+					, causing remote put requests to be issued as
+					removes at the client level. For groups the put
+					request functions slightly differently: the item
+					will be removed, since it is no longer valid in its
+					current form, but the list of group elements will be
+					updated. This way the client can maintain the
+					complete list of group elements without the burden
+					of storing all of the referenced elements. Session
+					distribution works in this half-lazy replication
+					mode.
+				</p>
+				<p>
+					Setting
+					<code>GetOnly</code>
+					to
+					<code>true</code>
+					would cause the remote cache client to stop
+					propagating updates to the remote server, while
+					continuing to get items from the remote store.
+				</p>
+				<source>
+					<![CDATA[
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.FailoverServers=
+    localhost:1102,localhost:1103
+jcs.auxiliary.RFailover.attributes.RemoveUponRemotePut=true
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+        ]]>
+				</source>
+				<p>
+					This cache region is setup to use a disk cache and
+					the remote cache configured above:
+				</p>
+				<source>
+					<![CDATA[
+#Regions preconfirgured for caching
+jcs.region.testCache1=DC,RFailover
+jcs.region.testCache1.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+        ]]>
+				</source>
+			</subsection>
+			<subsection name="Server Configuration">
+				<p>
+					The remote cache configuration is growing. For now,
+					the configuration is done at the top of the
+					<code>remote.cache.ccf</code>
+					file. The
+					<code>startRemoteCache</code>
+					script passes the configuration file name to the
+					server when it starts up. The configuration
+					parameters below will create a remote cache server
+					that listens to port
+					<code>1102</code>
+					and performs call backs on the
+					<code>remote.cache.service.port</code>
+					, also specified as port
+					<code>1102</code>
+					.
+				</p>
+				<source>
+					<![CDATA[
+# Registry used to register and provide the
+# IRemoteCacheService service.
+registry.host=localhost
+registry.port=1102
+# call back port to local caches.
+remote.cache.service.port=1102
+# rmi socket factory timeout
+remote.cache.rmiSocketFactoryTimeoutMillis=5000
+# cluster setting
+remote.cluster.LocalClusterConsistency=true
+remote.cluster.AllowClusterGet=true
+        ]]>
+				</source>
+				<p>
+					Remote servers can be chained (or clustered). This
+					allows gets from local caches to be distributed
+					between multiple remote servers. Since gets are the
+					most common operation for caches, remote server
+					chaining can help scale a caching solution.
+				</p>
+				<p>
+					The
+					<code>LocalClusterConsistency</code>
+					setting tells the remote cache server if it should
+					broadcast updates received from other cluster
+					servers to registered local caches.
+				</p>
+				<p>
+					The
+					<code>AllowClusterGet</code>
+					setting tells the remote cache server whether it
+					should allow the cache to look in non-local
+					auxiliaries for items if they are not present.
+					Basically, if the get request is not from a cluster
+					server, the cache will treat it as if it originated
+					locally. If the get request originated from a
+					cluster client, then the get will be restricted to
+					local (i.e. memory and disk) auxiliaries. Hence,
+					cluster gets can only go one server deep. They
+					cannot be chained.  By default this setting is true.
+				</p>
+				<p>
+					To use remote server clustering, the remote cache
+					will have to be told what regions to cluster. The
+					configuration below will cluster all
+					non-preconfigured regions with
+					<code>RCluster1</code>
+					.
+				</p>
+				<source>
+					<![CDATA[
+# sets the default aux value for any non configured caches
+jcs.default=DC,RCluster1
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+
+jcs.auxiliary.RCluster1=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RCluster1.attributes=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RCluster1.attributes.RemoteTypeName=CLUSTER
+jcs.auxiliary.RCluster1.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RCluster1.attributes.ClusterServers=localhost:1103
+jcs.auxiliary.RCluster1.attributes.GetOnly=false
+        ]]>
+				</source>
+				<p>
+					RCluster1 is configured to talk to a remote server
+					at
+					<code>localhost:1103</code>
+					. Additional servers can be added in a comma
+					separated list.
+				</p>
+				<p>
+					If we startup another remote server listening to
+					port 1103, (ServerB) then we can have that server
+					talk to the server we have been configuring,
+					listening at 1102 (ServerA). This would allow us to
+					set some local caches to talk to ServerA and some to
+					talk to ServerB. The two remote servers will
+					broadcast all puts and removes between themselves,
+					and the get requests from local caches could be
+					divided. The local caches do not need to know
+					anything about the server chaining configuration,
+					unless you want to use a standby, or failover
+					server.
+				</p>
+				<p>
+					We could also use ServerB as a hot standby. This can
+					be done in two ways. You could have all local caches
+					point to ServerA as a primary and ServerB as a
+					secondary. Alternatively, you can set ServerA as the
+					primary for some local caches and ServerB for the
+					primary for some others.
+				</p>
+				<p>
+					The local cache configuration below uses ServerA as
+					a primary and ServerB as a backup. More than one
+					backup can be defined, but only one will be used at
+					a time. If the cache is connected to any server
+					except the primary, it will try to restore the
+					primary connection indefinitely, at 20 second
+					intervals.
+				</p>
+				<source>
+					<![CDATA[
+	# Remote RMI Cache set up to failover
+	jcs.auxiliary.RFailover=
+	    org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+	jcs.auxiliary.RFailover.attributes=
+	    org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+	jcs.auxiliary.RFailover.attributes.FailoverServers=
+	    localhost:1102,localhost:1103
+	jcs.auxiliary.RC.attributes.RemoveUponRemotePut=true
+	jcs.auxiliary.RFailover.attributes.GetOnly=false
+	        ]]>
+				</source>
+			</subsection>
+			<subsection name="Server Startup / Shutdown">
+				<p>
+					It is highly recommended that you embed the Remote
+					Cache Server in a Servlet container such as Tomcat.
+					Running inside Tomcat allows you to use the
+					JCSAdmin.jsp page. It also takes care of the
+					complexity of creating working startup and shutdown
+					scripts.
+				</p>
+				<p>
+					JCS provides a convenient startup servlet for this
+					purpose. It will start the registry and bind the
+					JCS server to the registry. To use the startup
+					servlet, add the following to the web.xml file and
+					make sure you have the cache.ccf file in the
+					WEB-INF/classes directly of your war file.
+				</p>
+				<source>
+					<![CDATA[
+    <servlet>
+        <servlet-name>JCSRemoteCacheStartupServlet</servlet-name>
+        <servlet-class>
+             org.apache.commons.jcs.auxiliary.remote.server.RemoteCacheStartupServlet
+        </servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+
+    <servlet-mapping>
+        <servlet-name>JCSRemoteCacheStartupServlet</servlet-name>
+        <url-pattern>/jcs</url-pattern>
+    </servlet-mapping>
+]]>
+				</source>
+
+			</subsection>
+		</section>
+
+	</body>
+</document>
diff --git a/xdocs/RemoteCacheProperties.xml b/xdocs/RemoteCacheProperties.xml
new file mode 100644
index 0000000..ab96b16
--- /dev/null
+++ b/xdocs/RemoteCacheProperties.xml
@@ -0,0 +1,167 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Remote Cache Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+		<section name="Remote Auxiliary Cache Configuration">
+
+			<p>
+				The following properties apply to the Remote Cache
+				plugin.
+			</p>
+
+			<subsection name="Remote Client Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>FailoverServers</td>
+						<td>
+							This is a comma separated list of remote
+							servers to use. They should be specified in
+							the host:port format. The first server in
+							the list will be used as the primary server.
+							If the connection is lost with the primary,
+							the cache will try to connect to the next
+							server in the list. If a connection is
+							successfully established with a failover
+							server, then the cache will attempt to
+							restore the conenction with the primary
+							server.
+						</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+					<tr>
+						<td>LocalPort</td>
+						<td>
+							This is the port on which the client will
+							receive callbacks from the remote server. If
+							it is not specified, then some port in the
+							default range used by RMI will be the
+							callback port.
+						</td>
+						<td>N</td>
+						<td>default RMI port range</td>
+					</tr>
+					<tr>
+						<td>RemoveUponRemotePut</td>
+						<td>
+							If you configure the cache to remove upon a
+							remote put, this means that the client will
+							translate updates into removes. The client
+							will remove any local copy it has of the
+							object rather than storing the new version.
+							If you have sticky load balancing across
+							your client servers, then it would make
+							sense to set RemoveUponRemotePut to true if
+							the data is mostly client specific. If the
+							data is re-usable, the you should most
+							likely set this option to false, which is
+							the default.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>RmiSocketFactoryTimeoutMillis</td>
+						<td>
+							If this is greater than 0, then a custom
+							socket factory will be installed in the VM.
+							It will then use this timeout for all RMI
+							communication.
+						</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+					<tr>
+						<td>GetOnly</td>
+						<td>
+							GetOnly is somewhat misnamed. If it is set
+							to true, then the client will not send
+							updates or removes to the remote server. It
+							can still receive updates and removes.
+						</td>
+						<td>N</td>
+						<td>false</td>
+					</tr>
+					<tr>
+						<td>Receive</td>
+						<td>
+							By default Receive is set to true. This
+							means that the remote client will receive
+							updates and removes from the remote server.
+							If you set Receive to false, the remote
+							client will not register a listener with the
+							remote server. This means that the client
+							can send update and remove requests to the
+							server, and it can get from the server, but
+							it will never receive notifications from the
+							server. You might configure Receive to false
+							if you just want to use the remote server as
+							a data store. For instance, you may back the
+							Remote Cache Server with the JDBC disk cache
+							and set Receive=false when you have a high
+							put and low read region.
+						</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>ZombieQueueMaxSize</td>
+						<td>
+							The number of elements the zombie queue will
+							hold. This queue is used to store events if
+							we lose our connection with the server.
+						</td>
+						<td>N</td>
+						<td>1000</td>
+					</tr>
+				</table>
+			</subsection>
+
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+# This remote client does not receive
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.RmiSocketFactoryTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.attributes.Receive=false
+        ]]>
+				</source>
+			</subsection>
+
+		</section>
+	</body>
+</document>
diff --git a/xdocs/RemoteHttpCacheProperties.xml b/xdocs/RemoteHttpCacheProperties.xml
new file mode 100644
index 0000000..21c0141
--- /dev/null
+++ b/xdocs/RemoteHttpCacheProperties.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Remote Http Cache Configuration</title>
+		<author email="ASmuts at apache.org">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="Remote Auxiliary Http Cache Configuration">
+			<p> The following properties apply to the Remote Http Cache plugin.</p>
+			<subsection name="Remote Http Client Configuration Properties">
+				<table>
+					<tr>
+						<th>Property</th>
+						<th>Description</th>
+						<th>Required</th>
+						<th>Default Value</th>
+					</tr>
+					<tr>
+						<td>url</td>
+						<td> This is the full url for the http service.</td>
+						<td>Y</td>
+						<td>n/a</td>
+					</tr>
+					<tr>
+						<td>maxConnectionsPerHost</td>
+						<td> Maximum simultaneous connections per host.</td>
+						<td>N</td>
+						<td>100</td>
+					</tr>
+					<tr>
+						<td>socketTimeoutMillis</td>
+						<td> Read timeout.</td>
+						<td>N</td>
+						<td>3000</td>
+					</tr>
+					<tr>
+						<td>connectionTimeoutMillis</td>
+						<td> Connection timeout.</td>
+						<td>N</td>
+						<td>5000</td>
+					</tr>
+					<tr>
+						<td>httpVersion</td>
+						<td> The http version to use.</td>
+						<td>N</td>
+						<td>1.1</td>
+					</tr>
+					<tr>
+						<td>includeCacheNameAsParameter</td>
+						<td> Should the cache name be appended to the url.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>includeKeysAndPatternsAsParameter
+						</td>
+						<td> Should the key be appended to the url.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>includeRequestTypeasAsParameter
+						</td>
+						<td> Should the request type be appended to the url.</td>
+						<td>N</td>
+						<td>true</td>
+					</tr>
+					<tr>
+						<td>remoteHttpClientClassName
+						</td>
+						<td> This allows you to specify your own client implementation.</td>
+						<td>N</td>
+						<td>RemoteHttpCacheClient.class.getName()</td>
+					</tr>
+					<tr>
+						<td>ZombieQueueMaxSize</td>
+						<td> The number of elements the zombie queue will hold. This queue
+							is used to store events if we lose our connection with the
+							server.</td>
+						<td>N</td>
+						<td>1000</td>
+					</tr>
+				</table>
+			</subsection>
+			<subsection name="Example Configuration">
+				<source>
+					<![CDATA[
+# This remote client does not receive
+jcs.auxiliary.RC=org.apache.commons.jcs.auxiliary.remote.http.client.RemoteCacheFactory
+jcs.auxiliary.RC.attributes=org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RC.attributes.FailoverServers=localhost:1101,localhost:1102
+jcs.auxiliary.RC.attributes.LocalPort=1201
+jcs.auxiliary.RC.attributes.RemoveUponRemotePut=false
+jcs.auxiliary.RC.attributes.RmiSocketFactoryTimeoutMillis=5000
+jcs.auxiliary.RC.attributes.GetOnly=false
+jcs.auxiliary.RC.attributes.Receive=false
+        ]]>
+				</source>
+			</subsection>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/UsingJCSBasicWeb.xml b/xdocs/UsingJCSBasicWeb.xml
new file mode 100644
index 0000000..ee48112
--- /dev/null
+++ b/xdocs/UsingJCSBasicWeb.xml
@@ -0,0 +1,450 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>Using JCS: Some basics for the web</title>
+    <author email="ASmuts at yahoo.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="Using JCS: Some basics for the web">
+      <p>
+        The primary bottleneck in most dynamic web-based applications is
+        the retrieval of data from the database.  While it is relatively
+        inexpensive to add more front-end servers to scale the serving
+        of pages and images and the processing of content, it is an
+        expensive and complex ordeal to scale the database.  By taking
+        advantage of data caching, most web applications can reduce
+        latency times and scale farther with fewer machines.
+      </p>
+      <p>
+        JCS is a front-tier cache that can be configured to maintain
+        consistency across multiple servers by using a centralized
+        remote server or by lateral distribution of cache updates.
+        Other caches, like the Javlin EJB data cache, are basically
+        in-memory databases that sit between your EJB's and your
+        database.  Rather than trying to speed up your slow EJB's, you
+        can avoid most of the network traffic and the complexity by
+        implementing JCS front-tier caching.  Centralize your EJB access
+        or your JDBC data access into local managers and perform the
+        caching there.
+      </p>
+      <subsection name="What to cache?">
+        <p>
+          The data used by most web applications varies in its
+          dynamicity, from completely static to always changing at every
+          request.  Everything that has some degree of stability can be
+          cached.  Prime candidates for caching range from the list data
+          for stable dropdowns, user information, discrete and
+          infrequently changing information, to stable search results
+          that could be sorted in memory.
+        </p>
+        <p>
+          Since JCS is distributed and allows updates and invalidations
+          to be broadcast to multiple listeners, frequently changing
+          items can be easily cached and kept in sync through your data
+          access layer.  For data that must be 100% up to date, say an
+          account balance prior to a transfer, the data should directly
+          be retrieved from the database.  If your application allows
+          for the viewing and editing of data, the data for the view
+          pages could be cached, but the edit pages should, in most
+          cases, pull the data directly from the database.
+        </p>
+      </subsection>
+      <subsection name="How to cache discrete data">
+        <p>
+          Let's say that you have an e-commerce book store.  Each book
+          has a related set of information that you must present to the
+          user.  Let's say that 70% of your hits during a particular day
+          are for the same 1,000 popular items that you advertise on key
+          pages of your site, but users are still actively browsing your
+          catalog of over a million books.  You cannot possibly cache
+          your entire database, but you could dramatically decrease the
+          load on your database by caching the 1,000 or so most popular
+          items.
+        </p>
+        <p>
+          For the sake of simplicity let's ignore tie-ins and
+          user-profile based suggestions (also good candidates for
+          caching) and focus on the core of the book detail page.
+        </p>
+        <p>
+          A simple way to cache the core book information would be to
+          create a value object for book data that contains the
+          necessary information to build the display page.  This value
+          object could hold data from multiple related tables or book
+          subtype table, but lets say that you have a simple table
+          called <code>BOOK</code> that looks something like this:
+        </p>
+        <source><![CDATA[
+  Table BOOK
+  BOOK_ID_PK
+  TITLE
+  AUTHOR
+  ISBN
+  PRICE
+  PUBLISH_DATE
+        ]]></source>
+        <p>
+          We could create a value object for this table called
+          <code>BookVObj</code> that has variables with the same
+          names as the table columns that might look like this:
+        </p>
+        <source><![CDATA[
+package com.genericbookstore.data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class BookVObj implements Serializable
+{
+    public int bookId = 0;
+    public String title;
+    public String author;
+    public String ISBN;
+    public String price;
+    public Date publishDate;
+
+    public BookVObj()
+    {
+    }
+}
+        ]]></source>
+        <p>
+          Then we can create a manager called
+          <code>BookVObjManager</code> to store and retrieve
+          <code>BookVObj</code>'s.  All access to core book data
+          should go through this class, including inserts and
+          updates, to keep the caching simple.  Let's make
+          <code>BookVObjManager</code> a singleton that gets a
+          JCS access object in initialization.  The start of the
+          class might look like:
+        </p>
+        <source><![CDATA[
+package com.genericbookstore.data;
+
+import org.apache.commons.jcs.JCS;
+// in case we want to set some special behavior
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+
+public class BookVObjManager
+{
+    private static BookVObjManager instance;
+    private static int checkedOut = 0;
+    private static CacheAccess<String, BookVObj> bookCache;
+
+    private BookVObjManager()
+    {
+        try
+        {
+            bookCache = JCS.getInstance("bookCache");
+        }
+        catch (Exception e)
+        {
+            // Handle cache region initialization failure
+        }
+
+        // Do other initialization that may be necessary, such as getting
+        // references to any data access classes we may need to populate
+        // value objects later
+    }
+
+    /**
+     * Singleton access point to the manager.
+     */
+    public static BookVObjManager getInstance()
+    {
+        synchronized (BookVObjManager.class)
+        {
+            if (instance == null)
+            {
+                instance = new BookVObjManager();
+            }
+        }
+
+        synchronized (instance)
+        {
+            instance.checkedOut++;
+        }
+
+        return instance;
+    }
+        ]]></source>
+        <p>
+          To get a <code>BookVObj</code> we will need some access
+          methods in the manager. We should be able to get a
+          non-cached version if necessary, say before allowing an
+          administrator to edit the book data. The methods might
+          look like:
+        </p>
+        <source><![CDATA[
+    /**
+     * Retrieves a BookVObj.  Default to look in the cache.
+     */
+    public BookVObj getBookVObj(int id)
+    {
+        return getBookVObj(id, true);
+    }
+
+    /**
+     * Retrieves a BookVObj. Second argument decides whether to look
+     * in the cache. Returns a new value object if one can't be
+     * loaded from the database. Database cache synchronization is
+     * handled by removing cache elements upon modification.
+     */
+    public BookVObj getBookVObj(int id, boolean fromCache)
+    {
+        BookVObj vObj = null;
+
+        // First, if requested, attempt to load from cache
+
+        if (fromCache)
+        {
+            vObj = bookCache.get("BookVObj" + id);
+        }
+
+        // Either fromCache was false or the object was not found, so
+        // call loadBookVObj to create it
+
+        if (vObj == null)
+        {
+            vObj = loadvObj(id);
+        }
+
+        return  vObj;
+    }
+
+    /**
+     * Creates a BookVObj based on the id of the BOOK table.  Data
+     * access could be direct JDBC, some or mapping tool, or an EJB.
+     */
+    public BookVObj loadBookVObj(int id)
+    {
+        BookVObj vObj = new BookVObj();
+
+        vObj.bookID = id;
+
+        try
+        {
+            boolean found = false;
+
+            // load the data and set the rest of the fields
+            // set found to true if it was found
+
+            found = true;
+
+            // cache the value object if found
+
+            if (found)
+            {
+                // could use the defaults like this
+                // bookCache.put( "BookVObj" + id, vObj );
+                // or specify special characteristics
+
+                // put to cache
+
+                bookCache.put("BookVObj" + id, vObj);
+            }
+
+        }
+        catch (Exception e)
+        {
+            // Handle failure putting object to cache
+        }
+
+        return vObj;
+    }
+        ]]></source>
+        <p>
+          We will also need a method to insert and update book data.  To
+          keep the caching in one place, this should be the primary way
+          core book data is created.  The method might look like:
+        </p>
+        <source><![CDATA[
+    /**
+     * Stores BookVObj's in database.  Clears old items and caches
+     * new.
+     */
+    public int storeBookVObj(BookVObj vObj)
+    {
+        try
+        {
+            // since any cached data is no longer valid, we should
+            // remove the item from the cache if it an update.
+
+            if (vObj.bookID != 0)
+            {
+                bookCache.remove("BookVObj" + vObj.bookID);
+            }
+
+            // put the new object in the cache
+
+            bookCache.put("BookVObj" + id, vObj);
+        }
+        catch (Exception e)
+        {
+            // Handle failure removing object or putting object to cache.
+        }
+    }
+}
+        ]]></source>
+        <p>
+          As elements are placed in the cache via <code>put</code>, it
+          is possible to specify custom attributes for those elements
+          such as its maximum lifetime in the cache, whether or not it
+          can be spooled to disk, etc.  It is also possible (and easier)
+          to define these attributes in the configuration file as
+          demonstrated later.  We now have the basic infrastructure for
+          caching the book data.
+      </p>
+      </subsection>
+      <subsection name="Selecting the appropriate auxiliary caches">
+        <p>
+          The first step in creating a cache region is to determine the
+          makeup of the memory cache.  For the book store example, I
+          would create a region that could store a bit over the minimum
+          number I want to have in memory, so the core items always
+          readily available.  I would set the maximum memory size to
+          <code>1200</code>.  In addition, I might want to have all
+          objects in this cache region expire after <code>7200</code>
+          seconds.  This can be configured in the element attributes on
+          a default or per-region basis as illustrated in the
+          configuration file below.
+        </p>
+        <p>
+          For most cache regions you will want to use a disk
+          cache if the data takes over about .5 milliseconds to
+          create.  The <a href="IndexedDiskAuxCache.html">indexed
+          disk cache</a> is the most efficient disk caching
+          auxiliary, and for normal usage it is recommended.
+        </p>
+        <p>
+          The next step will be to select an appropriate
+          distribution layer.  If you have a back-end server
+          running an appserver or scripts or are running multiple
+          webserver VMs on one machine, you might want to use
+          the centralized <a href="RemoteAuxCache.html">remote
+          cache</a>.  The lateral cache would be fine, but since
+          the lateral cache binds to a port, you'd have to configure
+          each VM's lateral cache to listen to a different port on
+          that machine.
+        </p>
+        <p>
+          If your environment is very flat, say a few
+          load-balanced webservers and a database machine or one
+          webserver with multiple VMs and a database machine,
+          then the lateral cache will probably make more sense.
+          The <a href="LateralTCPAuxCache.html">TCP lateral
+          cache</a> is recommended.
+        </p>
+        <p>
+          For the book store configuration I will set up a region
+          for the <code>bookCache</code> that uses the LRU memory
+          cache, the indexed disk auxiliary cache, and the remote
+          cache.  The configuration file might look like this:
+        </p>
+        <source><![CDATA[
+# DEFAULT CACHE REGION
+
+# sets the default aux value for any non configured caches
+jcs.default=DC,RFailover
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=3600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+
+# CACHE REGIONS AVAILABLE
+
+# Regions preconfigured for caching
+jcs.region.bookCache=DC,RFailover
+jcs.region.bookCache.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.bookCache.cacheattributes.MaxObjects=1200
+jcs.region.bookCache.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.bookCache.elementattributes.IsEternal=false
+jcs.region.bookCache.elementattributes.MaxLife=7200
+jcs.region.bookCache.elementattributes.IdleTime=1800
+jcs.region.bookCache.elementattributes.IsSpool=true
+jcs.region.bookCache.elementattributes.IsRemote=true
+jcs.region.bookCache.elementattributes.IsLateral=true
+
+# AUXILIARY CACHES AVAILABLE
+
+# Primary Disk Cache -- faster than the rest because of memory key storage
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=/usr/opt/bookstore/raf
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000
+jcs.auxiliary.DC.attributes.MaxKeySize=10000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=7500
+
+# Remote RMI Cache set up to failover
+jcs.auxiliary.RFailover=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheFactory
+jcs.auxiliary.RFailover.attributes=
+    org.apache.commons.jcs.auxiliary.remote.RemoteCacheAttributes
+jcs.auxiliary.RFailover.attributes.RemoteTypeName=LOCAL
+jcs.auxiliary.RFailover.attributes.FailoverServers=scriptserver:1102
+jcs.auxiliary.RFailover.attributes.GetOnly=false
+        ]]></source>
+        <p>
+          I've set up the default cache settings in the above
+          file to approximate the <code>bookCache</code>
+          settings.  Other non-preconfigured cache regions will
+          use the default settings.  You only have to configure
+          the auxiliary caches once.  For most caches you will
+          not need to pre-configure your regions unless the size
+          of the elements varies radically.  We could easily put
+          several hundred thousand <code>BookVObj</code>'s in
+          memory.  The <code>1200</code> limit was very
+          conservative and would be more appropriate for a large
+          data structure.
+        </p>
+        <p>
+          To get running with the book store example, I will also
+          need to start up the remote cache server on the
+          scriptserver machine.  The
+          <a href="RemoteAuxCache.html">remote cache
+          documentation</a> describes the configuration.
+        </p>
+        <p>
+          I now have a basic caching system implemented for my book
+          data.  Performance should improve immediately.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
+
+
+
diff --git a/xdocs/download_jcs.xml b/xdocs/download_jcs.xml
new file mode 100644
index 0000000..6389f49
--- /dev/null
+++ b/xdocs/download_jcs.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<!--
+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 FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: download-page-template.xml                            |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:download-page                      |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |    - commons.release.version (required)                              |
+ |    - commons.binary.suffix (optional)                                |
+ |      (defaults to "-bin", set to "" for pre-maven2 releases)         |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |    <commons.release.version>1.2</commons.release.version>            |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Download Apache Commons JCS</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+    <section name="Download Apache Commons JCS">
+    <subsection name="Using a Mirror">
+      <p>
+        We recommend you use a mirror to download our release
+        builds, but you <strong>must</strong> verify the integrity of
+        the downloaded files using signatures downloaded from our main
+        distribution directories. Recent releases (48 hours) may not yet
+        be available from the mirrors.
+      </p>
+
+      <p>
+        You are currently using <b>[preferred]</b>.  If you
+        encounter a problem with this mirror, please select another
+        mirror.  If all mirrors are failing, there are <i>backup</i>
+        mirrors (at the end of the mirrors list) that should be
+        available.
+        <br></br>
+        [if-any logo]<a href="[link]"><img align="right" src="[logo]" border="0"></img></a>[end]
+      </p>
+
+      <form action="[location]" method="get" id="SelectMirror">
+        <p>
+          Other mirrors:
+          <select name="Preferred">
+          [if-any http]
+            [for http]<option value="[http]">[http]</option>[end]
+          [end]
+          [if-any ftp]
+            [for ftp]<option value="[ftp]">[ftp]</option>[end]
+          [end]
+          [if-any backup]
+            [for backup]<option value="[backup]">[backup] (backup)</option>[end]
+          [end]
+          </select>
+          <input type="submit" value="Change"></input>
+        </p>
+      </form>
+
+      <p>
+        The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
+        link links to the code signing keys used to sign the product.
+        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site.
+        The <code>MD5</code> link downloads the checksum from the main site.
+      </p>
+    </subsection>
+    </section>
+    <section name="Apache Commons JCS 2.0 (Java 6.0+)">
+      <subsection name="Binaries">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/jcs/binaries/commons-jcs-2.0-bin.tar.gz">commons-jcs-2.0-bin.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/binaries/commons-jcs-2.0-bin.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/binaries/commons-jcs-2.0-bin.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/jcs/binaries/commons-jcs-2.0-bin.zip">commons-jcs-2.0-bin.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/binaries/commons-jcs-2.0-bin.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/binaries/commons-jcs-2.0-bin.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+      <subsection name="Source">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/jcs/source/commons-jcs-2.0-src.tar.gz">commons-jcs-2.0-src.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/source/commons-jcs-2.0-src.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/source/commons-jcs-2.0-src.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/jcs/source/commons-jcs-2.0-src.zip">commons-jcs-2.0-src.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/source/commons-jcs-2.0-src.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/jcs/source/commons-jcs-2.0-src.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+    </section>
+    <section name="Archives">
+        <p>
+          Older releases can be obtained from the archives.
+        </p>
+        <ul>
+          <li class="download"><a href="[preferred]/commons/jcs/">browse download area</a></li>
+          <li><a href="http://archive.apache.org/dist/commons/jcs/">archives...</a></li>
+        </ul>
+    </section>
+  </body>
+</document>
diff --git a/xdocs/faq.fml b/xdocs/faq.fml
new file mode 100644
index 0000000..d1e937e
--- /dev/null
+++ b/xdocs/faq.fml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+	/*
+	* Copyright 2001-2004 The Apache Software Foundation.
+	* 
+	* Licensed 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.
+	*/
+-->
+
+<faqs title="Frequently Asked Questions">
+
+	<part id="configuration">
+		<title>Configuration</title>
+
+		<faq id="required-jars">
+			<question>What jars are required by JCS?</question>
+			<answer>
+				<p>
+					As of verison 1.2.7.0, the core of JCS (the LRU
+					memory cache, the indexed disk cache, the TCP
+					lateral, and the RMI remote server) requires only
+					two other jars.
+				</p>
+				<p>
+					<a
+						href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
+						concurrent
+					</a>
+				</p>
+				<p>commons-logging</p>
+
+				<p>
+					Versions 1.2.6.9 and below also require the
+					following two additional jars:
+				</p>
+
+				<p>commons-collections</p>
+				<p>commons-lang</p>
+				<p>
+					All of the other dependencies listed on the project
+					info page are for optional plugins.
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="configuration-file">
+			<question>How do I configure JCS?</question>
+			<answer>
+				<p>
+					By default JCS looks for a cache.ccf file in the
+					classpath. You must have a configuration file on the
+					classpath to use JCS. The documentation describes
+					how to configure the cache.
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="manual-configuration">
+			<question>
+				How can I configure JCS with my own properties?
+			</question>
+
+			<answer>
+				<p>
+					You don't have to put the cache.ccf file in the
+					classpath; instead you can do the following:
+				</p>
+				<code>
+					CompositeCacheManager ccm =
+					CompositeCacheManager.getUnconfiguredInstance();
+
+					Properties props = new Properties();
+
+					props.load(/* load properties from some location
+					defined by your app */);
+
+					ccm.configure(props);
+				</code>
+			</answer>
+		</faq>
+
+		<faq id="configuration-system-properties">
+			<question>
+				Can JCS use system properties during configuration?
+			</question>
+			<answer>
+				<p>
+					Yes. JCS will look for a system property for any
+					name inside the delimiters ${}. Also, JCS will check
+					to see if any property key in the cache.ccf is
+					defined in the system properties. If so, the system
+					value will be used.
+				</p>
+			</answer>
+		</faq>
+	</part>
+
+	<part id="general-questions">
+		<title>General Questions</title>
+
+		<faq id="jcs-vs-ehcache">
+			<question>Is JCS faster than EHCache?</question>
+			<answer>
+				<p>
+					Yes. JCS is almost twice as fast as EHCache. JCS
+					1.2.7.0, using the default LRU Memory Cache, has
+					proven to be nearly twice as fast as EHCache
+					1.2-beta4 at gets and puts. The EHCache benchmark
+					data is unsubstantiated and very old. As such the
+					EHCache site benchmark data is completely
+					inaccurate.
+					<a href="JCSvsEHCache.html">Read More</a>
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="admin-jsp">
+			<question>Where can I get the admin jsp?</question>
+			<answer>
+				<p>
+					You can download the admin jsp
+					<a
+						href="http://svn.apache.org/viewcvs.cgi/commons/proper/jcs/trunk/src/java/org/apache/jcs/admin/JCSAdmin.jsp">
+						here
+					</a>
+					.
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="source-code">
+			<question>Where can I get the source?</question>
+			<answer>
+				<p>
+					You can view the source
+					<a
+						href="http://svn.apache.org/viewcvs.cgi/commons/proper/jcs/trunk/">
+						here
+					</a>
+					or get the source code from subversion with <code>svn co
+					http://svn.apache.org/repos/asf/commons/proper/jcs/trunk</code>.
+					The tagged releases are available with <code>svn co.</code> ex.
+					http://svn.apache.org/repos/asf/commons/proper/jcs/tags/jcs_1_2_7_0
+				</p>
+			</answer>
+		</faq>
+		<faq id="compiling-source">
+			<question>How do I compile the source?</question>
+			<answer>
+				<p>
+					You first need to install
+					<a href="http://maven.apache.org/">Maven 2</a>
+					The download is available at
+					http://maven.apache.org/download.html.
+					After installing run
+					"mvn" which compiles and tests the entire package.
+					To build a jar run "mvn install".
+				</p>
+			</answer>
+		</faq>
+	</part>
+
+	<part id="elements">
+		<title>Elements</title>
+
+		<faq id="element-attributes">
+			<question>How do I set the element attributes?</question>
+			<answer>
+				<p>
+					Every element put into the cache has its own set of
+					attributes. By default elements are given a copy of
+					the default element attributes associated with a
+					region. You can also specify the attributes to use
+					for an element when you put it in the cache. See
+					<a href="ElementAttributes.html">
+						Element Attributes
+					</a>
+					for more information on the attributes that are
+					available.
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="element-events">
+			<question>How do I register an element event?</question>
+			<answer>
+				<p>
+					Element event handlers must be added to the element
+					attributes. See
+					<a href="ElementEventHandling.html">
+						Element Event Handling
+					</a>
+					for more information on how to handle element
+					events.
+				</p>
+			</answer>
+		</faq>
+
+		<faq id="hierarchical-removal">
+			<question>
+				Can I remove all items beginning with part of a key?
+			</question>
+			<answer>
+				<p>
+					Yes, but it is somewhat expensive, since some of the
+					auxiliaries will have to iterate over their keysets.
+					Although all the auxiliaries honor this, it is not
+					part of the auxiliary API. There is no method along
+					the lines of "removeStartingWith", but all the remove
+					methods can do it.
+				</p>
+				<p>
+					By default, the hierarchical key delimiter used in
+					JCS is a colon. You cannot add a String key that
+					ends with a colon. If you call remove with a String
+					key that ends in a colon, everything that has a key
+					that starts with the argument will be removed.
+				</p>
+				<p>If your keys are in this format</p>
+				<p>TYPE:SOURCE:OBJECT</p>
+				<p>
+					And you put n objects int he cache with keys like
+					this
+				</p>
+				<p>"ABC:123:0" to "ABC:123:n"</p>
+				<p>then you could remove all the obejcts by calling</p>
+				<p>jcs.remove( "ABC:123:" );</p>
+			</answer>
+		</faq>
+	</part>
+
+	<part id="indexed-disk-cache">
+		<title>Indexed Disk Cache</title>
+
+		<faq id="thread-pool">
+			<question>
+				How do I limit the number of threads used by the disk
+				cache?
+			</question>
+			<answer>
+				<p>
+					The indexed disk cache uses an event queue for each
+					region. By default these queues are worked by their
+					own dedicated threads. Hence, you will have one
+					thread per disk cache region. Although the queues
+					kill off idle threads, you may want to limit the
+					overall number of threads used by the queues. You
+					can do this by telling the disk cache to use a
+					thread pool. The configuration is described
+					<a href="IndexedDiskAuxCache.html">
+						on the disk cache configuration page
+					</a>
+					.
+				</p>
+			</answer>
+		</faq>
+	</part>
+
+	<part id="remote-cache-server">
+		<title>Remote Cache Server</title>
+
+		<faq id="classes">
+			<question>
+				Do I need to put my jars in the classpath of the remote
+				server?
+			</question>
+			<answer>
+				<p>
+					No. The remote server never deserializes your
+					classes.
+				</p>
+			</answer>
+		</faq>
+	</part>
+
+</faqs>
\ No newline at end of file
diff --git a/xdocs/getting_started/intro.xml b/xdocs/getting_started/intro.xml
new file mode 100644
index 0000000..a359d12
--- /dev/null
+++ b/xdocs/getting_started/intro.xml
@@ -0,0 +1,274 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+	<properties>
+		<title>Getting Started -- Introduction</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+
+	<body>
+
+		<section name="Getting Started">
+			<p>
+				To start using JCS you need to (1) understand the core
+				concepts, (2) download JCS, (3) get the required
+				dependencies, (4) configure JCS, and (5) then start
+				programming to it. The purpose of the getting started
+				guide is to help you get up and running with JCS as
+				quickly as possible. In depth documentation on the
+				various features of JCS is provided in the User's Guide.
+			</p>
+		</section>
+
+		<section name="STEP 1: Understand the Core Concepts">
+			<p>
+				In order to use JCS, you must understand a few core
+				concepts, most importantly you need to know the
+				difference between "elements," "regions," and
+				"auxiliaries".
+			</p>
+			<p>
+				JCS is an object cache. You can put objects, or
+				"elements," into JCS and reference them via a key, much
+				like a hashtable.
+			</p>
+			<p>
+				You can think of JCS as a collection of hashtables that
+				you reference by name. Each of these hashtables is
+				called a "region," and each region can be configured
+				independently of the others. For instance, I may have a
+				region called Cities where I cache City objects that
+				change infrequently. I may also define a region called
+				Products where I cache product data that changes more
+				frequently. I would configure the volatile Product
+				region to expire elements more quickly than the City
+				region.
+			</p>
+			<p>
+				"Auxiliaries" are optional plugins that a region can
+				use. The core auxiliaries are the Indexed Disk Cache,
+				the TCP Lateral Cache, and the Remote Cache Server. The
+				Disk Cache, for example, allows you to swap items onto
+				disk when a memory threshold is reached. You can read
+				more about the available auxiliaries
+				<a href="../JCSPlugins.html">HERE</a>
+				.
+			</p>
+		</section>
+
+
+		<section name="STEP 2: Download JCS">
+			<p>
+				Download the latest version of JCS. The latest JCS
+				builds are located
+				<a
+					href="http://www.apache.org/dist/commons/jcs/">
+					HERE
+				</a>
+			</p>
+			<p>
+				If you would like to build JCS yourself, check it out
+				from Subversion and build it as you would any other
+				project built by Maven. The location of the
+				repository is documented in the project info pages that
+				are linked via the left nav.
+			</p>
+		</section>
+
+		<section name="STEP 3: Get the Required Dependencies">
+            <p>
+                Beginning with version 2.0 the core of JCS (the LRU memory
+                cache, the indexed disk cache, the TCP lateral, and the
+                RMI remote server) requires only commons-logging.
+            </p>
+			<p>
+				Beginning with version 1.2.7.0 and up to version 1.3, the core of 
+                JCS (the LRU memory
+				cache, the indexed disk cache, the TCP lateral, and the
+				RMI remote server) requires only two other jars.
+			</p>
+			<p>
+				<a	href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">
+					concurrent
+				</a>
+			</p>
+			<p>commons-logging</p>
+			<p>
+				Versions 1.2.6.9 and below also require the following
+				two additional jars:
+			</p>
+			<p>commons-collections</p>
+			<p>commons-lang</p>
+			<p>
+				All of the other dependencies listed on the project info
+				page are for optional plugins.
+			</p>
+		</section>
+
+		<section name="STEP 4: Configure JCS">
+			<p>
+				JCS is configured from a properties file called
+				"cache.ccf". There are alternatives to using this file,
+				but they are beyond the scope of the getting started
+				guide.
+			</p>
+			<p>
+				The cache configuration has three parts: default,
+				regions, and auxiliaries. You can think of the
+				auxiliaries as log4j appenders and the regions as log4j
+				categories. For each region (or category) you can
+				specify and auxiliary (or appender to use). If you don't
+				define a region in the cache.ccf, then the default
+				settings are used. The difference between JCS and log4j
+				is that in JCS, pre-defined regions do not inherent
+				auxiliaries from the default region.
+			</p>
+			<p>
+				The following cache.ccf file defines one region called
+				"testCache1" and uses the Indexed Disk Cache, here
+				called "DC" by default. The LRU Memory Cache is selected
+				as the memory manager.
+			</p>
+			<source>
+				<![CDATA[
+# DEFAULT CACHE REGION
+jcs.default=DC
+jcs.default.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=1000
+jcs.default.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.default.cacheattributes.UseMemoryShrinker=false
+jcs.default.cacheattributes.MaxMemoryIdleTime=3600
+jcs.default.cacheattributes.ShrinkerInterval=60
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.default.elementattributes.IsEternal=false
+jcs.default.elementattributes.MaxLife=21600
+jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.IsSpool=true
+jcs.default.elementattributes.IsRemote=true
+jcs.default.elementattributes.IsLateral=true
+
+# PRE-DEFINED CACHE REGIONS
+jcs.region.testCache1=DC
+jcs.region.testCache1.cacheattributes=
+    org.apache.commons.jcs.engine.CompositeCacheAttributes
+jcs.region.testCache1.cacheattributes.MaxObjects=1000
+jcs.region.testCache1.cacheattributes.MemoryCacheName=
+    org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
+jcs.region.testCache1.cacheattributes.UseMemoryShrinker=false
+jcs.region.testCache1.cacheattributes.MaxMemoryIdleTime=3600
+jcs.region.testCache1.cacheattributes.ShrinkerInterval=60
+jcs.region.testCache1.cacheattributes.MaxSpoolPerRun=500
+jcs.region.testCache1.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+jcs.region.testCache1.elementattributes.IsEternal=false
+
+# AVAILABLE AUXILIARY CACHES
+jcs.auxiliary.DC=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
+jcs.auxiliary.DC.attributes=
+    org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
+jcs.auxiliary.DC.attributes.DiskPath=${user.dir}/jcs_swap
+jcs.auxiliary.DC.attributes.MaxPurgatorySize=10000000
+jcs.auxiliary.DC.attributes.MaxKeySize=1000000
+jcs.auxiliary.DC.attributes.MaxRecycleBinSize=5000
+jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=300000
+jcs.auxiliary.DC.attributes.ShutdownSpoolTimeLimit=60
+        ]]>
+			</source>
+			<p>
+				Basic JCS configuration is described in more detail
+				<a href="../BasicJCSConfiguration.html">HERE</a>
+			</p>
+			<p>
+				Element level configuration is described in more detail
+				<a href="../ElementAttributes.html">HERE</a>
+			</p>
+			<p>
+				For more information on advanced configuration options
+				and the available plugins, see the User's Guide.
+			</p>
+		</section>
+
+		<section name="STEP 5: Programming to JCS">
+			<p>
+				JCS provides a few convenient classes that should meet all
+				your needs. 
+			</p>
+			<p>
+				To get a cache region you simply ask JCS for the region
+				by name. If you wanted to use JCS for City objects, you
+				would do something like this:
+			</p>
+			<source>
+				<![CDATA[
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.exception.CacheException;
+
+. . .
+
+    private static final String cacheRegionName = "city";
+
+    private CacheAccess<String, City> cache = null;
+
+. . .
+	// in your constructor you might do this
+    try
+    {
+        CacheAccess<String, City> c = JCS.getInstance( this.getCacheRegionName() );
+        setCache( c );
+    }
+    catch ( CacheException e )
+    {
+        log.error( "Problem initializing cache for region name ["
+          + this.getCacheRegionName() + "].", e );
+    }
+
+. . .
+
+    // to get a city out of the cache by id you might do this:
+    String key = "cityId:" + String.valueOf( id );
+
+    City city = cache.get( key );
+
+. . .
+
+    // to put a city object in the cache, you could do this:
+    try
+    {
+        // if it isn't null, insert it
+        if ( city != null )
+        {
+            cache.put( key, city );
+        }
+    }
+    catch ( CacheException e )
+    {
+         log.error( "Problem putting "
+           + city + " in the cache, for key " + key, e );
+    }
+        ]]>
+</source>
+		</section>
+
+	</body>
+</document>
diff --git a/xdocs/index.xml b/xdocs/index.xml
new file mode 100644
index 0000000..9c00eb7
--- /dev/null
+++ b/xdocs/index.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0"?>
+	<!--
+		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.
+	-->
+<document>
+	<properties>
+		<title>Java Caching System</title>
+		<author email="asmuts at apache.org">Aaron Smuts</author>
+	</properties>
+	<body>
+		<section name="Java Caching System">
+			<p>
+				JCS is a distributed caching system written in Java. It is intended
+				to speed up applications by providing a means to manage cached data
+				of various dynamic natures. Like any caching system, JCS is
+				<a href="UsingJCSBasicWeb.html">most useful</a>
+				for high read, low put applications. Latency times drop sharply and
+				bottlenecks move away from the database in an effectively cached
+				system.
+				<a href="getting_started/intro.html">Learn how to start using JCS.</a>
+			</p>
+			<p> The JCS goes beyond simply caching objects in memory. It provides
+				numerous additional features:</p>
+			<ul>
+				<li>Memory management</li>
+				<li>Disk overflow (and defragmentation)</li>
+				<li>Thread pool controls</li>
+				<li>Element grouping</li>
+				<li>Minimal dependencies</li>
+				<li>Quick nested categorical removal</li>
+				<li>Data expiration (idle time and max life)</li>
+				<li>Extensible framework</li>
+				<li>Fully configurable runtime parameters</li>
+				<li>Region data separation and configuration</li>
+				<li>Fine grained element configuration options</li>
+				<li>Remote synchronization</li>
+				<li>Remote store recovery</li>
+				<li>Non-blocking "zombie" (balking facade) pattern</li>
+				<li>Lateral distribution of elements via HTTP, TCP, or UDP</li>
+				<li>UDP Discovery of other caches</li>
+				<li>Element event handling</li>
+				<li>Remote server chaining (or clustering) and failover</li>
+				<li>Custom event logging hooks</li>
+				<li>Custom event queue injection</li>
+				<li>Custom object serializer injection</li>
+				<li>Key pattern matching retrieval</li>
+				<li>Network efficient multi-key retrieval</li>
+			</ul>
+			<p> JCS 2.0 works on JDK versions 1.6 and up. It only has a
+				dependency on Commons Logging.</p>
+		</section>
+		<section name="JCS is a Composite Cache">
+			<p>
+				The foundation of JCS is the Composite Cache, which is the
+				<a href="JCSPlugins.html">pluggable</a>
+				controller for a cache region. Four types of caches can be plugged
+				into the Composite Cache for any given region: (1) Memory, (2) Disk,
+				(3) Lateral, and (4) Remote. The Composite Cache orchestrates access
+				to the various caches configured for use in a region.
+			</p>
+			<p> The JCS jar provides production ready implementations of each of
+				the four types of caches. In addition to the core four, JCS also
+				provides additional plugins of each type.</p>
+			<subsection name="LRU Memory Cache">
+				<p>
+					The LRU Memory Cache is an extremely fast, highly configurable
+					<a href="RegionProperties.html"> memory cache</a>
+					. It uses a Least Recently Used algorithm to manage the number of
+					items that can be stored in memory. The LRU Memory Cache uses its
+					own LRU Map implementation that is significantly faster than both
+					the commons LRUMap implementation and the LinkedHashMap that is
+					provided with JDK1.4 up. This makes JCS faster than its
+					<a href="JCSvsEHCache.html">competitors</a>
+					.
+				</p>
+			</subsection>
+			<subsection name="Indexed Disk Cache">
+				<p>
+					The
+					<a href="IndexedDiskAuxCache.html">Indexed Disk Cache</a>
+					is a fast, reliable, and
+					<a href="IndexedDiskCacheProperties.html"> highly configurable</a>
+					swap for cached data. The indexed disk cache follows the fastest
+					pattern for disk swapping. Cache elements are written to disk via a
+					continuous queue-based process. The length of the item is stored in
+					the first few bytes of the entry. The offset is stored in memory
+					and can be reference via the key. When items are removed from the
+					disk cache, the location and size are recorded and reused when
+					possible. Every aspect of the disk cache is configurable, and a
+					thread pool can be used to reduce the number of queue worker
+					threads across the system.
+				</p>
+			</subsection>
+			<subsection name="JDBC Disk Cache">
+				<p>
+					The
+					<a href="JDBCDiskCache.html">JDBC Disk Cache</a>
+					is a fast, reliable, and
+					<a href="JDBCDiskCacheProperties.html"> highly configurable</a>
+					disk cache. It stores both the keys and elements in a JDBC
+					compatible database. The JDBC disk cache stores elements in
+					a database as BLOBs. Periodically, the table is swept to remove
+					expired elements. Multiple instances can be configured to use a
+					common connection pool. A thread pool can be used to reduce the
+					number of queue worker threads across the system. The
+					<a href="MySQLDiskCacheProperties.html">MySQL version of the JDBC Disk Cache</a>
+					can optimize and repair tables.
+				</p>
+			</subsection>
+			<subsection name="TCP Lateral Cache">
+				<p>
+					The
+					<a href="LateralTCPAuxCache.html">TCP Lateral Cache</a>
+					provides an easy way to distribute cached data to multiple servers.
+					It comes with a
+					<a href="LateralUDPDiscovery.html">UDP discovery</a>
+					mechanism, so you can add nodes without having to reconfigure the
+					entire farm. The TCP Lateral Cache works by establishing
+					connections with socket server running on other nodes. Each node
+					maintains a connection to every other. Only one server is needed
+					for any number of regions. The client is able to re-establish
+					connections if it looses its connection with another server. The
+					TCP Lateral is
+					<a href="LateralTCPProperties.html"> highly configurable</a>
+					. You can choose to only send data, to not look for data on other
+					servers, to send removes instead of puts, and to filter removes
+					based on hash codes.
+				</p>
+			</subsection>
+			<subsection name="RMI Remote Cache">
+				<p>
+					JCS also provides an RMI based
+					<a href="RemoteAuxCache.html">Remote Cache Server</a>
+					. Rather than having each node connect to every other node, you can
+					use the remote cache server as the connection point. Each node
+					connects to the remove server, which then broadcasts events to the
+					other nodes. To maintain consistency across a cluster without
+					incurring the overhead of serialization, you can decide to send
+					invalidation messages to the other locals rather than send the
+					object over the wire. The remote cache server holds a serialized
+					version of your objects, so it does not need to be deployed with
+					your class libraries. The remote servers can be chained and a list
+					of failover servers can be configured on the client.
+				</p>
+			</subsection>
+		</section>
+		<section name="What JCS is not">
+			<p> JCS is not a tag library or a web specific application. JCS is a
+				general purpose caching system that can be used in web applications,
+				services, and stand alone Java applications.</p>
+			<p> JCS is not a transactional distribution mechanism. Transactional
+				distributed caches are not scalable. JCS is a cache not a database.
+				The distribution mechanisms provided by JCS can scale into the tens
+				of servers. In a well-designed service oriented architecture, JCS
+				can be used in a high demand service with numerous nodes. This would
+				not be possible if the distribution mechanism were transactional.
+			</p>
+			<p> JCS does not use AOP. JCS is a high performance, non-invasive
+				cache. It does not manipulate your objects so it can just send a
+				field or two fewer over the wire.</p>
+			<p> JCS is not a fork, an offshoot, a branch, or any other derivation
+				of JCS. Nor is JCS named after another library. JCS is a mature
+				project that has been under development and in use since 2001. Over
+				the years JCS has incorporated numerous bug fixes and has added
+				dozens of features, making it the best designed and most feature
+				rich caching solution available.</p>
+		</section>
+	</body>
+</document>
\ No newline at end of file
diff --git a/xdocs/tasks.xml b/xdocs/tasks.xml
new file mode 100644
index 0000000..6e6715d
--- /dev/null
+++ b/xdocs/tasks.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+
+<document>
+  <properties>
+    <title>TODO (or not todo)</title>
+    <author email="ASmuts at therealm.com">Aaron Smuts</author>
+  </properties>
+
+  <body>
+    <section name="TODO">
+      <p>
+        The following is a list of items that need to be completed in
+        JCS.  Contributions are welcome!  If you want to get involved,
+        its as easy as reading the <a href="/site/source.html">source
+        page</a> and the <a href="/turbine/common/code-standards.html">
+        coding guidelines</a> established for Turbine family of projects.
+      </p>
+      <ul>
+        <li>Examples</li>
+        <li>XML config</li>
+        <li>
+          Run-time auxiliary selection (establish all lateral
+          available and choose via access)</li>
+        <li>Idle time check in
+          hub on get  ( only check maxLife expiration now)
+        </li>
+        <li>LFU memory cache</li>
+        <li>JUnit tests</li>
+        <li>Embedded servlet options</li>
+        <li>JMS lateral cache</li>
+      </ul>
+    </section>
+  </body>
+</document>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/commons-jcs.git



More information about the pkg-java-commits mailing list